shithub: libgraphics

Download patch

ref: f98dcc45225fa78341aba06137b05b9b6b89cb12
parent: 2c410b56f801708ec11a1af6cdd995d69e9db059
author: rodri <rgl@antares-labs.eu>
date: Thu Aug 29 14:19:15 EDT 2024

implement a general raster interface for the framebuffers.

got a bit tired of having to add a ulong *buf every time
i needed to get some data out of the shaders, so i made
it possible to create as many extra buffers as needed,
which then can be addressed from the fragment shader
and drawn using the same drawing routines that were used
for the color buffer.

these so called Rasters are very similar to the OpenGL
FBOs, in case you are familiar, but we address them by
strings instead of numbers. and they can also be used as
textures if you create one (see alloctexture) and then
memdraw into it after every shot.

--- a/fb.c
+++ b/fb.c
@@ -14,7 +14,7 @@
  * see https://www.scale2x.it/algorithm
  */
 static void
-scale2x_filter(ulong *dst, Framebuf *fb, Point sp)
+scale2x_filter(ulong *dst, Raster *fb, Point sp)
 {
 	ulong B, D, E, F, H;
 
@@ -34,7 +34,7 @@
 }
 
 static void
-scale3x_filter(ulong *dst, Framebuf *fb, Point sp)
+scale3x_filter(ulong *dst, Raster *fb, Point sp)
 {
 	ulong A, B, C, D, E, F, G, H, I;
 
@@ -93,153 +93,187 @@
 //}
 
 static void
-upscaledraw(Framebufctl *ctl, Image *dst, Point off, Point scale)
+fb_createraster(Framebuf *fb, char *name, ulong chan)
 {
-	void (*filter)(ulong*, Framebuf*, Point);
-	Framebuf *fb;
+	Raster *r;
+
+	assert(name != nil);
+
+	/*
+	 * TODO might be better to keep a tail so it's O(1)
+	 *
+	 * in practice though, most users won't ever create
+	 * more than ten extra rasters.
+	 */
+	r = fb->rasters;
+	while(r->next != nil)
+		r = r->next;
+	r->next = allocraster(name, fb->r, chan);
+}
+
+static Raster *
+fb_fetchraster(Framebuf *fb, char *name)
+{
+	Raster *r;
+
+	r = fb->rasters;
+	if(name == nil)
+		return r;
+
+	while((r = r->next) != nil)
+		if(strcmp(name, r->name) == 0)
+			return r;
+	return nil;
+
+}
+
+static void
+upscaledraw(Raster *fb, Image *dst, Point off, Point scale, uint filter)
+{
+	void (*filterfn)(ulong*, Raster*, Point);
 	Rectangle blkr;
 	Point sp, dp;
 	ulong *blk;
 
-	filter = nil;
+	filterfn = nil;
 	blk = emalloc(scale.x*scale.y*4);
 	blkr = Rect(0,0,scale.x,scale.y);
 
-	qlock(ctl);
-	fb = ctl->getfb(ctl);
-
-	switch(ctl->upfilter){
+	switch(filter){
 	case UFScale2x:
 		if(scale.x == scale.y && scale.y == 2)
-			filter = scale2x_filter;
+			filterfn = scale2x_filter;
 		break;
 	case UFScale3x:
 		if(scale.x == scale.y && scale.y == 3)
-			filter = scale3x_filter;
+			filterfn = scale3x_filter;
 		break;
 	}
 
 	for(sp.y = fb->r.min.y, dp.y = dst->r.min.y+off.y; sp.y < fb->r.max.y; sp.y++, dp.y += scale.y)
 	for(sp.x = fb->r.min.x, dp.x = dst->r.min.x+off.x; sp.x < fb->r.max.x; sp.x++, dp.x += scale.x){
-		if(filter != nil)
-			filter(blk, fb, sp);
+		if(filterfn != nil)
+			filterfn(blk, fb, sp);
 		else
 			memsetl(blk, getpixel(fb, sp), scale.x*scale.y);
 		loadimage(dst, rectaddpt(blkr, dp), (uchar*)blk, scale.x*scale.y*4);
 	}
-	qunlock(ctl);
 	free(blk);
 }
 
 static void
-framebufctl_draw(Framebufctl *ctl, Image *dst, Point off, Point scale)
+framebufctl_draw(Framebufctl *ctl, Image *dst, char *name, Point off, Point scale)
 {
 	Framebuf *fb;
+	Raster *r;
 	Rectangle sr, dr;
-	int y;
 
+	qlock(ctl);
+	fb = ctl->getfb(ctl);
+
+	r = fb->rasters;
+	if(name != nil)
+		do r = r->next; while(r != nil && strcmp(name, r->name) != 0);
+	if(r == nil){
+		qunlock(ctl);
+		return;
+	}
+
 	if(scale.x > 1 || scale.y > 1){
-		upscaledraw(ctl, dst, off, scale);
+		upscaledraw(r, dst, off, scale, ctl->upfilter);
+		qunlock(ctl);
 		return;
 	}
 
-	qlock(ctl);
-	fb = ctl->getfb(ctl);
 	sr = rectaddpt(fb->r, off);
 	dr = rectsubpt(dst->r, dst->r.min);
 	if(rectinrect(sr, dr))
-		loadimage(dst, rectaddpt(sr, dst->r.min), (uchar*)fb->cb, Dx(fb->r)*Dy(fb->r)*4);
+		loadimage(dst, rectaddpt(sr, dst->r.min), (uchar*)r->data, Dx(fb->r)*Dy(r->r)*4);
 	else if(rectclip(&sr, dr)){
 		dr = sr;
 		dr.max.y = dr.min.y + 1;
 		/* remove offset to get the actual rect within the framebuffer */
 		sr = rectsubpt(sr, off);
-		for(y = sr.min.y; y < sr.max.y; y++, dr.min.y++, dr.max.y++)
-			loadimage(dst, rectaddpt(dr, dst->r.min), (uchar*)&fb->cb[y*Dx(fb->r) + sr.min.x], Dx(dr)*4);
+		for(; sr.min.y < sr.max.y; sr.min.y++, dr.min.y++, dr.max.y++)
+			loadimage(dst, rectaddpt(dr, dst->r.min), rasterbyteaddr(r, sr.min), Dx(dr)*4);
 	}
 	qunlock(ctl);
 }
 
 static void
-upscalememdraw(Framebufctl *ctl, Memimage *dst, Point off, Point scale)
+upscalememdraw(Raster *fb, Memimage *dst, Point off, Point scale, uint filter)
 {
-	void (*filter)(ulong*, Framebuf*, Point);
-	Framebuf *fb;
+	void (*filterfn)(ulong*, Raster*, Point);
 	Rectangle blkr;
 	Point sp, dp;
 	ulong *blk;
 
-	filter = nil;
+	filterfn = nil;
 	blk = emalloc(scale.x*scale.y*4);
 	blkr = Rect(0,0,scale.x,scale.y);
 
-	qlock(ctl);
-	fb = ctl->getfb(ctl);
-
-	switch(ctl->upfilter){
+	switch(filter){
 	case UFScale2x:
 		if(scale.x == scale.y && scale.y == 2)
-			filter = scale2x_filter;
+			filterfn = scale2x_filter;
 		break;
 	case UFScale3x:
 		if(scale.x == scale.y && scale.y == 3)
-			filter = scale3x_filter;
+			filterfn = scale3x_filter;
 		break;
 	}
 
 	for(sp.y = fb->r.min.y, dp.y = dst->r.min.y+off.y; sp.y < fb->r.max.y; sp.y++, dp.y += scale.y)
 	for(sp.x = fb->r.min.x, dp.x = dst->r.min.x+off.x; sp.x < fb->r.max.x; sp.x++, dp.x += scale.x){
-		if(filter != nil)
-			filter(blk, fb, sp);
+		if(filterfn != nil)
+			filterfn(blk, fb, sp);
 		else
 			memsetl(blk, getpixel(fb, sp), scale.x*scale.y);
 		loadmemimage(dst, rectaddpt(blkr, dp), (uchar*)blk, scale.x*scale.y*4);
 	}
-	qunlock(ctl);
 	free(blk);
 }
 
 static void
-framebufctl_memdraw(Framebufctl *ctl, Memimage *dst, Point off, Point scale)
+framebufctl_memdraw(Framebufctl *ctl, Memimage *dst, char *name, Point off, Point scale)
 {
 	Framebuf *fb;
+	Raster *r;
 	Rectangle sr, dr;
-	int y;
 
+	qlock(ctl);
+	fb = ctl->getfb(ctl);
+
+	r = fb->rasters;
+	if(name != nil)
+		do r = r->next; while(r != nil && strcmp(name, r->name) != 0);
+	if(r == nil){
+		qunlock(ctl);
+		return;
+	}
+
 	if(scale.x > 1 || scale.y > 1){
-		upscalememdraw(ctl, dst, off, scale);
+		upscalememdraw(r, dst, off, scale, ctl->upfilter);
+		qunlock(ctl);
 		return;
 	}
 
-	qlock(ctl);
-	fb = ctl->getfb(ctl);
 	sr = rectaddpt(fb->r, off);
 	dr = rectsubpt(dst->r, dst->r.min);
 	if(rectinrect(sr, dr))
-		loadmemimage(dst, rectaddpt(sr, dst->r.min), (uchar*)fb->cb, Dx(fb->r)*Dy(fb->r)*4);
+		loadmemimage(dst, rectaddpt(sr, dst->r.min), (uchar*)r->data, Dx(fb->r)*Dy(r->r)*4);
 	else if(rectclip(&sr, dr)){
 		dr = sr;
 		dr.max.y = dr.min.y + 1;
 		/* remove offset to get the actual rect within the framebuffer */
 		sr = rectsubpt(sr, off);
-		for(y = sr.min.y; y < sr.max.y; y++, dr.min.y++, dr.max.y++)
-			loadmemimage(dst, rectaddpt(dr, dst->r.min), (uchar*)&fb->cb[y*Dx(fb->r) + sr.min.x], Dx(dr)*4);
+		for(; sr.min.y < sr.max.y; sr.min.y++, dr.min.y++, dr.max.y++)
+			loadmemimage(dst, rectaddpt(dr, dst->r.min), rasterbyteaddr(r, sr.min), Dx(dr)*4);
 	}
 	qunlock(ctl);
 }
 
 static void
-framebufctl_drawnormals(Framebufctl *ctl, Image *dst)
-{
-	Framebuf *fb;
-
-	qlock(ctl);
-	fb = ctl->getfb(ctl);
-	loadimage(dst, rectaddpt(fb->r, dst->r.min), (uchar*)fb->nb, Dx(fb->r)*Dy(fb->r)*4);
-	qunlock(ctl);
-}
-
-static void
 framebufctl_swap(Framebufctl *ctl)
 {
 	qlock(ctl);
@@ -261,13 +295,18 @@
 framebufctl_reset(Framebufctl *ctl, ulong clr)
 {
 	Framebuf *fb;
+	Raster *r;
 
 	/* address the back buffer—resetting the front buffer is VERBOTEN */
 	fb = ctl->getbb(ctl);
 	resetAbuf(&fb->abuf);
-	memsetl(fb->nb, 0, Dx(fb->r)*Dy(fb->r));
-	memsetf(fb->zb, Inf(-1), Dx(fb->r)*Dy(fb->r));
-	memsetl(fb->cb, rgba2xrgb(clr), Dx(fb->r)*Dy(fb->r));
+
+	r = fb->rasters;		/* color buffer */
+	clearraster(r, rgba2xrgb(clr));
+	r = r->next;			/* z-buffer */
+	fclearraster(r, Inf(-1));
+	while((r = r->next) != nil)
+		clearraster(r, 0);	/* every other raster */
 }
 
 static Framebuf *
@@ -282,6 +321,126 @@
 	return ctl->fb[ctl->idx^1];	/* back buffer */
 }
 
+static void
+framebufctl_createraster(Framebufctl *ctl, char *name, ulong chan)
+{
+	Framebuf *fb;
+	int i;
+
+	for(i = 0; i < 2; i++){
+		fb = ctl->fb[i];
+		fb->createraster(fb, name, chan);
+	}
+}
+
+static Raster *
+framebufctl_fetchraster(Framebufctl *ctl, char *name)
+{
+	Framebuf *fb;
+
+	fb = ctl->getfb(ctl);
+	return fb->fetchraster(fb, name);
+}
+
+Raster *
+allocraster(char *name, Rectangle rr, ulong chan)
+{
+	Raster *r;
+
+	assert(chan <= FLOAT32);
+
+	r = emalloc(sizeof *r);
+	memset(r, 0, sizeof *r);
+	if(name != nil && (r->name = strdup(name)) == nil)
+		sysfatal("strdup: %r");
+	r->chan = chan;
+	r->r = rr;
+	r->data = emalloc(Dx(rr)*Dy(rr)*sizeof(*r->data));
+	return r;
+}
+
+void
+clearraster(Raster *r, ulong v)
+{
+	memsetl(r->data, v, Dx(r->r)*Dy(r->r));
+}
+
+void
+fclearraster(Raster *r, float v)
+{
+	memsetf(r->data, v, Dx(r->r)*Dy(r->r));
+}
+
+uchar *
+rasterbyteaddr(Raster *r, Point p)
+{
+	return (uchar*)&r->data[p.y*Dx(r->r) + p.x];
+}
+
+void
+rasterput(Raster *r, Point p, void *v)
+{
+	switch(r->chan){
+	case COLOR32:
+		*(ulong*)rasterbyteaddr(r, p) = *(ulong*)v;
+		break;
+	case FLOAT32:
+		*(float*)rasterbyteaddr(r, p) = *(float*)v;
+		break;
+	}
+}
+
+void
+rasterget(Raster *r, Point p, void *v)
+{
+	switch(r->chan){
+	case COLOR32:
+		*(ulong*)v = *(ulong*)rasterbyteaddr(r, p);
+		break;
+	case FLOAT32:
+		*(float*)v = *(float*)rasterbyteaddr(r, p);
+		break;
+	}
+}
+
+void
+rasterputcolor(Raster *r, Point p, ulong c)
+{
+	rasterput(r, p, &c);
+}
+
+ulong
+rastergetcolor(Raster *r, Point p)
+{
+	ulong c;
+
+	rasterget(r, p, &c);
+	return c;
+}
+
+void
+rasterputfloat(Raster *r, Point p, float v)
+{
+	rasterput(r, p, &v);
+}
+
+float
+rastergetfloat(Raster *r, Point p)
+{
+	float v;
+
+	rasterget(r, p, &v);
+	return v;
+}
+
+void
+freeraster(Raster *r)
+{
+	free(r->data);
+	free(r->name);
+	free(r);
+}
+
 Framebuf *
 mkfb(Rectangle r)
 {
@@ -289,11 +448,11 @@
 
 	fb = emalloc(sizeof *fb);
 	memset(fb, 0, sizeof *fb);
-	fb->cb = emalloc(Dx(r)*Dy(r)*4);
-	fb->zb = emalloc(Dx(r)*Dy(r)*sizeof(*fb->zb));
-	memsetf(fb->zb, Inf(-1), Dx(r)*Dy(r));
-	fb->nb = emalloc(Dx(r)*Dy(r)*4);
+	fb->rasters = allocraster(nil, r, COLOR32);
+	fb->rasters->next = allocraster("z-buffer", r, FLOAT32);
 	fb->r = r;
+	fb->createraster = fb_createraster;
+	fb->fetchraster = fb_fetchraster;
 	return fb;
 }
 
@@ -300,9 +459,12 @@
 void
 rmfb(Framebuf *fb)
 {
-	free(fb->nb);
-	free(fb->zb);
-	free(fb->cb);
+	Raster *r, *nr;
+
+	for(r = fb->rasters; r != nil; r = nr){
+		nr = r->next;
+		freeraster(r);
+	}
 	free(fb);
 }
 
@@ -317,9 +479,10 @@
 	fc->fb[1] = mkfb(r);
 	fc->draw = framebufctl_draw;
 	fc->memdraw = framebufctl_memdraw;
-	fc->drawnormals = framebufctl_drawnormals;
 	fc->swap = framebufctl_swap;
 	fc->reset = framebufctl_reset;
+	fc->createraster = framebufctl_createraster;
+	fc->fetchraster = framebufctl_fetchraster;
 	fc->getfb = framebufctl_getfb;
 	fc->getbb = framebufctl_getbb;
 	return fc;
--- a/graphics.h
+++ b/graphics.h
@@ -23,6 +23,10 @@
 	LightDirectional,
 	LightSpot,
 
+	/* raster formats */
+	COLOR32 = 0,
+	FLOAT32,
+
 	/* texture formats */
 	RAWTexture = 0,	/* unmanaged */
 	sRGBTexture,
@@ -50,6 +54,7 @@
 typedef struct Entity Entity;
 typedef struct Scene Scene;
 typedef struct VSparams VSparams;
+typedef struct FSoutput FSoutput;
 typedef struct FSparams FSparams;
 typedef struct SUparams SUparams;
 typedef struct Shadertab Shadertab;
@@ -59,6 +64,7 @@
 typedef struct Fragment Fragment;
 typedef struct Astk Astk;
 typedef struct Abuf Abuf;
+typedef struct Raster Raster;
 typedef struct Framebuf Framebuf;
 typedef struct Framebufctl Framebufctl;
 typedef struct Viewport Viewport;
@@ -205,6 +211,8 @@
 	SUparams *su;
 	Point p;
 	Vertex v;		/* only for the attributes (varyings) */
+
+	void (*toraster)(FSparams*, char*, void*);
 };
 
 /* shader unit params */
@@ -278,13 +286,24 @@
 	ulong nact;
 };
 
+struct Raster
+{
+	Rectangle r;
+	char *name;
+	ulong chan;
+	ulong *data;
+
+	Raster *next;
+};
+
 struct Framebuf
 {
-	ulong *cb;	/* color buffer */
-	float *zb;	/* z/depth buffer */
-	ulong *nb;	/* normals buffer (DBG only) */
-	Abuf abuf;	/* A-buffer */
 	Rectangle r;
+	Raster *rasters;	/* [0] color, [1] depth, [n] user-defined */
+	Abuf abuf;		/* A-buffer */
+
+	void (*createraster)(Framebuf*, char*, ulong);
+	Raster *(*fetchraster)(Framebuf*, char*);
 };
 
 struct Framebufctl
@@ -294,11 +313,12 @@
 	uint idx;		/* front buffer index */
 	uint upfilter;		/* upscaling filter */
 
-	void (*draw)(Framebufctl*, Image*, Point, Point);
-	void (*memdraw)(Framebufctl*, Memimage*, Point, Point);
-	void (*drawnormals)(Framebufctl*, Image*);
+	void (*draw)(Framebufctl*, Image*, char*, Point, Point);
+	void (*memdraw)(Framebufctl*, Memimage*, char*, Point, Point);
 	void (*swap)(Framebufctl*);
 	void (*reset)(Framebufctl*, ulong);
+	void (*createraster)(Framebufctl*, char*, ulong);
+	Raster *(*fetchraster)(Framebufctl*, char*);
 	Framebuf *(*getfb)(Framebufctl*);
 	Framebuf *(*getbb)(Framebufctl*);
 };
