diff options
author | rodri <rgl@antares-labs.eu> | 2024-03-25 22:25:32 +0000 |
---|---|---|
committer | rodri <rgl@antares-labs.eu> | 2024-03-25 22:25:32 +0000 |
commit | 0874435504c0816c0e5f11a2e852507626fe185e (patch) | |
tree | ac42078e476358ce3c0131e6a5da19fc4aebd9e8 | |
parent | 764afe37e2438498bcace9de56b9618565b7cd34 (diff) | |
download | libgraphics-0874435504c0816c0e5f11a2e852507626fe185e.tar.gz libgraphics-0874435504c0816c0e5f11a2e852507626fe185e.tar.bz2 libgraphics-0874435504c0816c0e5f11a2e852507626fe185e.zip |
implement a (partially) concurrent pipeline.
-rw-r--r-- | camera.c | 16 | ||||
-rw-r--r-- | graphics.h | 31 | ||||
-rw-r--r-- | internal.h | 10 | ||||
-rw-r--r-- | render.c | 311 |
4 files changed, 234 insertions, 134 deletions
@@ -79,15 +79,27 @@ reloadcamera(Camera *c) } void -shootcamera(Camera *c, Shader *s) +shootcamera(Camera *c, Shadertab *s) { + Renderjob *job; uvlong t0, t1; + job = emalloc(sizeof *job); + memset(job, 0, sizeof *job); + job->fb = c->vp->fbctl->fb[c->vp->fbctl->idx^1]; /* address the back buffer */ + job->scene = c->s; + job->shaders = s; + job->donec = chancreate(sizeof(void*), 0); + c->vp->fbctl->reset(c->vp->fbctl); t0 = nanosec(); - shade(c->vp->fbctl->fb[c->vp->fbctl->idx^1], c->s, s); /* address the back buffer */ + sendp(c->rctl->c, job); + recvp(job->donec); t1 = nanosec(); c->vp->fbctl->swap(c->vp->fbctl); + chanfree(job->donec); + free(job); + updatestats(c, t1-t0); } @@ -27,7 +27,9 @@ typedef struct Scene Scene; typedef struct VSparams VSparams; typedef struct FSparams FSparams; typedef struct SUparams SUparams; -typedef struct Shader Shader; +typedef struct Shadertab Shadertab; +typedef struct Renderer Renderer; +typedef struct Renderjob Renderjob; typedef struct Framebuf Framebuf; typedef struct Framebufctl Framebufctl; typedef struct Viewport Viewport; @@ -150,8 +152,8 @@ struct SUparams int id; Memimage *frag; Channel *donec; + Renderjob *job; - /* TODO replace with a Scene */ Entity *entity; uvlong uni_time; @@ -160,13 +162,32 @@ struct SUparams Color (*fshader)(FSparams*); }; -struct Shader +struct Shadertab { char *name; Point3 (*vshader)(VSparams*); /* vertex shader */ Color (*fshader)(FSparams*); /* fragment shader */ }; +struct Renderer +{ + Channel *c; +}; + +struct Renderjob +{ + Framebuf *fb; + Scene *scene; + Shadertab *shaders; + Channel *donec; + + ulong nrem; /* remaining entities to process */ + ulong lastid; + uvlong time0; + + Renderjob *next; +}; + struct Framebuf { Memimage *cb; /* color buffer */ @@ -201,6 +222,7 @@ struct Camera RFrame3; /* VCS */ Viewport *vp; Scene *s; + Renderer *rctl; double fov; /* vertical FOV */ struct { double n, f; /* near and far clipping planes */ @@ -219,13 +241,14 @@ void configcamera(Camera*, Viewport*, double, double, double, Projection); void placecamera(Camera*, Point3, Point3, Point3); void aimcamera(Camera*, Point3); void reloadcamera(Camera*); -void shootcamera(Camera*, Shader*); +void shootcamera(Camera*, Shadertab*); /* viewport */ Viewport *mkviewport(Rectangle); void rmviewport(Viewport*); /* render */ +Renderer *initgraphics(void); Point3 model2world(Entity*, Point3); Point3 world2vcs(Camera*, Point3); Point3 vcs2clip(Camera*, Point3); @@ -1,3 +1,10 @@ +typedef struct Jobqueue Jobqueue; + +struct Jobqueue +{ + Renderjob *hd, *tl; +}; + /* alloc */ void *emalloc(ulong); void *erealloc(void*, ulong); @@ -9,9 +16,6 @@ void rmfb(Framebuf*); Framebufctl *mkfbctl(Rectangle); void rmfbctl(Framebufctl*); -/* render */ -void shade(Framebuf*, Scene*, Shader*); - /* vertex */ Vertex dupvertex(Vertex*); void lerpvertex(Vertex*, Vertex*, Vertex*, double); @@ -354,8 +354,9 @@ rasterize(SUparams *params, Triangle t) } static void -shaderunit(void *arg) +entityproc(void *arg) { + Channel *paramsc; SUparams *params; VSparams vsp; OBJVertex *verts, *tverts, *nverts; /* geometric, texture and normals vertices */ @@ -365,144 +366,204 @@ shaderunit(void *arg) Triangle *t; /* triangles to raster */ int i, nt; - params = arg; - vsp.su = params; - - threadsetname("shader unit #%d", params->id); + threadsetname("entityproc"); + paramsc = arg; t = emalloc(sizeof(*t)*16); - verts = params->entity->mdl->obj->vertdata[OBJVGeometric].verts; - tverts = params->entity->mdl->obj->vertdata[OBJVTexture].verts; - nverts = params->entity->mdl->obj->vertdata[OBJVNormal].verts; - eb = params->entity->mdl->elems; - ee = eb + params->entity->mdl->nelems; - - for(ep = eb; ep != ee; ep++){ - nt = 1; /* start with one. after clipping it might change */ - - idxtab = &(*ep)->indextab[OBJVGeometric]; - t[0][0].p = Pt3(verts[idxtab->indices[0]].x, - verts[idxtab->indices[0]].y, - verts[idxtab->indices[0]].z, - verts[idxtab->indices[0]].w); - t[0][1].p = Pt3(verts[idxtab->indices[1]].x, - verts[idxtab->indices[1]].y, - verts[idxtab->indices[1]].z, - verts[idxtab->indices[1]].w); - t[0][2].p = Pt3(verts[idxtab->indices[2]].x, - verts[idxtab->indices[2]].y, - verts[idxtab->indices[2]].z, - verts[idxtab->indices[2]].w); - - idxtab = &(*ep)->indextab[OBJVNormal]; - if(idxtab->nindex == 3){ - t[0][0].n = Vec3(nverts[idxtab->indices[0]].i, - nverts[idxtab->indices[0]].j, - nverts[idxtab->indices[0]].k); - t[0][0].n = normvec3(t[0][0].n); - t[0][1].n = Vec3(nverts[idxtab->indices[1]].i, - nverts[idxtab->indices[1]].j, - nverts[idxtab->indices[1]].k); - t[0][1].n = normvec3(t[0][1].n); - t[0][2].n = Vec3(nverts[idxtab->indices[2]].i, - nverts[idxtab->indices[2]].j, - nverts[idxtab->indices[2]].k); - t[0][2].n = normvec3(t[0][2].n); - }else{ - /* TODO build a list of per-vertex normals earlier */ - n = normvec3(crossvec3(subpt3(t[0][1].p, t[0][0].p), subpt3(t[0][2].p, t[0][0].p))); - t[0][0].n = t[0][1].n = t[0][2].n = n; - } - idxtab = &(*ep)->indextab[OBJVTexture]; - if(idxtab->nindex == 3){ - t[0][0].uv = Pt2(tverts[idxtab->indices[0]].u, - tverts[idxtab->indices[0]].v, 1); - t[0][1].uv = Pt2(tverts[idxtab->indices[1]].u, - tverts[idxtab->indices[1]].v, 1); - t[0][2].uv = Pt2(tverts[idxtab->indices[2]].u, - tverts[idxtab->indices[2]].v, 1); - }else{ - t[0][0].uv = t[0][1].uv = t[0][2].uv = Vec2(0,0); - } + while((params = recvp(paramsc)) != nil){ + vsp.su = params; + + verts = params->entity->mdl->obj->vertdata[OBJVGeometric].verts; + tverts = params->entity->mdl->obj->vertdata[OBJVTexture].verts; + nverts = params->entity->mdl->obj->vertdata[OBJVNormal].verts; + + eb = params->entity->mdl->elems; + ee = eb + params->entity->mdl->nelems; + + for(ep = eb; ep != ee; ep++){ + nt = 1; /* start with one. after clipping it might change */ + + idxtab = &(*ep)->indextab[OBJVGeometric]; + t[0][0].p = Pt3(verts[idxtab->indices[0]].x, + verts[idxtab->indices[0]].y, + verts[idxtab->indices[0]].z, + verts[idxtab->indices[0]].w); + t[0][1].p = Pt3(verts[idxtab->indices[1]].x, + verts[idxtab->indices[1]].y, + verts[idxtab->indices[1]].z, + verts[idxtab->indices[1]].w); + t[0][2].p = Pt3(verts[idxtab->indices[2]].x, + verts[idxtab->indices[2]].y, + verts[idxtab->indices[2]].z, + verts[idxtab->indices[2]].w); + + idxtab = &(*ep)->indextab[OBJVNormal]; + if(idxtab->nindex == 3){ + t[0][0].n = Vec3(nverts[idxtab->indices[0]].i, + nverts[idxtab->indices[0]].j, + nverts[idxtab->indices[0]].k); + t[0][0].n = normvec3(t[0][0].n); + t[0][1].n = Vec3(nverts[idxtab->indices[1]].i, + nverts[idxtab->indices[1]].j, + nverts[idxtab->indices[1]].k); + t[0][1].n = normvec3(t[0][1].n); + t[0][2].n = Vec3(nverts[idxtab->indices[2]].i, + nverts[idxtab->indices[2]].j, + nverts[idxtab->indices[2]].k); + t[0][2].n = normvec3(t[0][2].n); + }else{ + /* TODO build a list of per-vertex normals earlier */ + n = normvec3(crossvec3(subpt3(t[0][1].p, t[0][0].p), subpt3(t[0][2].p, t[0][0].p))); + t[0][0].n = t[0][1].n = t[0][2].n = n; + } - for(i = 0; i < 3; i++){ - t[0][i].c = Pt3(1,1,1,1); - t[0][i].mtl = (*ep)->mtl; - t[0][i].attrs = nil; - t[0][i].nattrs = 0; - } + idxtab = &(*ep)->indextab[OBJVTexture]; + if(idxtab->nindex == 3){ + t[0][0].uv = Pt2(tverts[idxtab->indices[0]].u, + tverts[idxtab->indices[0]].v, 1); + t[0][1].uv = Pt2(tverts[idxtab->indices[1]].u, + tverts[idxtab->indices[1]].v, 1); + t[0][2].uv = Pt2(tverts[idxtab->indices[2]].u, + tverts[idxtab->indices[2]].v, 1); + }else{ + t[0][0].uv = t[0][1].uv = t[0][2].uv = Vec2(0,0); + } - vsp.v = &t[0][0]; - vsp.idx = 0; - t[0][0].p = params->vshader(&vsp); - vsp.v = &t[0][1]; - vsp.idx = 1; - t[0][1].p = params->vshader(&vsp); - vsp.v = &t[0][2]; - vsp.idx = 2; - t[0][2].p = params->vshader(&vsp); + for(i = 0; i < 3; i++){ + t[0][i].c = Pt3(1,1,1,1); + t[0][i].mtl = (*ep)->mtl; + t[0][i].attrs = nil; + t[0][i].nattrs = 0; + } - if(!isvisible(t[0][0].p) || !isvisible(t[0][1].p) || !isvisible(t[0][2].p)) - nt = cliptriangle(t); + vsp.v = &t[0][0]; + vsp.idx = 0; + t[0][0].p = params->vshader(&vsp); + vsp.v = &t[0][1]; + vsp.idx = 1; + t[0][1].p = params->vshader(&vsp); + vsp.v = &t[0][2]; + vsp.idx = 2; + t[0][2].p = params->vshader(&vsp); + + if(!isvisible(t[0][0].p) || !isvisible(t[0][1].p) || !isvisible(t[0][2].p)) + nt = cliptriangle(t); + + while(nt--){ + t[nt][0].p = clip2ndc(t[nt][0].p); + t[nt][1].p = clip2ndc(t[nt][1].p); + t[nt][2].p = clip2ndc(t[nt][2].p); + + /* culling */ +// if(isfacingback(t[nt])) +// goto skiptri; + + t[nt][0].p = ndc2viewport(params->fb, t[nt][0].p); + t[nt][1].p = ndc2viewport(params->fb, t[nt][1].p); + t[nt][2].p = ndc2viewport(params->fb, t[nt][2].p); + + rasterize(params, t[nt]); +//skiptri: + delvattrs(&t[nt][0]); + delvattrs(&t[nt][1]); + delvattrs(&t[nt][2]); + } + } + sendp(params->donec, params); + } +} - while(nt--){ - t[nt][0].p = clip2ndc(t[nt][0].p); - t[nt][1].p = clip2ndc(t[nt][1].p); - t[nt][2].p = clip2ndc(t[nt][2].p); +static void +renderer(void *arg) +{ + Channel *jobc; + Jobqueue jobq; + Renderjob *job; + Scene *sc; + Entity *ent; + SUparams *params, *params2; + Channel *paramsc, *donec; - /* culling */ -// if(isfacingback(t[nt])) -// goto skiptri; + threadsetname("renderer"); - t[nt][0].p = ndc2viewport(params->fb, t[nt][0].p); - t[nt][1].p = ndc2viewport(params->fb, t[nt][1].p); - t[nt][2].p = ndc2viewport(params->fb, t[nt][2].p); + jobc = arg; + jobq.tl = jobq.hd = nil; + ent = nil; + paramsc = chancreate(sizeof(SUparams*), 8); + donec = chancreate(sizeof(SUparams*), 0); - rasterize(params, t[nt]); + proccreate(entityproc, paramsc, mainstacksize); -//skiptri: - delvattrs(&t[nt][0]); - delvattrs(&t[nt][1]); - delvattrs(&t[nt][2]); + enum { JOB, PARM, DONE }; + Alt a[] = { + [JOB] {jobc, &job, CHANRCV}, + [PARM] {paramsc, ¶ms, CHANNOP}, + [DONE] {donec, ¶ms2, CHANRCV}, + {nil, nil, CHANEND} + }; + for(;;) + switch(alt(a)){ + case JOB: + sc = job->scene; + job->nrem = sc->nents; + job->lastid = 0; + job->time0 = nanosec(); + + if(jobq.tl == nil){ + jobq.tl = jobq.hd = job; + ent = sc->ents.next; + a[PARM].op = CHANSND; + goto sendparams; + }else + jobq.tl = jobq.tl->next = job; + break; + case PARM: +sendparams: + job = jobq.hd; + sc = job->scene; + + if(ent != nil && ent != &sc->ents){ + params = emalloc(sizeof *params); + memset(params, 0, sizeof *params); + params->fb = job->fb; + params->id = job->lastid++; + params->frag = rgb(DBlack); + params->donec = donec; + params->job = job; + params->entity = ent; + params->uni_time = job->time0; + params->vshader = job->shaders->vshader; + params->fshader = job->shaders->fshader; + ent = ent->next; + }else{ + jobq.hd = job->next; + if((job = jobq.hd) != nil){ + ent = job->scene->ents.next; + goto sendparams; + } + + jobq.tl = jobq.hd; + a[PARM].op = CHANNOP; + } + break; + case DONE: + if(--params2->job->nrem < 1) + send(params2->job->donec, nil); + + freememimage(params2->frag); + free(params2); + break; } - } - - free(t); - sendp(params->donec, params); - threadexits(nil); } -void -shade(Framebuf *fb, Scene *sc, Shader *s) +Renderer * +initgraphics(void) { - int i; - uvlong time; - Entity *ent; - SUparams *params; - Channel *donec; - - time = nanosec(); - donec = chancreate(sizeof(void*), 0); - - /* TODO come up with an actual concurrent architecture */ - for(i = 0, ent = sc->ents.next; i < sc->nents; i++, ent = ent->next){ - params = emalloc(sizeof *params); - params->fb = fb; - params->id = i; - params->frag = rgb(DBlack); - params->donec = donec; - params->entity = ent; - params->uni_time = time; - params->vshader = s->vshader; - params->fshader = s->fshader; - proccreate(shaderunit, params, mainstacksize); - } + Renderer *r; - while(i--){ - params = recvp(donec); - freememimage(params->frag); - free(params); - } - chanfree(donec); + r = emalloc(sizeof *r); + r->c = chancreate(sizeof(Renderjob*), 8); + proccreate(renderer, r->c, mainstacksize); + return r; } |