From 966028467011647e3d74b9dea14a591a6b20d7a4 Mon Sep 17 00:00:00 2001 From: Muffin Date: Sun, 19 May 2024 22:05:34 -0500 Subject: [PATCH 1/6] Update @turbowarp/scratch-svg-renderer --- package-lock.json | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index c8e2a8cee..f7acdecbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -964,16 +964,17 @@ "integrity": "sha512-texcM9oxfEsADVlVDR5UhLkYclPKsV9mytJh+9pHHonNcUrxRVGF6FkJTzWO/Hl5NafU1crSdw737nqKE3atSA==" }, "node_modules/@turbowarp/scratch-svg-renderer": { - "version": "1.0.0-202401140118-6632ac8", - "resolved": "https://registry.npmjs.org/@turbowarp/scratch-svg-renderer/-/scratch-svg-renderer-1.0.0-202401140118-6632ac8.tgz", - "integrity": "sha512-JD9ax1AY3bvHFRPwrYseMs55ttA209hy3JDvWDTqgbJscwGDXF8dR1zHAgz4eD0lgBdqUAClUxW+W7CFtcxrkQ==", + "version": "1.0.202405192203", + "resolved": "https://registry.npmjs.org/@turbowarp/scratch-svg-renderer/-/scratch-svg-renderer-1.0.202405192203.tgz", + "integrity": "sha512-jFfXl8GHraAy9ie88RsR844y8vjPXXDWUtZf9Nm1oEDJ4vMP7Q0ecikiJVDAIu27rLXWcQLQn31KWmUZvLRZhw==", + "license": "MPL-2.0", "dependencies": { + "@turbowarp/nanolog": "^0.2.0", "base64-js": "1.2.1", "base64-loader": "1.0.0", "css-tree": "1.1.3", "dompurify": "2.2.7", "fastestsmallesttextencoderdecoder": "^1.0.22", - "minilog": "3.1.0", "transformation-matrix": "1.15.0" }, "peerDependencies": { @@ -16586,16 +16587,16 @@ "integrity": "sha512-texcM9oxfEsADVlVDR5UhLkYclPKsV9mytJh+9pHHonNcUrxRVGF6FkJTzWO/Hl5NafU1crSdw737nqKE3atSA==" }, "@turbowarp/scratch-svg-renderer": { - "version": "1.0.0-202401140118-6632ac8", - "resolved": "https://registry.npmjs.org/@turbowarp/scratch-svg-renderer/-/scratch-svg-renderer-1.0.0-202401140118-6632ac8.tgz", - "integrity": "sha512-JD9ax1AY3bvHFRPwrYseMs55ttA209hy3JDvWDTqgbJscwGDXF8dR1zHAgz4eD0lgBdqUAClUxW+W7CFtcxrkQ==", + "version": "1.0.202405192203", + "resolved": "https://registry.npmjs.org/@turbowarp/scratch-svg-renderer/-/scratch-svg-renderer-1.0.202405192203.tgz", + "integrity": "sha512-jFfXl8GHraAy9ie88RsR844y8vjPXXDWUtZf9Nm1oEDJ4vMP7Q0ecikiJVDAIu27rLXWcQLQn31KWmUZvLRZhw==", "requires": { + "@turbowarp/nanolog": "^0.2.0", "base64-js": "1.2.1", "base64-loader": "1.0.0", "css-tree": "1.1.3", "dompurify": "2.2.7", "fastestsmallesttextencoderdecoder": "^1.0.22", - "minilog": "3.1.0", "transformation-matrix": "1.15.0" }, "dependencies": { From 98405442ddbacba4c5d8e62c8add417ffa390261 Mon Sep 17 00:00:00 2001 From: Muffin Date: Thu, 6 Jun 2024 15:11:49 -0500 Subject: [PATCH 2/6] Update @turbowarp/scratch-svg-renderer --- package-lock.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index f7acdecbe..393f21145 100644 --- a/package-lock.json +++ b/package-lock.json @@ -964,10 +964,9 @@ "integrity": "sha512-texcM9oxfEsADVlVDR5UhLkYclPKsV9mytJh+9pHHonNcUrxRVGF6FkJTzWO/Hl5NafU1crSdw737nqKE3atSA==" }, "node_modules/@turbowarp/scratch-svg-renderer": { - "version": "1.0.202405192203", - "resolved": "https://registry.npmjs.org/@turbowarp/scratch-svg-renderer/-/scratch-svg-renderer-1.0.202405192203.tgz", - "integrity": "sha512-jFfXl8GHraAy9ie88RsR844y8vjPXXDWUtZf9Nm1oEDJ4vMP7Q0ecikiJVDAIu27rLXWcQLQn31KWmUZvLRZhw==", - "license": "MPL-2.0", + "version": "1.0.202406061508", + "resolved": "https://registry.npmjs.org/@turbowarp/scratch-svg-renderer/-/scratch-svg-renderer-1.0.202406061508.tgz", + "integrity": "sha512-/8NS5v0OcRzsmYkA3MBfN3kAuqnC3zdZuwbBYEqAVczBsJfyoTHwPrlNtsMXFdG2+2CHKq7G4zLipKdgglwljw==", "dependencies": { "@turbowarp/nanolog": "^0.2.0", "base64-js": "1.2.1", @@ -16587,9 +16586,9 @@ "integrity": "sha512-texcM9oxfEsADVlVDR5UhLkYclPKsV9mytJh+9pHHonNcUrxRVGF6FkJTzWO/Hl5NafU1crSdw737nqKE3atSA==" }, "@turbowarp/scratch-svg-renderer": { - "version": "1.0.202405192203", - "resolved": "https://registry.npmjs.org/@turbowarp/scratch-svg-renderer/-/scratch-svg-renderer-1.0.202405192203.tgz", - "integrity": "sha512-jFfXl8GHraAy9ie88RsR844y8vjPXXDWUtZf9Nm1oEDJ4vMP7Q0ecikiJVDAIu27rLXWcQLQn31KWmUZvLRZhw==", + "version": "1.0.202406061508", + "resolved": "https://registry.npmjs.org/@turbowarp/scratch-svg-renderer/-/scratch-svg-renderer-1.0.202406061508.tgz", + "integrity": "sha512-/8NS5v0OcRzsmYkA3MBfN3kAuqnC3zdZuwbBYEqAVczBsJfyoTHwPrlNtsMXFdG2+2CHKq7G4zLipKdgglwljw==", "requires": { "@turbowarp/nanolog": "^0.2.0", "base64-js": "1.2.1", From 208e27ad997078e5add4dc53b655949ee221d3f1 Mon Sep 17 00:00:00 2001 From: Muffin Date: Tue, 16 Jul 2024 22:04:19 -0500 Subject: [PATCH 3/6] Update @turbowarp/scratch-svg-renderer --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 393f21145..88e8a0213 100644 --- a/package-lock.json +++ b/package-lock.json @@ -964,9 +964,9 @@ "integrity": "sha512-texcM9oxfEsADVlVDR5UhLkYclPKsV9mytJh+9pHHonNcUrxRVGF6FkJTzWO/Hl5NafU1crSdw737nqKE3atSA==" }, "node_modules/@turbowarp/scratch-svg-renderer": { - "version": "1.0.202406061508", - "resolved": "https://registry.npmjs.org/@turbowarp/scratch-svg-renderer/-/scratch-svg-renderer-1.0.202406061508.tgz", - "integrity": "sha512-/8NS5v0OcRzsmYkA3MBfN3kAuqnC3zdZuwbBYEqAVczBsJfyoTHwPrlNtsMXFdG2+2CHKq7G4zLipKdgglwljw==", + "version": "1.0.202407162159", + "resolved": "https://registry.npmjs.org/@turbowarp/scratch-svg-renderer/-/scratch-svg-renderer-1.0.202407162159.tgz", + "integrity": "sha512-AfTPmQBMDNjtkHjP70V2MohoLK2mnmziUYbgY+98dzfpOJFapuKDwT5tBZGMZA/KqEqkyWfg6i3hw0MKhPpeEQ==", "dependencies": { "@turbowarp/nanolog": "^0.2.0", "base64-js": "1.2.1", @@ -16586,9 +16586,9 @@ "integrity": "sha512-texcM9oxfEsADVlVDR5UhLkYclPKsV9mytJh+9pHHonNcUrxRVGF6FkJTzWO/Hl5NafU1crSdw737nqKE3atSA==" }, "@turbowarp/scratch-svg-renderer": { - "version": "1.0.202406061508", - "resolved": "https://registry.npmjs.org/@turbowarp/scratch-svg-renderer/-/scratch-svg-renderer-1.0.202406061508.tgz", - "integrity": "sha512-/8NS5v0OcRzsmYkA3MBfN3kAuqnC3zdZuwbBYEqAVczBsJfyoTHwPrlNtsMXFdG2+2CHKq7G4zLipKdgglwljw==", + "version": "1.0.202407162159", + "resolved": "https://registry.npmjs.org/@turbowarp/scratch-svg-renderer/-/scratch-svg-renderer-1.0.202407162159.tgz", + "integrity": "sha512-AfTPmQBMDNjtkHjP70V2MohoLK2mnmziUYbgY+98dzfpOJFapuKDwT5tBZGMZA/KqEqkyWfg6i3hw0MKhPpeEQ==", "requires": { "@turbowarp/nanolog": "^0.2.0", "base64-js": "1.2.1", From a4c2e18a027dd09f9a39258e215d8a72f47e1c1c Mon Sep 17 00:00:00 2001 From: Muffin Date: Sat, 20 Jul 2024 01:55:29 -0500 Subject: [PATCH 4/6] Prefer webgl2 Closes https://github.com/TurboWarp/scratch-render/pull/10 --- src/RenderWebGL.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/RenderWebGL.js b/src/RenderWebGL.js index 58215c2c4..ffbe53f4b 100644 --- a/src/RenderWebGL.js +++ b/src/RenderWebGL.js @@ -122,10 +122,12 @@ class RenderWebGL extends EventEmitter { try { optCanvas = optCanvas || document.createElement('canvas'); const options = {alpha: false, stencil: true, antialias: false}; + // Don't use twgl's getContext here because it will spend a few milliseconds enabling extensions + // on a context that won't get used. return !!( + optCanvas.getContext('webgl2', options) || optCanvas.getContext('webgl', options) || - optCanvas.getContext('experimental-webgl', options) || - optCanvas.getContext('webgl2', options) + optCanvas.getContext('experimental-webgl', options) ); } catch (e) { return false; @@ -145,11 +147,7 @@ class RenderWebGL extends EventEmitter { antialias: false, powerPreference: RenderWebGL.powerPreference }; - // getWebGLContext = try WebGL 1.0 only - // getContext = try WebGL 2.0 and if that doesn't work, try WebGL 1.0 - // getWebGLContext || getContext = try WebGL 1.0 and if that doesn't work, try WebGL 2.0 - return twgl.getWebGLContext(canvas, contextAttribs) || - twgl.getContext(canvas, contextAttribs); + return twgl.getContext(canvas, contextAttribs); } /** From c89c863541a2639893366be0172eb726923d3e31 Mon Sep 17 00:00:00 2001 From: Muffin Date: Wed, 31 Jul 2024 16:33:32 -0500 Subject: [PATCH 5/6] Export scratch-svg-renderer for extensions --- src/RenderWebGL.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/RenderWebGL.js b/src/RenderWebGL.js index ffbe53f4b..a7a41f262 100644 --- a/src/RenderWebGL.js +++ b/src/RenderWebGL.js @@ -3,6 +3,7 @@ const EventEmitter = require('events'); const hull = require('hull.js'); const twgl = require('twgl.js'); +const SVGRenderer = require('@turbowarp/scratch-svg-renderer'); const Skin = require('./Skin'); const BitmapSkin = require('./BitmapSkin'); const Drawable = require('./Drawable'); @@ -308,6 +309,7 @@ class RenderWebGL extends EventEmitter { */ this.exports = { twgl, + SVGRenderer, Drawable, Skin, BitmapSkin, From ed45bcdac86c83142cec1b519ec5f73a268c6f93 Mon Sep 17 00:00:00 2001 From: Tacodiva <27910867+Tacodiva@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:23:35 +1000 Subject: [PATCH 6/6] Optimize pen line drawing using instanced rendering (#11) --- src/PenSkin.js | 287 +++++++++++++++++++++++++------------------------ 1 file changed, 147 insertions(+), 140 deletions(-) diff --git a/src/PenSkin.js b/src/PenSkin.js index 0a25bdb4a..762bfbe91 100644 --- a/src/PenSkin.js +++ b/src/PenSkin.js @@ -24,14 +24,9 @@ const DefaultPenAttributes = { diameter: 1 }; -/** - * Reused memory location for storing a premultiplied pen color. - * @type {FloatArray} - */ -const __premultipliedColor = [0, 0, 0, 0]; - -const PEN_BUFFER_SIZE_LARGER = 65520; -const PEN_BUFFER_SIZE_SMALLER = 32760; +const PEN_ATTRIBUTE_BUFFER_SIZE = 163800; +const PEN_ATTRIBUTE_STRIDE = 10; +const PEN_ATTRIBUTE_STRIDE_BYTES = PEN_ATTRIBUTE_STRIDE * 4; class PenSkin extends Skin { /** @@ -71,60 +66,20 @@ class PenSkin extends Skin { exit: () => this._exitUsePenBuffer() }; + /** @type {WebGLRenderingContext} */ + const gl = this._renderer.gl; + // tw: renderQuality attribute this.renderQuality = 1; // tw: keep track of native size this._nativeSize = renderer.getNativeSize(); - // tw: create the extra data structures needed to buffer pen - this._resetAttributeIndexes(); - this.a_lineColor = new Float32Array(PEN_BUFFER_SIZE_LARGER); - this.a_lineThicknessAndLength = new Float32Array(PEN_BUFFER_SIZE_SMALLER); - this.a_penPoints = new Float32Array(PEN_BUFFER_SIZE_LARGER); - this.a_position = new Float32Array(PEN_BUFFER_SIZE_SMALLER); - for (let i = 0; i < this.a_position.length; i += 12) { - this.a_position[i + 0] = 1; - this.a_position[i + 1] = 0; - this.a_position[i + 2] = 0; - this.a_position[i + 3] = 0; - this.a_position[i + 4] = 1; - this.a_position[i + 5] = 1; - this.a_position[i + 6] = 1; - this.a_position[i + 7] = 1; - this.a_position[i + 8] = 0; - this.a_position[i + 9] = 0; - this.a_position[i + 10] = 0; - this.a_position[i + 11] = 1; - } - /** @type {twgl.BufferInfo} */ - this._lineBufferInfo = twgl.createBufferInfoFromArrays(this._renderer.gl, { - a_position: { - numComponents: 2, - data: this.a_position - }, - a_lineColor: { - numComponents: 4, - drawType: this._renderer.gl.STREAM_DRAW, - data: this.a_lineColor - }, - a_lineThicknessAndLength: { - numComponents: 2, - drawType: this._renderer.gl.STREAM_DRAW, - data: this.a_lineThicknessAndLength - }, - a_penPoints: { - numComponents: 4, - drawType: this._renderer.gl.STREAM_DRAW, - data: this.a_penPoints - } - }); - const NO_EFFECTS = 0; /** @type {twgl.ProgramInfo} */ this._lineShader = this._renderer._shaderManager.getShader(ShaderManager.DRAW_MODE.line, NO_EFFECTS); - // tw: draw region used to preserve texture when resizing + // Draw region used to preserve texture when resizing this._drawTextureShader = this._renderer._shaderManager.getShader(ShaderManager.DRAW_MODE.default, NO_EFFECTS); /** @type {object} */ this._drawTextureRegionId = { @@ -132,6 +87,69 @@ class PenSkin extends Skin { exit: () => this._exitDrawTexture() }; + this.a_position_glbuffer = gl.createBuffer(); + this.a_position_loc = gl.getAttribLocation(this._lineShader.program, 'a_position'); + + this.a_lineColor_loc = gl.getAttribLocation(this._lineShader.program, 'a_lineColor'); + this.a_lineThicknessAndLength_loc = gl.getAttribLocation(this._lineShader.program, 'a_lineThicknessAndLength'); + this.a_penPoints_loc = gl.getAttribLocation(this._lineShader.program, 'a_penPoints'); + + this.attribute_glbuffer = gl.createBuffer(); + this.attribute_index = 0; + this.attribute_data = new Float32Array(PEN_ATTRIBUTE_BUFFER_SIZE); + gl.bindBuffer(gl.ARRAY_BUFFER, this.attribute_glbuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.attribute_data.length * 4, gl.STREAM_DRAW); + + if (gl.drawArraysInstanced) { + // WebGL2 has native instanced rendering + this.instancedRendering = true; + this.glDrawArraysInstanced = gl.drawArraysInstanced.bind(gl); + this.glVertexAttribDivisor = gl.vertexAttribDivisor.bind(gl); + } else { + // WebGL1 may have instanced rendering through the ANGLE_instanced_arrays extension + const instancedArraysExtension = gl.getExtension('ANGLE_instanced_arrays'); + if (instancedArraysExtension) { + this.instancedRendering = true; + this.glDrawArraysInstanced = instancedArraysExtension.drawArraysInstancedANGLE.bind( + instancedArraysExtension + ); + this.glVertexAttribDivisor = instancedArraysExtension.vertexAttribDivisorANGLE.bind( + instancedArraysExtension + ); + } else { + // Inefficient but still supported + this.instancedRendering = false; + } + } + + if (this.instancedRendering) { + gl.bindBuffer(gl.ARRAY_BUFFER, this.a_position_glbuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + 1, 0, + 0, 0, + 1, 1, + 0, 1 + ]), gl.STATIC_DRAW); + } else { + const positionBuffer = new Float32Array(PEN_ATTRIBUTE_BUFFER_SIZE / PEN_ATTRIBUTE_STRIDE * 2); + for (let i = 0; i < positionBuffer.length; i += 12) { + positionBuffer[i + 0] = 1; + positionBuffer[i + 1] = 0; + positionBuffer[i + 2] = 0; + positionBuffer[i + 3] = 0; + positionBuffer[i + 4] = 1; + positionBuffer[i + 5] = 1; + positionBuffer[i + 6] = 1; + positionBuffer[i + 7] = 1; + positionBuffer[i + 8] = 0; + positionBuffer[i + 9] = 0; + positionBuffer[i + 10] = 0; + positionBuffer[i + 11] = 1; + } + gl.bindBuffer(gl.ARRAY_BUFFER, this.a_position_glbuffer); + gl.bufferData(gl.ARRAY_BUFFER, positionBuffer, gl.STATIC_DRAW); + } + this.onNativeSizeChanged = this.onNativeSizeChanged.bind(this); this._renderer.on(RenderConstants.Events.NativeSizeChanged, this.onNativeSizeChanged); @@ -222,8 +240,6 @@ class PenSkin extends Skin { * Prepare to draw lines in the _lineOnBufferDrawRegionId region. */ _enterDrawLineOnBuffer () { - // tw: reset attributes when starting pen drawing - this._resetAttributeIndexes(); const gl = this._renderer.gl; twgl.bindFramebufferInfo(gl, this._framebuffer); @@ -232,7 +248,6 @@ class PenSkin extends Skin { const currentShader = this._lineShader; gl.useProgram(currentShader.program); - twgl.setBuffersAndAttributes(gl, currentShader, this._lineBufferInfo); const uniforms = { u_skin: this._texture, @@ -240,6 +255,12 @@ class PenSkin extends Skin { }; twgl.setUniforms(currentShader, uniforms); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.a_position_glbuffer); + gl.enableVertexAttribArray(this.a_position_loc); + gl.vertexAttribPointer(this.a_position_loc, 2, gl.FLOAT, false, 2 * 4, 0); + + this.attribute_index = 0; } /** @@ -247,7 +268,7 @@ class PenSkin extends Skin { */ _exitDrawLineOnBuffer () { // tw: flush when exiting pen rendering - if (this.a_lineColorIndex) { + if (this.attribute_index) { this._flushLines(); } @@ -326,19 +347,15 @@ class PenSkin extends Skin { _drawLineOnBuffer (penAttributes, x0, y0, x1, y1) { this._renderer.enterDrawRegion(this._lineOnBufferDrawRegionId); - // tw: flush if this line would overflow buffers - // For some reason, looking up the size of a_lineColor with .length is very slow in some browsers. - // We see measurable performance improvements by comparing to a constant instead. - if (this.a_lineColorIndex + 24 > PEN_BUFFER_SIZE_LARGER) { + const iters = this.instancedRendering ? 1 : 6; + + // For some reason, looking up the size of a buffer through .length can be slow, + // so use a constant instead. + if (this.attribute_index + (PEN_ATTRIBUTE_STRIDE * iters) > PEN_ATTRIBUTE_BUFFER_SIZE) { this._flushLines(); } - // Premultiply pen color by pen transparency const penColor = penAttributes.color4f || DefaultPenAttributes.color4f; - __premultipliedColor[0] = penColor[0] * penColor[3]; - __premultipliedColor[1] = penColor[1] * penColor[3]; - __premultipliedColor[2] = penColor[2] * penColor[3]; - __premultipliedColor[3] = penColor[3]; // tw: apply renderQuality x0 *= this.renderQuality; @@ -357,92 +374,82 @@ class PenSkin extends Skin { // tw: apply renderQuality const lineThickness = (penAttributes.diameter || DefaultPenAttributes.diameter) * this.renderQuality; - // tw: write pen draws to buffers where they will be flushed later - for (let i = 0; i < 6; i++) { - this.a_lineColor[this.a_lineColorIndex] = __premultipliedColor[0]; - this.a_lineColorIndex++; - this.a_lineColor[this.a_lineColorIndex] = __premultipliedColor[1]; - this.a_lineColorIndex++; - this.a_lineColor[this.a_lineColorIndex] = __premultipliedColor[2]; - this.a_lineColorIndex++; - this.a_lineColor[this.a_lineColorIndex] = __premultipliedColor[3]; - this.a_lineColorIndex++; - - this.a_lineThicknessAndLength[this.a_lineThicknessAndLengthIndex] = lineThickness; - this.a_lineThicknessAndLengthIndex++; - - this.a_lineThicknessAndLength[this.a_lineThicknessAndLengthIndex] = lineLength; - this.a_lineThicknessAndLengthIndex++; - - this.a_penPoints[this.a_penPointsIndex] = x0; - this.a_penPointsIndex++; - this.a_penPoints[this.a_penPointsIndex] = -y0; - this.a_penPointsIndex++; - this.a_penPoints[this.a_penPointsIndex] = lineDiffX; - this.a_penPointsIndex++; - this.a_penPoints[this.a_penPointsIndex] = -lineDiffY; - this.a_penPointsIndex++; - } - } - // tw: resets indexes in the pen drawing buffers - _resetAttributeIndexes () { - this.a_lineColorIndex = 0; - this.a_lineThicknessAndLengthIndex = 0; - this.a_penPointsIndex = 0; + for (let i = 0; i < iters; i++) { + // Pen color sent to the GPU is pre-multiplied by transparency + this.attribute_data[this.attribute_index] = penColor[0] * penColor[3]; + this.attribute_index++; + this.attribute_data[this.attribute_index] = penColor[1] * penColor[3]; + this.attribute_index++; + this.attribute_data[this.attribute_index] = penColor[2] * penColor[3]; + this.attribute_index++; + this.attribute_data[this.attribute_index] = penColor[3]; + this.attribute_index++; + + this.attribute_data[this.attribute_index] = lineThickness; + this.attribute_index++; + + this.attribute_data[this.attribute_index] = lineLength; + this.attribute_index++; + + this.attribute_data[this.attribute_index] = x0; + this.attribute_index++; + this.attribute_data[this.attribute_index] = -y0; + this.attribute_index++; + this.attribute_data[this.attribute_index] = lineDiffX; + this.attribute_index++; + this.attribute_data[this.attribute_index] = -lineDiffY; + this.attribute_index++; + } } - // tw: flushes buffered pen lines to the GPU _flushLines () { + /** @type {WebGLRenderingContext} */ const gl = this._renderer.gl; - const currentShader = this._lineShader; + gl.bindBuffer(gl.ARRAY_BUFFER, this.attribute_glbuffer); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array(this.attribute_data.buffer, 0, this.attribute_index)); - // If only a small amount of data needs to be uploaded, only upload part of the data. - // todo: need to see if this helps and fine tune this number - if (this.a_lineColorIndex < 1000) { - twgl.setAttribInfoBufferFromArray( - gl, - this._lineBufferInfo.attribs.a_lineColor, - new Float32Array(this.a_lineColor.buffer, 0, this.a_lineColorIndex), - 0 - ); - twgl.setAttribInfoBufferFromArray( - gl, - this._lineBufferInfo.attribs.a_penPoints, - new Float32Array(this.a_penPoints.buffer, 0, this.a_penPointsIndex), - 0 - ); - twgl.setAttribInfoBufferFromArray( - gl, - this._lineBufferInfo.attribs.a_lineThicknessAndLength, - new Float32Array(this.a_lineThicknessAndLength.buffer, 0, this.a_lineThicknessAndLengthIndex), - 0 + gl.enableVertexAttribArray(this.a_lineColor_loc); + gl.vertexAttribPointer( + this.a_lineColor_loc, + 4, gl.FLOAT, false, + PEN_ATTRIBUTE_STRIDE_BYTES, 0 + ); + + gl.enableVertexAttribArray(this.a_lineThicknessAndLength_loc); + gl.vertexAttribPointer( + this.a_lineThicknessAndLength_loc, + 2, gl.FLOAT, false, + PEN_ATTRIBUTE_STRIDE_BYTES, 4 * 4 + ); + + gl.enableVertexAttribArray(this.a_penPoints_loc); + gl.vertexAttribPointer( + this.a_penPoints_loc, + 4, gl.FLOAT, false, + PEN_ATTRIBUTE_STRIDE_BYTES, 6 * 4 + ); + + if (this.instancedRendering) { + this.glVertexAttribDivisor(this.a_lineColor_loc, 1); + this.glVertexAttribDivisor(this.a_lineThicknessAndLength_loc, 1); + this.glVertexAttribDivisor(this.a_penPoints_loc, 1); + + this.glDrawArraysInstanced( + gl.TRIANGLE_STRIP, + 0, 4, + this.attribute_index / PEN_ATTRIBUTE_STRIDE ); + + this.glVertexAttribDivisor(this.a_lineColor_loc, 0); + this.glVertexAttribDivisor(this.a_lineThicknessAndLength_loc, 0); + this.glVertexAttribDivisor(this.a_penPoints_loc, 0); } else { - twgl.setAttribInfoBufferFromArray( - gl, - this._lineBufferInfo.attribs.a_lineColor, - this.a_lineColor - ); - twgl.setAttribInfoBufferFromArray( - gl, - this._lineBufferInfo.attribs.a_penPoints, - this.a_penPoints - ); - twgl.setAttribInfoBufferFromArray( - gl, - this._lineBufferInfo.attribs.a_lineThicknessAndLength, - this.a_lineThicknessAndLength - ); + gl.drawArrays(gl.TRIANGLES, 0, this.attribute_index / PEN_ATTRIBUTE_STRIDE); } - // todo: if we skip twgl and do all this buffer stuff ourselves, we can skip some unneeded gl calls - twgl.setBuffersAndAttributes(gl, currentShader, this._lineBufferInfo); - - twgl.drawBufferInfo(gl, this._lineBufferInfo, gl.TRIANGLES, this.a_lineThicknessAndLengthIndex / 2); - - this._resetAttributeIndexes(); + this.attribute_index = 0; this._silhouetteDirty = true; }