From 027430e7a7e24343acda9f742d67be91c4b40088 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Fri, 1 May 2020 22:12:42 -0400 Subject: [PATCH] initial commit of wasm worker for sla rendering --- js/kiri-driver-sla.js | 280 +++++++++++++++++++++++++++++------------- js/web-server.js | 24 ++++ wasm/kiri-sla.c | 112 +++++++++++++++++ wasm/makefile | 5 + 4 files changed, 338 insertions(+), 83 deletions(-) create mode 100644 wasm/kiri-sla.c create mode 100644 wasm/makefile diff --git a/js/kiri-driver-sla.js b/js/kiri-driver-sla.js index c4b73211..64c021a5 100644 --- a/js/kiri-driver-sla.js +++ b/js/kiri-driver-sla.js @@ -609,61 +609,21 @@ let gs_kiri_sla = exports; layermax = Math.max(widget.slices.length); }); - function polyout(poly, ctx) { - poly.forEachPoint((p,i) => { - if (i === 0) { - ctx.moveTo(height - (p.y * scaleY + height2), p.x * scaleX + width2); - } else { - ctx.lineTo(height - (p.y * scaleY + height2), p.x * scaleX + width2); - } - }, true); - ctx.closePath(); - } + let render = renderLayer; + // let render = renderLayerWasm; // generate layer 8-bit bitaps using canvas for (let index=0; index < layermax; index++) { - let layer = new OffscreenCanvas(height,width); - let ctx = layer.getContext('2d'); - ctx.fillStyle = 'rgb(200, 0, 0)'; - let count = 0; - widgets.forEach(widget => { - let slice = widget.slices[index]; - if (slice) { - // prevent premature exit on empty synth slice - if (slice.synth) count++; - let polys = slice.solids.unioned; - if (!polys) polys = slice.tops.map(t => t.poly); - if (slice.supports) polys.appendAll(slice.supports); - polys.forEach(poly => { - poly.move(widget.track.pos); - ctx.beginPath(); - polyout(poly.setClockwise(), ctx); - if (poly.inner) { - poly.inner.forEach(inner => { - polyout(inner.setCounterClockwise(), ctx); - }); - } - ctx.fill(); - count++; - }); - } else { - // console.log({no_slice_at: index}) - } - }); - let data = ctx.getImageData(0,0,height,width).data; - // reduce RGBA to R - let red = new Uint8ClampedArray(data.length / 4); - for (let i=0; i response.arrayBuffer()) + .then(bytes => WebAssembly.instantiate(bytes, { + env: { + last: (a,b) => { console.log('last',a,b) }, + report: (a,b) => { console.log('report',a,b) + }} + })) + .then(results => { + let {module, instance} = results; + let {exports} = instance; + let heap = new Uint8Array(exports.memory.buffer); + self.wasm = { + heap, + memory: exports.memory, + render: exports.render + }; + // console.log({heap}); + }); + + self.renderLayerWasm = function renderLayer(params) { + let { width, height, index, widgets, scaleX, scaleY } = params; + let width2 = width / 2, height2 = height / 2; + let array = []; + let count = 0; + let wasm = self.wasm; + + function scaleMovePoly(poly) { + let points = poly.points; + for (let i=0, il=points.length; i { + let slice = widget.slices[index]; + if (slice) { + if (slice.synth) count++; + let polys = slice.solids.unioned; + if (!polys) polys = slice.tops.map(t => t.poly); + if (slice.supports) polys.appendAll(slice.supports); + array.appendAll(polys); + count += polys.length; + } + }); + + let imagelen = width * height; + let writer = new DataWriter(new DataView(wasm.memory.buffer), imagelen); + writer.writeU16(width, true); + writer.writeU16(height, true); + writer.writeU16(array.length, true); + + for (let i=0, il=array.length; i v !== 0) + // }); + + return { data: data, end: count === 0 }; + } + + function polyout(poly, ctx, opt) { + let { scaleX, scaleY, width, height, width2, height2 } = opt; + poly.forEachPoint((p,i) => { + if (i === 0) { + ctx.moveTo(height - (p.y * scaleY + height2), p.x * scaleX + width2); + } else { + ctx.lineTo(height - (p.y * scaleY + height2), p.x * scaleX + width2); + } + }, true); + ctx.closePath(); + } + + self.renderLayer = function renderLayer(params) { + let {width, height, index, widgets, scaleX, scaleY} = params; + let layer = new OffscreenCanvas(height,width); + let opt = { scaleX, scaleY, width, height, width2: width/2, height2: height/2 }; + let ctx = layer.getContext('2d'); + ctx.fillStyle = 'rgb(200, 0, 0)'; + let count = 0; + widgets.forEach(widget => { + let slice = widget.slices[index]; + if (slice) { + // prevent premature exit on empty synth slice + if (slice.synth) count++; + let polys = slice.solids.unioned; + if (!polys) polys = slice.tops.map(t => t.poly); + if (slice.supports) polys.appendAll(slice.supports); + polys.forEach(poly => { + poly.move(widget.track.pos); + ctx.beginPath(); + polyout(poly.setClockwise(), ctx, opt); + if (poly.inner) { + poly.inner.forEach(inner => { + polyout(inner.setCounterClockwise(), ctx, opt); + }); + } + ctx.fill(); + count++; + }); + } else { + // console.log({no_slice_at: index}) + } + }); + let data = ctx.getImageData(0,0,height,width).data; + // reduce RGBA to R + let red = new Uint8ClampedArray(data.length / 4); + for (let i=0; i +#include + +typedef unsigned char Uint8; +typedef unsigned short Uint16; +typedef unsigned int Uint32; + +struct info { + Uint16 width; // image width + Uint16 height; // image height + Uint16 polys; // total number of polygons to render +}; + +struct poly { + Uint16 length; // offset to end of record in bytes + Uint16 inners; // number of inner polygons + Uint16 points; // number of points in polygon +}; + +struct point { + float x; + float y; +}; + +extern void last(float a, float b); +extern void report(int a, int b); + +Uint32 readoff; // read position offset + +Uint8 check_cross(Uint32 x, Uint32 y, struct point *p1, struct point *p2) { + return ( + ((p1->y >= y) != (p2->y >= y)) && + (x < (p2->x - p1->x) * (y - p1->y) / (p2->y - p1->y) + p1->x) + ) ? 1 : 0; +} + +Uint8 check_inside(Uint32 x, Uint32 y, unsigned char *p) { + struct poly *poly = (struct poly *)(p + readoff); + + Uint32 nextpoly = readoff + poly->length; + Uint8 side = 0; // 0=outside, 1=inside + Uint8 psize = sizeof(struct point); + + // skip post header + readoff += sizeof(struct poly); + + // save pointer to first point or last comparison + struct point *pf = (struct point *)(p + readoff); + struct point *p1 = pf; + struct point *p2; + + // skip past first point + readoff += psize; + + // read and check point pairs updating `side` + for (Uint16 i=1; ipoints; i++) { + p2 = (struct point *)(p + readoff); + // increment read pointe + readoff += psize; + // check current/previous point crossing + if (check_cross(x, y, p1, p2)) { + side = 1 - side; + } + p1 = p2; + } + + // check last/first point crossing + if (check_cross(x, y, p1, pf)) { + side = 1 - side; + } + + // check inner polygons only if inside the outer polygon + if (side) + for (Uint16 i=0; iinners; i++) { + // if inside an inner, then actually outside poly and term + if (check_inside(x, y, p + readoff)) { + side = 0; + break; + } + } + + // update pointer to next polygon + readoff = nextpoly; + + return side; +} + +EMSCRIPTEN_KEEPALIVE +Uint32 render(unsigned char *m, Uint32 i, Uint32 o) { + struct info *info = (struct info *)(m + i); + Uint32 impos; + Uint8 inside; + Uint32 maxpos = 0; + Uint32 polypos = (sizeof (struct info)) + i; + + memset(m+o, 0, info->width * info->height); + + for (Uint32 x=0; xwidth; x++) { + for (Uint32 y=0; yheight; y++) { + inside = 0; + impos = y + (x * info->height); + readoff = polypos; + for (Uint32 p=0; ppolys; p++) { + inside = inside | check_inside(x, y, m); + } + if (inside) (m + o)[impos] = 255; + if (impos > maxpos) maxpos = impos; + } + } + + return maxpos; +} diff --git a/wasm/makefile b/wasm/makefile new file mode 100644 index 00000000..c9bae4ac --- /dev/null +++ b/wasm/makefile @@ -0,0 +1,5 @@ +kiri-sla.wasm: kiri-sla.c + emcc kiri-sla.c -o kiri-sla.wasm -O3 -s ERROR_ON_UNDEFINED_SYMBOLS=0 + +clean: *.wasm + rm *.wasm