@@ -309,13 +329,15 @@
 	Framebufctl *fbctl;
 	Rectangle r;
 
-	void (*draw)(Viewport*, Image*);
-	void (*memdraw)(Viewport*, Memimage*);
+	void (*draw)(Viewport*, Image*, char*);
+	void (*memdraw)(Viewport*, Memimage*, char*);
 	void (*setscale)(Viewport*, double, double);
 	void (*setscalefilter)(Viewport*, int);
 	Framebuf *(*getfb)(Viewport*);
 	int (*getwidth)(Viewport*);
 	int (*getheight)(Viewport*);
+	void (*createraster)(Viewport*, char*, ulong);
+	Raster *(*fetchraster)(Viewport*, char*);
 };
 
 struct Camera
--- a/internal.h
+++ b/internal.h
@@ -38,6 +38,17 @@
 Memimage *eallocmemimage(Rectangle, ulong);
 
 /* fb */
+Raster *allocraster(char*, Rectangle, ulong);
+void clearraster(Raster*, ulong);
+void fclearraster(Raster*, float);
+uchar *rasterbyteaddr(Raster*, Point);
+void rasterput(Raster*, Point, void*);
+void rasterget(Raster*, Point, void*);
+void rasterputcolor(Raster*, Point, ulong);
+ulong rastergetcolor(Raster*, Point);
+void rasterputfloat(Raster*, Point, float);
+float rastergetfloat(Raster*, Point);
+void freeraster(Raster*);
 Framebuf *mkfb(Rectangle);
 void rmfb(Framebuf*);
 Framebufctl *mkfbctl(Rectangle);
