shithub: gpufs

ref: 5781e4d2a29054abc5d659e9ca867604cba4513f
dir: /vm.c/

View raw version
#include <u.h>
#include <libc.h>
#include <String.h>
#include "objects.h"
#include "ops.h"
#include "vm.h"

extern int debug;

u32int magic = 0x07230203;

static char *Einvalid = "invalid id %lld";
static char *Ewrongtype = "wrong object type %lld != %s";

#define IDVALID(c) (c < numobjects)

typedef struct PidStack PidStack;
struct PidStack {
	int pid;
	Frame *stack;
};

#define NSTACKS 10
int nstacks = NSTACKS;
PidStack stacks[NSTACKS];

u32int
runinst(Frame *stack, u32int *ptr)
{
	u32int len, opcode;
	void (*func)(Frame*,u32int);
	Frame **fptr;
	
	fptr = stack->ctxt.r.frameptr;
	
	opcode = (*ptr) & 0x0000ffff;
	len = ((*ptr) & 0xffff0000) >> 16;
	
	fprint(2, "op: %ux\n", opcode);
	
	if (oplookup(opcode, &func)) {
		func(stack, len);
		
		// if frame changed, return (and quit)
		if (*fptr != stack) {
			fprint(2, "stack changed\n");
			return 0;
		}
		
		// if (func) changes pc, ignore it
		if (ptr == stack->pc) {
			stack->pc += len;
		}
		return len;
	}
	fprint(2, "error: %r\n");
	return 0;
}

void
runstack(Frame *frame, u32int *ptr)
{
	Frame **fptr = frame->ctxt.r.frameptr;
	
	while (runinst(frame, frame->pc)) {
		if (1) {
			Frame *cs;
			for (cs = *fptr; cs; cs = cs->next) {
				fprint(2, "Stack: %p (PC: %p)\n", cs, cs->pc);
			}
		}
	}
}

void
vmrun(Shader *s, u32int *ptr)
{
	Frame *n;
	
	int pid = getpid();
	int sid = -1;
	for (int i = 0; i < nstacks; i++) {
		if (stacks[i].pid <= 0) {
			stacks[i].pid = pid;
			sid = i;
			break;
		}
	}
	
	if (sid < 0) {
		fprint(2, "no free pid stack found!");
		exits(nil);
		return;
	}
	
	n = malloc(sizeof(Frame));
	
	n->next = stacks[sid].stack;
	stacks[sid].stack = n;
	n->pc = ptr;
	n->ctxt.type = RUNTIME;
	n->ctxt.r.shader = s;
	n->ctxt.r.frameptr = &stacks[sid].stack;
	
	runstack(n, ptr);
	
	// free all stacks for this PID
	for (n = stacks[sid].stack; n; ) {
		Frame *ns = n->next;
		free(n);
		n = ns;
	}
	stacks[sid].stack = nil;
	stacks[sid].pid = -1;
	
	exits(nil);
}

typedef struct Object Object;
struct Object {
	int id;
	char type;
	char *info;
	union {
		Shader s;
		Buffer b;
	};
};

Object objects[256];
int numobjects = 0;

DescPool descpools[8];
int numdescpools = 0;

static char* typenames[] = {
	"BUFFER",
	"SHADER",
};

int
validate(vlong id, int type)
{
	if (!IDVALID(id)) {
		werrstr(Einvalid, id);
		return 0;
	}
	if (objects[id].type != type) {
		werrstr(Ewrongtype, id, typenames[type]);
		return 0;
	}
	return 1;
}

int
runshader(vlong id, char *entrypoint)
{
	Shader *s;
	EntryPoint *ep;
	
	if (!validate(id, SHADER))
		return 0;
	
	s = &objects[id].s;
	
	if (!s->compiled) {
		werrstr("Shader %lld not compiled", id);
		return 0;
	}
	
	for (ep = s->entrypoints; ep; ep = ep->next) {
		if (strcmp(ep->name, entrypoint) == 0)
			break;
	}
	if (!ep) {
		werrstr("entry point %s not found", entrypoint);
		return 0;
	}
	
	for (int i = 0; i < s->nitems; i++) {
		fprint(2, "%d → %d\n", i, s->items[i].type);
	}
	
	if (s->items[ep->func].type != TFUNCTION) {
		werrstr("entry point function not found (object %ld type %d)", ep->func, s->items[ep->func].type);
		return 0;
	}
	
	long label = s->items[ep->func].f.label;
	if (label < 0) {
		werrstr("entry point function has no label");
		return 0;
	}
	
	if (s->items[label].type != TLABEL) {
		werrstr("entry point function label is not valid");
		return 0;
	}
	
	u32int *ptr = s->items[label].l.ptr;
	
	if (debug) {
		fprint(2, "running from %p\n", ptr);
		fprint(2, "ptr: %ux\n", *ptr);
	}
	
	switch (rfork(RFFDG|RFREND|RFPROC|RFMEM)) {
	case 0:
		// child
		vmrun(s, ptr);
		return 0;
	case -1:
		// error
		werrstr("error spawning child process");
		return 0;
	}
	// parent
	return 1;
}

