ref: 0874435504c0816c0e5f11a2e852507626fe185e
parent: 764afe37e2438498bcace9de56b9618565b7cd34
author: rodri <rgl@antares-labs.eu>
date: Mon Mar 25 18:25:32 EDT 2024
implement a (partially) concurrent pipeline.
--- a/camera.c
+++ b/camera.c
@@ -79,15 +79,27 @@
}
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);
}
--- a/graphics.h
+++ b/graphics.h
@@ -27,7 +27,9 @@
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 @@
int id;
Memimage *frag;
Channel *donec;
+ Renderjob *job;
- /* TODO replace with a Scene */
Entity *entity;
uvlong uni_time;
@@ -160,7 +162,7 @@
Color (*fshader)(FSparams*);
};
-struct Shader
+struct Shadertab
{
char *name;
Point3 (*vshader)(VSparams*); /* vertex shader */
@@ -167,6 +169,25 @@
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 @@
RFrame3; /* VCS */
Viewport *vp;
Scene *s;
+ Renderer *rctl;
double fov; /* vertical FOV */
struct {
double n, f; /* near and far clipping planes */
@@ -219,7 +241,7 @@
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);
@@ -226,6 +248,7 @@
void rmviewport(Viewport*);
/* render */
+Renderer *initgraphics(void);
Point3 model2world(Entity*, Point3);
Point3 world2vcs(Camera*, Point3);
Point3 vcs2clip(Camera*, Point3);
--- a/internal.h
+++ b/internal.h
@@ -1,3 +1,10 @@
+typedef struct Jobqueue Jobqueue;
+
+struct Jobqueue
+{
+ Renderjob *hd, *tl;
+};
+
/* alloc */
void *emalloc(ulong);
void *erealloc(void*, ulong);
@@ -8,9 +15,6 @@
void rmfb(Framebuf*);
Framebufctl *mkfbctl(Rectangle);
void rmfbctl(Framebufctl*);
-
-/* render */
-void shade(Framebuf*, Scene*, Shader*);
/* vertex */
Vertex dupvertex(Vertex*);
--- a/render.c
+++ b/render.c
@@ -354,8 +354,9 @@
}
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 @@
Triangle *t; /* triangles to raster */
int i, nt;
- params = arg;
- vsp.su = params;
+ threadsetname("entityproc");
- threadsetname("shader unit #%d", params->id);
-
+ 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 */
+ while((params = recvp(paramsc)) != nil){
+ vsp.su = params;
- 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);
+ verts = params->entity->mdl->obj->vertdata[OBJVGeometric].verts;
+ tverts = params->entity->mdl->obj->vertdata[OBJVTexture].verts;
+ nverts = params->entity->mdl->obj->vertdata[OBJVNormal].verts;
- 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;
- }
+ eb = params->entity->mdl->elems;
+ ee = eb + params->entity->mdl->nelems;
- 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);
- }
+ for(ep = eb; ep != ee; ep++){
+ nt = 1; /* start with one. after clipping it might change */
- 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[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);
- 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);
+ 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;
+ }
- if(!isvisible(t[0][0].p) || !isvisible(t[0][1].p) || !isvisible(t[0][2].p))
- nt = cliptriangle(t);
+ 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(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);
+ 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;
+ }
- /* culling */
-// if(isfacingback(t[nt]))
-// goto skiptri;
+ 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);
- 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);
+ if(!isvisible(t[0][0].p) || !isvisible(t[0][1].p) || !isvisible(t[0][2].p))
+ nt = cliptriangle(t);
- rasterize(params, t[nt]);
+ 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]);
+ delvattrs(&t[nt][0]);
+ delvattrs(&t[nt][1]);
+ delvattrs(&t[nt][2]);
+ }
}
+ sendp(params->donec, params);
}
-
- free(t);
- sendp(params->donec, params);
- threadexits(nil);
}
-void
-shade(Framebuf *fb, Scene *sc, Shader *s)
+static void
+renderer(void *arg)
{
- int i;
- uvlong time;
+ Channel *jobc;
+ Jobqueue jobq;
+ Renderjob *job;
+ Scene *sc;
Entity *ent;
- SUparams *params;
- Channel *donec;
+ SUparams *params, *params2;
+ Channel *paramsc, *donec;
- time = nanosec();
- donec = chancreate(sizeof(void*), 0);
+ threadsetname("renderer");
- /* 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);
- }
+ jobc = arg;
+ jobq.tl = jobq.hd = nil;
+ ent = nil;
+ paramsc = chancreate(sizeof(SUparams*), 8);
+ donec = chancreate(sizeof(SUparams*), 0);
- while(i--){
- params = recvp(donec);
- freememimage(params->frag);
- free(params);
- }
- chanfree(donec);
+ proccreate(entityproc, paramsc, mainstacksize);
+
+ 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;
+ }
+}
+
+Renderer *
+initgraphics(void)
+{
+ Renderer *r;
+
+ r = emalloc(sizeof *r);
+ r->c = chancreate(sizeof(Renderjob*), 8);
+ proccreate(renderer, r->c, mainstacksize);
+ return r;
}