@@ -61,10 +72,10 @@
 /* nanosec */
 uvlong nanosec(void);
 
-/* ulong getpixel(Framebuf *fb, Point p) */
-#define getpixel(fb, p) (((fb)->cb)[Dx((fb)->r)*(p).y + (p).x])
-/* void putpixel(Framebuf *fb, Point p, ulong c) */
-#define putpixel(fb, p, c) (((fb)->cb)[Dx((fb)->r)*(p).y + (p).x] = (c))
+#define getpixel(fb, p)		rastergetcolor(fb, p)
+#define putpixel(fb, p, c)	rasterputcolor(fb, p, c)
+#define getdepth(fb, p)		rastergetfloat(fb, p)
+#define putdepth(fb, p, z)	rasterputfloat(fb, p, z)
 
 /* void SWAP(type t, type *a, type *b) */
 #define SWAP(t, a, b) {t tmp; tmp = *(a); *(a) = *(b); *(b) = tmp;}
--- a/render.c
+++ b/render.c
@@ -35,7 +35,7 @@
 }
 
 static void
-pixel(Framebuf *fb, Point p, Color c, int blend)
+pixel(Raster *fb, Point p, Color c, int blend)
 {
 	Color dc;
 
@@ -50,13 +50,31 @@
 }
 
 static void