String*
getentrypointlist(Shader *sh)
{
	String *s;
	EntryPoint *ep;
	s = s_new();
	
	for (ep = sh->entrypoints; ep; ep = ep->next) {
		s_append(s, "\nEntryPoint ");
		s_append(s, ep->name);
	}
	return s;
}

void
updateinfostring(vlong id)
{
	String *s;
	
	switch (objects[id].type) {
	case SHADER:
		if (objects[id].info)
			free(objects[id].info);
		
		s = getentrypointlist(&objects[id].s);
		objects[id].info = smprint(
			"DescriptorPool %d\n"
			"Compiled %s"
			"%s\n",
			objects[id].s.descpool,
			objects[id].s.compiled ? "yes" : "no",
			s_to_c(s)
		);
		s_free(s);
		break;
	case BUFFER:
		if (objects[id].info)
			free(objects[id].info);
		objects[id].info = smprint(
			"Length %ld\n",
			objects[id].b.len
		);
		break;
	}
}

vlong
genshader(void)
{
	vlong id;
	if (numobjects >= 256) {
		werrstr("not enough free objects!");
		return -1;
	}
	id = numobjects++;
	objects[id].id = id;
	objects[id].type = SHADER;
	objects[id].s.buffer = nil;
	objects[id].s.len = -1;
	objects[id].s.descpool = -1;
	objects[id].s.compiled = 0;
	objects[id].s.items = nil;
	objects[id].s.nitems = -1;
	objects[id].s.maxitems = -1;
	objects[id].s.entrypoints = nil;
	objects[id].s.lastfunction = -1;
	updateinfostring(id);
	return id;
}

vlong
genbuffer(long size)
{
	vlong id;
	if (numobjects >= 256) {
		werrstr("not enough free objects!");
		return -1;
	}
	id = numobjects++;
	objects[id].id = id;
	objects[id].type = BUFFER;
	objects[id].b.len = size;
	objects[id].b.buffer = malloc(size);
	if (!objects[id].b.buffer) {
		werrstr("cannot allocate memory: %r");
		return -1;
	}
	updateinfostring(id);
	return id;
}

vlong
getnumobjects()
{
	return numobjects;
}

long
getbufferlength(vlong id)
{
	if (objects[id].type != BUFFER) {
		werrstr("invalid object type!");
		return -1;
	}
	return objects[id].b.len;
}

long
getshaderlength(vlong id)
{
	long len;
	if (objects[id].type != SHADER) {
		werrstr("invalid object type!");
		return -1;
	}
	len = objects[id].s.len;
	return len < 0 ? 0 : len;
}

int
getobjecttype(vlong id)
{
	return objects[id].type;
}

vlong
getobjectid(vlong num)
{
	if (num >= numobjects) {
		werrstr("invalid object number: %lld", num);
		return -1;
	}
	return objects[num].id;
}

int
writeshader(vlong id, void *data, long n, long offset)
{
	char *buf;
	
	if (!validate(id, SHADER)) {
		return 0;
	}
	
	buf = (char*)objects[id].s.buffer;
	if (!buf) {
		objects[id].s.len = n+offset;
		objects[id].s.buffer = malloc(objects[id].s.len);
		buf = (char*)objects[id].s.buffer;
	}
	if (!buf) {
		sysfatal("out of memory");
		return 0;
	}
	if (offset+n > objects[id].s.len) {
		objects[id].s.len = n+offset;
		objects[id].s.buffer = realloc(objects[id].s.buffer, objects[id].s.len);
		buf = (char*)objects[id].s.buffer;
	}
	if (!buf) {
		sysfatal("out of memory!");
		return 0;
	}
	buf += offset;
	memcpy(buf, data, n);
	updateinfostring(id);
	return n;
}

int
writebuffer(vlong id, void *data, long n, long offset)
{
	char *buf;
	
	if (!validate(id, BUFFER)) {
		return 0;
	}
	
	if (offset+n > objects[id].b.len) {
		return -1;
	}
	
	buf = &objects[id].b.buffer[offset];
	memcpy(buf, data, n);
	updateinfostring(id);
	return n;
}

