diff --git a/internal/graphicsdriver/opengl/context_js.go b/internal/graphicsdriver/opengl/context_js.go index 530756e26..3cd53d7cf 100644 --- a/internal/graphicsdriver/opengl/context_js.go +++ b/internal/graphicsdriver/opengl/context_js.go @@ -483,16 +483,26 @@ func (c *context) bindElementArrayBuffer(b buffer) { func (c *context) arrayBufferSubData(data []float32) { gl := c.gl - arr := jsutil.TemporaryUint8Array(len(data) * 4) + l := len(data) * 4 + arr := jsutil.TemporaryUint8Array(l) jsutil.CopySliceToJS(arr, data) - gl.Call("bufferSubData", gles.ARRAY_BUFFER, 0, arr) + if isWebGL2Available { + gl.Call("bufferSubData", gles.ARRAY_BUFFER, 0, arr, 0, l) + } else { + gl.Call("bufferSubData", gles.ARRAY_BUFFER, 0, arr.Call("subarray", 0, l)) + } } func (c *context) elementArrayBufferSubData(data []uint16) { gl := c.gl - arr := jsutil.TemporaryUint8Array(len(data) * 2) + l := len(data) * 2 + arr := jsutil.TemporaryUint8Array(l) jsutil.CopySliceToJS(arr, data) - gl.Call("bufferSubData", gles.ELEMENT_ARRAY_BUFFER, 0, arr) + if isWebGL2Available { + gl.Call("bufferSubData", gles.ELEMENT_ARRAY_BUFFER, 0, arr, 0, l) + } else { + gl.Call("bufferSubData", gles.ELEMENT_ARRAY_BUFFER, 0, arr.Call("subarray", 0, l)) + } } func (c *context) deleteBuffer(b buffer) { @@ -531,13 +541,20 @@ func (c *context) canUsePBO() bool { func (c *context) texSubImage2D(t textureNative, width, height int, args []*driver.ReplacePixelsArgs) { c.bindTexture(t) gl := c.gl - // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, - // GLsizei width, GLsizei height, - // GLenum format, GLenum type, ArrayBufferView? pixels); for _, a := range args { arr := jsutil.TemporaryUint8Array(len(a.Pixels)) jsutil.CopySliceToJS(arr, a.Pixels) - gl.Call("texSubImage2D", gles.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, gles.RGBA, gles.UNSIGNED_BYTE, arr) + if isWebGL2Available { + // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + // GLsizei width, GLsizei height, + // GLenum format, GLenum type, ArrayBufferView pixels, srcOffset); + gl.Call("texSubImage2D", gles.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, gles.RGBA, gles.UNSIGNED_BYTE, arr, 0) + } else { + // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + // GLsizei width, GLsizei height, + // GLenum format, GLenum type, ArrayBufferView? pixels); + gl.Call("texSubImage2D", gles.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, gles.RGBA, gles.UNSIGNED_BYTE, arr) + } } } @@ -551,6 +568,10 @@ func (c *context) newPixelBufferObject(width, height int) buffer { } func (c *context) replacePixelsWithPBO(buffer buffer, t textureNative, width, height int, args []*driver.ReplacePixelsArgs) { + if !isWebGL2Available { + panic("opengl: WebGL2 must be available when replacePixelsWithPBO is called") + } + c.bindTexture(t) gl := c.gl gl.Call("bindBuffer", gles.PIXEL_UNPACK_BUFFER, js.Value(buffer)) @@ -577,7 +598,11 @@ func (c *context) getBufferSubData(buffer buffer, width, height int) []byte { gl.Call("bindBuffer", gles.PIXEL_UNPACK_BUFFER, buffer) l := 4 * width * height arr := jsutil.TemporaryUint8Array(l) - gl.Call("getBufferSubData", gles.PIXEL_UNPACK_BUFFER, 0, arr) + if isWebGL2Available { + gl.Call("getBufferSubData", gles.PIXEL_UNPACK_BUFFER, 0, arr, 0, l) + } else { + gl.Call("getBufferSubData", gles.PIXEL_UNPACK_BUFFER, 0, arr.Call("subarray", 0, l)) + } gl.Call("bindBuffer", gles.PIXEL_UNPACK_BUFFER, 0) return jsutil.Uint8ArrayToSlice(arr, l) } diff --git a/internal/jsutil/buf_js.go b/internal/jsutil/buf_js.go index b9f4e88b0..407056941 100644 --- a/internal/jsutil/buf_js.go +++ b/internal/jsutil/buf_js.go @@ -23,12 +23,18 @@ import ( // This enables to avoid unnecessary allocations of js.Value. var isTypedArrayWritable = js.Global().Get("go2cpp").Truthy() -// temporaryArrayBuffer is a temporary buffer used at gl.readPixels or gl.texSubImage2D. -// The read data is converted to Go's byte slice as soon as possible. -// To avoid often allocating ArrayBuffer, reuse the buffer whenever possible. -var temporaryArrayBuffer = js.Global().Get("ArrayBuffer").New(16) +var ( + // temporaryArrayBuffer is a temporary buffer used at gl.readPixels or gl.texSubImage2D. + // The read data is converted to Go's byte slice as soon as possible. + // To avoid often allocating ArrayBuffer, reuse the buffer whenever possible. + temporaryArrayBuffer = js.Global().Get("ArrayBuffer").New(16) -var temporaryFloat32Array = js.Global().Get("Float32Array").New(temporaryArrayBuffer) + // temporaryUint8Array is a Uint8ArrayBuffer whose underlying buffer is always temporaryArrayBuffer. + temporaryUint8Array = js.Global().Get("Uint8Array").New(temporaryArrayBuffer) + + // temporaryFloat32Array is a Float32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer. + temporaryFloat32Array = js.Global().Get("Float32Array").New(temporaryArrayBuffer) +) func ensureTemporaryArrayBufferSize(byteLength int) { if bufl := temporaryArrayBuffer.Get("byteLength").Int(); bufl < byteLength { @@ -39,20 +45,29 @@ func ensureTemporaryArrayBufferSize(byteLength int) { } } -func ensureTemporaryFloat32ArraySize(length int) { - ensureTemporaryArrayBufferSize(length * 4) +// TemporaryUint8Array returns a Uint8Array whose length is at least minLength. +// Be careful that the length can exceed the given minLength. +func TemporaryUint8Array(minLength int) js.Value { + ensureTemporaryArrayBufferSize(minLength) + if temporaryUint8Array.Get("byteLength").Int() < temporaryArrayBuffer.Get("byteLength").Int() { + temporaryUint8Array = js.Global().Get("Uint8Array").New(temporaryArrayBuffer) + } + return temporaryUint8Array +} + +// TemporaryFloat32Array returns a Float32Array whose length is at least minLength. +// Be careful that the length can exceed the given minLength. +func TemporaryFloat32Array(minLength int) js.Value { + ensureTemporaryArrayBufferSize(minLength * 4) if temporaryFloat32Array.Get("byteLength").Int() < temporaryArrayBuffer.Get("byteLength").Int() { temporaryFloat32Array = js.Global().Get("Float32Array").New(temporaryArrayBuffer) } -} - -func TemporaryUint8Array(byteLength int) js.Value { - ensureTemporaryArrayBufferSize(byteLength) - return uint8Array(temporaryArrayBuffer, 0, byteLength) + return temporaryFloat32Array } var uint8ArrayObj js.Value +// TODO: Remove this func uint8Array(buffer js.Value, byteOffset, byteLength int) js.Value { if isTypedArrayWritable { if Equal(uint8ArrayObj, js.Undefined()) { @@ -65,10 +80,3 @@ func uint8Array(buffer js.Value, byteOffset, byteLength int) js.Value { } return js.Global().Get("Uint8Array").New(buffer, byteOffset, byteLength) } - -// TemporaryFloat32Array returns a Float32Array whose length is at least minLength. -// Be careful that the length can exceed the given minLength. -func TemporaryFloat32Array(minLength int) js.Value { - ensureTemporaryFloat32ArraySize(minLength * 4) - return temporaryFloat32Array -}