-pixeln(Framebuf *fb, Point p, Color c)
+fsparams_toraster(FSparams *sp, char *rname, void *v)
 {
-	ulong *dst;
+	Framebuf *fb;
+	Raster *r;
+	ulong c, z;
 
-	dst = fb->nb;
-	c.a = 1;
-	dst[Dx(fb->r)*p.y + p.x] = col2ul(c);
+	/* keep the user away from the color buffer */
+	if(rname == nil || v == nil)
+		return;
+
+	fb = sp->su->fb;
+	r = fb->fetchraster(fb, rname);
+	if(r == nil)
+		return;
+
+	switch(r->chan){
+	case COLOR32:
+		c = col2ul(*(Color*)v);
+		rasterput(r, sp->p, &c);
+		break;
+	case FLOAT32:
+		z = *(float*)v;
+		rasterput(r, sp->p, &z);
+		break;
+	}
 }
 
 static int
@@ -117,16 +135,19 @@
 {
 	Abuf *buf;
 	Astk *stk;
+	Raster *cr, *zr;
 	int i, j;
 
 	buf = &fb->abuf;
+	cr = fb->rasters;
+	zr = cr->next;
 	for(i = 0; i < buf->nact; i++){
 		stk = buf->act[i];
 		j = stk->size;
 		while(j--)
-			pixel(fb, stk->p, stk->items[j].c, blend);
+			pixel(cr, stk->p, stk->items[j].c, blend);
 		/* write to the depth buffer as well */
-		fb->zb[stk->p.x + stk->p.y*Dx(fb->r)] = stk->items[0].z;
+		putdepth(zr, stk->p, stk->items[0].z);
 	}
 }
 