int
compileshader(vlong id)
{
	Shader *sh;
	Frame f;
	u32int *lastbuf;
	
	if (!validate(id, SHADER)) {
		return 0;
	}
	
	sh = &objects[id].s;
	if (!sh->buffer || sh->len <= 0) {
		werrstr("shader is empty");
		return 0;
	}
	
	if (sh->descpool < 0) {
		werrstr("shader is not bound to a descriptor pool");
		return 0;
	}
	
	if (sh->compiled) {
		werrstr("shader is already compiled");
		return 0;
	}
	
	if (*sh->buffer != magic) {
		werrstr("invalid shader format");
		return 0;
	}
	
	sh->maxitems = 100;
	sh->items = malloc(sh->maxitems*sizeof(Item));
	sh->nitems = 0;
	
	f.pc = &sh->buffer[1]; // ignore magic number
	f.next = nil;
	f.ctxt.type = COMPILE;
	f.ctxt.c.shader = sh;
	lastbuf = &sh->buffer[sh->len-1];
	
	while (f.pc < lastbuf) {
		u32int len, opcode;
		u32int *oldpc = f.pc;
		void (*func)(Frame*,u32int);
		opcode = (*f.pc) & 0x0000ffff;
		len = ((*f.pc) & 0xffff0000) >> 16;
		
		if (!len)
			break;
		
		if (oplookup(opcode, &func)) {
			func(&f, len);
		} else {
			fprint(2, "error: %r\n");
		}
			
		if (oldpc == f.pc) {
			f.pc += len;
		}
	}
	
	sh->compiled = 1;
	updateinfostring(id);
	return sh->compiled;
}

int
readshader(vlong id, void *data, long n, long offset)
{
	char *buf;
	
	if (!validate(id, SHADER)) {
		return 0;
	}
	
	buf = (char*)objects[id].s.buffer;
	
	if (!buf) {
		werrstr("shader is empty");
		return 0;
	}
	
	if (offset+n > objects[id].b.len) {
		n = objects[id].s.len - offset;
	}
	if (n <= 0)
		return 0;
	
	buf = &buf[offset];
	memcpy(data, buf, n);
	return n;
}

int readbuffer(vlong id, void *data, long n, long offset)
{
	char *buf;
	
	if (!validate(id, BUFFER)) {
		return 0;
	}
	
	if (offset+n > objects[id].b.len) {
		n = objects[id].b.len - offset;
	}
	if (n <= 0)
		return 0;
	
	buf = &objects[id].b.buffer[offset];
	memcpy(data, buf, n);
	return n;
}

int
gendescpool(int numsets)
{
	int id;
	if (numdescpools >= 8) {
		werrstr("not enough pools reserved");
		return -1;
	}
	
	id = numdescpools++;
	descpools[id].sets = malloc(sizeof(DescSet)*numsets);
	if (!descpools[id].sets) {
		sysfatal("out of memory");
		return -1;
	}
	descpools[id].numsets = numsets;
	memset(descpools[id].sets, 0, sizeof(DescSet)*numsets);
	
	return id;
}

int
getnumdescpools(void)
{
	return numdescpools;
}

char*
getpoolinfo(void)
{
	String* ret = s_new();
	char s[128];
	
	for (int i = 0; i < numdescpools; i++) {
		snprint(s, 128, "DescPool %d\n", i);
		ret = s_append(ret, s);
		for (int j = 0; j < descpools[i].numsets; j++) {
			snprint(s, 128, "\tSet %d\n", j);
			s_append(ret, s);
			for (int k = 0; k < descpools[i].sets[j].numbindings; k++) {
				snprint(s, 128, "\t\t%5d %5lld\n", k, descpools[i].sets[j].bindings[k]);
				s_append(ret, s);
			}
		}
	}
	return s_to_c(ret);
}

int
allocdescset(int pool, int set, int numbindings)
{
	if (pool >= numdescpools) {
		werrstr("invalid pool id %d", pool);
		return 0;
	}
	
	if (set >= descpools[pool].numsets) {
		werrstr("invalid set id %d", pool);
		return 0;
	}
	
	descpools[pool].sets[set].bindings = malloc(sizeof(vlong)*numbindings);
	if (!descpools[pool].sets[set].bindings) {
		sysfatal("out of memory");
		return 0;
	}
	for (int i = 0; i < numbindings; i++) {
		descpools[pool].sets[set].bindings[i] = -1;
	}
	descpools[pool].sets[set].numbindings = numbindings;
	return 1;
}

int
binduniform(vlong id, int pool, int set, int binding)
{
	if (!validate(id, BUFFER)) {
		return 0;
	}
	
	if (pool >= numdescpools) {
		werrstr("invalid pool id %d", pool);
		return 0;
	}
	
	if (set >= descpools[pool].numsets) {
		werrstr("pool has not enough sets");
		return 0;
	}
	
	if (binding >= descpools[pool].sets[set].numbindings) {
		werrstr("set has not enough bindings");
		return 0;
	}
	
	descpools[pool].sets[set].bindings[binding] = id;
	return 1;
}

int
bindshader(vlong id, int pool)
{
	if (!validate(id, SHADER)) {
		return 0;
	}
	
	objects[id].s.descpool = pool;
	updateinfostring(id);
	return 1;
}

char*
getobjectinfo(vlong id)
{
	if (!IDVALID(id)) {
		werrstr(Einvalid, id);
		return nil;
	}
	return objects[id].info;
}