diff --git a/dev/index.html b/dev/index.html index 751ee6d..6c32228 100644 --- a/dev/index.html +++ b/dev/index.html @@ -18,6 +18,8 @@
+ + diff --git a/package.json b/package.json index cbc5f3e..4aa6f19 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "q5", - "version": "2.5.6", + "version": "2.6.0", "description": "A sequel to p5.js that's optimized for interactive art", "author": "quinton-ashley", "contributors": [ diff --git a/q5.js b/q5.js index 2f76167..dedeaf1 100644 --- a/q5.js +++ b/q5.js @@ -1,6 +1,6 @@ /** * q5.js - * @version 2.5 + * @version 2.6 * @author quinton-ashley, Tezumie, and LingDong- * @license LGPL-3.0 * @class Q5 @@ -3075,7 +3075,11 @@ Q5.renderers.webgpu.canvas = ($, q) => { if ($.colorMode) $.colorMode('rgb', 1); - let pass, colorsLayout; + let pass, + mainView, + colorsLayout, + colorIndex = 1, + colorStackIndex = 8; $._pipelineConfigs = []; $._pipelines = []; @@ -3085,8 +3089,11 @@ Q5.renderers.webgpu.canvas = ($, q) => { let drawStack = ($.drawStack = []); // colors used for each draw call + + let colorStack = ($.colorStack = new Float32Array(1e6)); + // prettier-ignore - let colorsStack = ($.colorsStack = [ + colorStack.set([ 0, 0, 0, 1, // black 1, 1, 1, 1 // white ]); @@ -3118,7 +3125,7 @@ Q5.renderers.webgpu.canvas = ($, q) => { entries: [ { binding: 0, - visibility: GPUShaderStage.FRAGMENT, + visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: 'read-only-storage', hasDynamicOffset: false @@ -3134,28 +3141,49 @@ Q5.renderers.webgpu.canvas = ($, q) => { usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }); + let createMainView = () => { + mainView = Q5.device + .createTexture({ + size: [$.canvas.width, $.canvas.height], + sampleCount: 4, + format: 'bgra8unorm', + usage: GPUTextureUsage.RENDER_ATTACHMENT + }) + .createView(); + }; + $._createCanvas = (w, h, opt) => { q.ctx = q.drawingContext = c.getContext('webgpu'); opt.format ??= navigator.gpu.getPreferredCanvasFormat(); opt.device ??= Q5.device; - // needed for blend modes but couldn't get it working + // needed for other blend modes but couldn't get it working // opt.alphaMode = 'premultiplied'; $.ctx.configure(opt); Q5.device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([$.canvas.hw, $.canvas.hh])); + createMainView(); + return c; }; $._resizeCanvas = (w, h) => { $._setCanvasSize(w, h); + createMainView(); + }; + + $.pixelDensity = (v) => { + if (!v || v == $._pixelDensity) return $._pixelDensity; + $._pixelDensity = v; + $._setCanvasSize(c.w, c.h); + createMainView(); + return v; }; // current color index, used to associate a vertex with a color - let colorIndex = 1; let addColor = (r, g, b, a = 1) => { if (typeof r == 'string') r = $.color(r); else if (b == undefined) { @@ -3163,8 +3191,21 @@ Q5.renderers.webgpu.canvas = ($, q) => { a = g ?? 1; g = b = r; } - if (r._q5Color) colorsStack.push(r.r, r.g, r.b, r.a); - else colorsStack.push(r, g, b, a); + if (r._q5Color) { + a = r.a; + b = r.b; + g = r.g; + r = r.r; + } + + let cs = colorStack, + i = colorStackIndex; + cs[i++] = r; + cs[i++] = g; + cs[i++] = b; + cs[i++] = a; + colorStackIndex = i; + colorIndex++; }; @@ -3451,7 +3492,8 @@ Q5.renderers.webgpu.canvas = ($, q) => { label: 'q5-webgpu', colorAttachments: [ { - view: $.ctx.getCurrentTexture().createView(), + view: mainView, + resolveTarget: $.ctx.getCurrentTexture().createView(), loadOp: 'clear', storeOp: 'store' } @@ -3468,7 +3510,6 @@ Q5.renderers.webgpu.canvas = ($, q) => { }); new Float32Array(transformBuffer.getMappedRange()).set(transformStates.flat()); - transformBuffer.unmap(); $._transformBindGroup = Q5.device.createBindGroup({ @@ -3482,14 +3523,13 @@ Q5.renderers.webgpu.canvas = ($, q) => { pass.setBindGroup(0, $._transformBindGroup); - const colorsBuffer = Q5.device.createBuffer({ - size: colorsStack.length * 4, + let colorsBuffer = Q5.device.createBuffer({ + size: colorStackIndex * 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, mappedAtCreation: true }); - new Float32Array(colorsBuffer.getMappedRange()).set(colorsStack); - + new Float32Array(colorsBuffer.getMappedRange()).set(colorStack.slice(0, colorStackIndex)); colorsBuffer.unmap(); $._colorsBindGroup = Q5.device.createBindGroup({ @@ -3501,19 +3541,15 @@ Q5.renderers.webgpu.canvas = ($, q) => { for (let m of $._hooks.preRender) m(); - let drawVertOffset = 0; - let imageVertOffset = 0; - let textCharOffset = 0; - let curPipelineIndex = -1; - let curTextureIndex = -1; + let drawVertOffset = 0, + imageVertOffset = 0, + textCharOffset = 0, + curPipelineIndex = -1, + curTextureIndex = -1; - for (let i = 0; i < drawStack.length; i += 2) { + for (let i = 0; i < drawStack.length; i += 3) { let v = drawStack[i + 1]; - - if (drawStack[i] == -1) { - v(); - continue; - } + let o = drawStack[i + 2]; if (curPipelineIndex != drawStack[i]) { curPipelineIndex = drawStack[i]; @@ -3522,8 +3558,8 @@ Q5.renderers.webgpu.canvas = ($, q) => { if (curPipelineIndex == 0) { // v is the number of vertices - pass.draw(v, 1, drawVertOffset); - drawVertOffset += v; + pass.drawIndexed(v, 1, 0, drawVertOffset); + drawVertOffset += o; } else if (curPipelineIndex == 1) { if (curTextureIndex != v) { // v is the texture index @@ -3532,7 +3568,7 @@ Q5.renderers.webgpu.canvas = ($, q) => { pass.draw(6, 1, imageVertOffset); imageVertOffset += 6; } else if (curPipelineIndex == 2) { - pass.setBindGroup(2, $._font.bindGroup); + pass.setBindGroup(2, $._fonts[o].bindGroup); pass.setBindGroup(3, $._textBindGroup); // v is the number of characters in the text @@ -3548,12 +3584,13 @@ Q5.renderers.webgpu.canvas = ($, q) => { pass.end(); let commandBuffer = $.encoder.finish(); Q5.device.queue.submit([commandBuffer]); + q.pass = $.encoder = null; // clear the stacks for the next frame $.drawStack.length = 0; - $.colorsStack.length = 8; colorIndex = 1; + colorStackIndex = 8; rotation = 0; transformStates.length = 1; $._transformIndexStack.length = 0; @@ -3586,35 +3623,45 @@ Q5.webgpu = async function (scope, parent) { Q5.renderers.webgpu.drawing = ($, q) => { let c = $.canvas, drawStack = $.drawStack, - verticesStack = [], + vertexStack = new Float32Array(1e7), + indexStack = new Uint32Array(1e6), + vertIndex = 0, + vertCount = 0, + idxBufferIndex = 0, colorIndex; let vertexShader = Q5.device.createShaderModule({ label: 'drawingVertexShader', code: ` +struct VertexInput { + @location(0) pos: vec2f, + @location(1) colorIndex: f32, + @location(2) transformIndex: f32 +} struct VertexOutput { @builtin(position) position: vec4f, - @location(0) colorIndex: f32 -}; - + @location(0) color: vec4f +} struct Uniforms { halfWidth: f32, halfHeight: f32 -}; +} @group(0) @binding(0) var