@@ -149,6 +170,7 @@
 rasterize(Rastertask *task)
 {
 	SUparams *params;
+	Raster *cr, *zr;
 	Primitive prim;
 	FSparams fsp;
 	Triangle2 t;
@@ -164,7 +186,11 @@
 	prim = task->p;
 	fsp.su = params;
 	memset(&fsp.v, 0, sizeof fsp.v);
+	fsp.toraster = fsparams_toraster;
 
+	cr = params->fb->rasters;
+	zr = cr->next;
+
 	switch(prim.type){
 	case PPoint:
 		p = Pt(prim.v[0].p.x, prim.v[0].p.y);
@@ -171,9 +197,9 @@
 
 		z = fclamp(prim.v[0].p.z, 0, 1);
 		if(params->camera->enabledepth){
-			if(z <= params->fb->zb[p.x + p.y*Dx(params->fb->r)])
+			if(z <= getdepth(zr, p))
 				break;
-			params->fb->zb[p.x + p.y*Dx(params->fb->r)] = z;
+			putdepth(zr, p, z);
 		}
 
 		fsp.v = dupvertex(&prim.v[0]);
@@ -182,7 +208,7 @@
 		if(params->camera->enableAbuff)
 			pushtoAbuf(params->fb, p, c, z);
 		else
-			pixel(params->fb, p, c, params->camera->enableblend);
+			pixel(cr, p, c, params->camera->enableblend);
 		delvattrs(&fsp.v);
 		break;
 	case PLine:
@@ -220,9 +246,9 @@
 			z = flerp(prim.v[0].p.z, prim.v[1].p.z, perc);
 			/* TODO get rid of the bounds check and make sure the clipping doesn't overflow */
 			if(params->camera->enabledepth){
-				if(!ptinrect(p, params->fb->r) || z <= params->fb->zb[p.x + p.y*Dx(params->fb->r)])
+				if(!ptinrect(p, params->fb->r) || z <= getdepth(zr, p))
 					goto discard;
-				params->fb->zb[p.x + p.y*Dx(params->fb->r)] = z;
+				putdepth(zr, p, z);
 			}
 
 			/* interpolate z⁻¹ and get actual z */
@@ -238,7 +264,7 @@
 			if(params->camera->enableAbuff)
 				pushtoAbuf(params->fb, p, c, z);
 			else
-				pixel(params->fb, p, c, params->camera->enableblend);
+				pixel(cr, p, c, params->camera->enableblend);
 			delvattrs(&fsp.v);
 discard:
 			if(steep) SWAP(int, &p.x, &p.y);
@@ -272,9 +298,9 @@
 
 				z = fberp(prim.v[0].p.z, prim.v[1].p.z, prim.v[2].p.z, bc);
 				if(params->camera->enabledepth){
-					if(z <= params->fb->zb[p.x + p.y*Dx(params->fb->r)])
+					if(z <= getdepth(zr, p))
 						continue;
-					params->fb->zb[p.x + p.y*Dx(params->fb->r)] = z;
+					putdepth(zr, p, z);
 				}
 
 				/* interpolate z⁻¹ and get actual z */
@@ -292,8 +318,7 @@
 				if(params->camera->enableAbuff)
 					pushtoAbuf(params->fb, p, c, z);
 				else
-					pixel(params->fb, p, c, params->camera->enableblend);
-				pixeln(params->fb, p, fsp.v.n);
+					pixel(cr, p, c, params->camera->enableblend);
 				delvattrs(&fsp.v);
 			}
 		break;
--- a/util.c
+++ b/util.c
@@ -23,19 +23,21 @@
 void
 memsetf(void *dp, float v, usize len)
 {
-	float *p, *ep;
+	float *p;
 
-	for(p = dp, ep = p+len; p < ep; p++)
-		*p = v;
+	p = dp;
+	while(len--)
+		*p++ = v;
 }
 
 void
 memsetl(void *dp, ulong v, usize len)
 {
-	ulong *p, *ep;
+	ulong *p;
 
-	for(p = dp, ep = p+len; p < ep; p++)
-		*p = v;
+	p = dp;
+	while(len--)
+		*p++ = v;
 }
 
 Memimage *
--- a/viewport.c
+++ b/viewport.c
@@ -9,7 +9,7 @@
 #include "internal.h"
 
 static void
-viewport_draw(Viewport *v, Image *dst)
+viewport_draw(Viewport *v, Image *dst, char *rname)
 {
 	Point off, scale;
 
@@ -18,11 +18,11 @@
 	scale.x = max(v->bx.x, 1);
 	scale.y = max(v->by.y, 1);
 
-	v->fbctl->draw(v->fbctl, dst, off, scale);
+	v->fbctl->draw(v->fbctl, dst, rname, off, scale);
 }
 
 static void
-viewport_memdraw(Viewport *v, Memimage *dst)
+viewport_memdraw(Viewport *v, Memimage *dst, char *rname)
 {
 	Point off, scale;
 
@@ -31,7 +31,7 @@
 	scale.x = max(v->bx.x, 1);
 	scale.y = max(v->by.y, 1);
 
-	v->fbctl->memdraw(v->fbctl, dst, off, scale);
+	v->fbctl->memdraw(v->fbctl, dst, rname, off, scale);
 }
 
 static void
@@ -67,6 +67,18 @@
 	return Dy(v->r)*v->by.y;
 }
 
+static void
+viewport_createraster(Viewport *v, char *name, ulong chan)
+{
+	v->fbctl->createraster(v->fbctl, name, chan);
+}
+
+static Raster *
+viewport_fetchraster(Viewport *v, char *name)
+{
+	return v->fbctl->fetchraster(v->fbctl, name);
+}
+
 Viewport *
 mkviewport(Rectangle r)
 {
@@ -90,6 +102,8 @@
 	v->getfb = viewport_getfb;
 	v->getwidth = viewport_getwidth;
 	v->getheight = viewport_getheight;
+	v->createraster = viewport_createraster;
+	v->fetchraster = viewport_fetchraster;
 	return v;
 }