mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 02:38:53 +01:00
graphicsdriver/opengl: Use glBufferSubData instead of glTexSubImage2D on browsers
Updates #988
This commit is contained in:
parent
de48a13a6e
commit
e66f1fb71e
@ -228,14 +228,6 @@ func (c *context) isTexture(t textureNative) bool {
|
||||
return r
|
||||
}
|
||||
|
||||
func (c *context) texSubImage2D(t textureNative, p []byte, x, y, width, height int) {
|
||||
c.bindTexture(t)
|
||||
_ = c.t.Call(func() error {
|
||||
gl.TexSubImage2D(gl.TEXTURE_2D, 0, int32(x), int32(y), int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(p))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *context) newFramebuffer(texture textureNative) (framebufferNative, error) {
|
||||
var framebuffer framebufferNative
|
||||
var f uint32
|
||||
@ -534,6 +526,14 @@ func (c *context) needsRestoring() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *context) canUsePBO() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *context) texSubImage2D(t textureNative, width, height int, args []*driver.ReplacePixelsArgs) {
|
||||
panic("opengl: texSubImage2D is not implemented on this environment")
|
||||
}
|
||||
|
||||
func (c *context) newPixelBufferObject(width, height int) buffer {
|
||||
var bf buffer
|
||||
_ = c.t.Call(func() error {
|
||||
|
@ -72,47 +72,102 @@ func getProgramID(p program) programID {
|
||||
}
|
||||
|
||||
var (
|
||||
vertexShader shaderType
|
||||
fragmentShader shaderType
|
||||
arrayBuffer bufferType
|
||||
elementArrayBuffer bufferType
|
||||
dynamicDraw bufferUsage
|
||||
streamDraw bufferUsage
|
||||
pixelUnpackBuffer bufferType
|
||||
short dataType
|
||||
float dataType
|
||||
|
||||
zero operation
|
||||
one operation
|
||||
srcAlpha operation
|
||||
dstAlpha operation
|
||||
oneMinusSrcAlpha operation
|
||||
oneMinusDstAlpha operation
|
||||
|
||||
blend js.Value
|
||||
clampToEdge js.Value
|
||||
compileStatus js.Value
|
||||
colorAttachment0 js.Value
|
||||
framebuffer_ js.Value
|
||||
framebufferBinding js.Value
|
||||
framebufferComplete js.Value
|
||||
highFloat js.Value
|
||||
linkStatus js.Value
|
||||
maxTextureSize js.Value
|
||||
nearest js.Value
|
||||
noError js.Value
|
||||
rgba js.Value
|
||||
texture2d js.Value
|
||||
textureMagFilter js.Value
|
||||
textureMinFilter js.Value
|
||||
textureWrapS js.Value
|
||||
textureWrapT js.Value
|
||||
triangles js.Value
|
||||
unpackAlignment js.Value
|
||||
unsignedByte js.Value
|
||||
unsignedShort js.Value
|
||||
|
||||
isWebGL2Available bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Accessing the prototype is rquired on Safari.
|
||||
contextPrototype = js.Global().Get("WebGLRenderingContext").Get("prototype")
|
||||
var contextPrototype js.Value
|
||||
if !jsutil.Equal(js.Global().Get("WebGL2RenderingContext"), js.Undefined()) {
|
||||
contextPrototype = js.Global().Get("WebGL2RenderingContext").Get("prototype")
|
||||
isWebGL2Available = true
|
||||
} else {
|
||||
contextPrototype = js.Global().Get("WebGLRenderingContext").Get("prototype")
|
||||
}
|
||||
|
||||
vertexShader = shaderType(contextPrototype.Get("VERTEX_SHADER").Int())
|
||||
fragmentShader = shaderType(contextPrototype.Get("FRAGMENT_SHADER").Int())
|
||||
arrayBuffer = bufferType(contextPrototype.Get("ARRAY_BUFFER").Int())
|
||||
vertexShader = shaderType(contextPrototype.Get("VERTEX_SHADER").Int())
|
||||
fragmentShader = shaderType(contextPrototype.Get("FRAGMENT_SHADER").Int())
|
||||
arrayBuffer = bufferType(contextPrototype.Get("ARRAY_BUFFER").Int())
|
||||
elementArrayBuffer = bufferType(contextPrototype.Get("ELEMENT_ARRAY_BUFFER").Int())
|
||||
dynamicDraw = bufferUsage(contextPrototype.Get("DYNAMIC_DRAW").Int())
|
||||
short = dataType(contextPrototype.Get("SHORT").Int())
|
||||
float = dataType(contextPrototype.Get("FLOAT").Int())
|
||||
dynamicDraw = bufferUsage(contextPrototype.Get("DYNAMIC_DRAW").Int())
|
||||
streamDraw = bufferUsage(contextPrototype.Get("STREAM_DRAW").Int())
|
||||
short = dataType(contextPrototype.Get("SHORT").Int())
|
||||
float = dataType(contextPrototype.Get("FLOAT").Int())
|
||||
|
||||
zero = operation(contextPrototype.Get("ZERO").Int())
|
||||
one = operation(contextPrototype.Get("ONE").Int())
|
||||
srcAlpha = operation(contextPrototype.Get("SRC_ALPHA").Int())
|
||||
dstAlpha = operation(contextPrototype.Get("DST_ALPHA").Int())
|
||||
zero = operation(contextPrototype.Get("ZERO").Int())
|
||||
one = operation(contextPrototype.Get("ONE").Int())
|
||||
srcAlpha = operation(contextPrototype.Get("SRC_ALPHA").Int())
|
||||
dstAlpha = operation(contextPrototype.Get("DST_ALPHA").Int())
|
||||
oneMinusSrcAlpha = operation(contextPrototype.Get("ONE_MINUS_SRC_ALPHA").Int())
|
||||
oneMinusDstAlpha = operation(contextPrototype.Get("ONE_MINUS_DST_ALPHA").Int())
|
||||
|
||||
blend = contextPrototype.Get("BLEND")
|
||||
clampToEdge = contextPrototype.Get("CLAMP_TO_EDGE")
|
||||
compileStatus = contextPrototype.Get("COMPILE_STATUS")
|
||||
colorAttachment0 = contextPrototype.Get("COLOR_ATTACHMENT0")
|
||||
framebuffer_ = contextPrototype.Get("FRAMEBUFFER")
|
||||
framebufferBinding = contextPrototype.Get("FRAMEBUFFER_BINDING")
|
||||
blend = contextPrototype.Get("BLEND")
|
||||
clampToEdge = contextPrototype.Get("CLAMP_TO_EDGE")
|
||||
compileStatus = contextPrototype.Get("COMPILE_STATUS")
|
||||
colorAttachment0 = contextPrototype.Get("COLOR_ATTACHMENT0")
|
||||
framebuffer_ = contextPrototype.Get("FRAMEBUFFER")
|
||||
framebufferBinding = contextPrototype.Get("FRAMEBUFFER_BINDING")
|
||||
framebufferComplete = contextPrototype.Get("FRAMEBUFFER_COMPLETE")
|
||||
highFloat = contextPrototype.Get("HIGH_FLOAT")
|
||||
linkStatus = contextPrototype.Get("LINK_STATUS")
|
||||
maxTextureSize = contextPrototype.Get("MAX_TEXTURE_SIZE")
|
||||
nearest = contextPrototype.Get("NEAREST")
|
||||
noError = contextPrototype.Get("NO_ERROR")
|
||||
rgba = contextPrototype.Get("RGBA")
|
||||
texture2d = contextPrototype.Get("TEXTURE_2D")
|
||||
textureMagFilter = contextPrototype.Get("TEXTURE_MAG_FILTER")
|
||||
textureMinFilter = contextPrototype.Get("TEXTURE_MIN_FILTER")
|
||||
textureWrapS = contextPrototype.Get("TEXTURE_WRAP_S")
|
||||
textureWrapT = contextPrototype.Get("TEXTURE_WRAP_T")
|
||||
triangles = contextPrototype.Get("TRIANGLES")
|
||||
unpackAlignment = contextPrototype.Get("UNPACK_ALIGNMENT")
|
||||
unsignedByte = contextPrototype.Get("UNSIGNED_BYTE")
|
||||
unsignedShort = contextPrototype.Get("UNSIGNED_SHORT")
|
||||
)
|
||||
highFloat = contextPrototype.Get("HIGH_FLOAT")
|
||||
linkStatus = contextPrototype.Get("LINK_STATUS")
|
||||
maxTextureSize = contextPrototype.Get("MAX_TEXTURE_SIZE")
|
||||
nearest = contextPrototype.Get("NEAREST")
|
||||
noError = contextPrototype.Get("NO_ERROR")
|
||||
rgba = contextPrototype.Get("RGBA")
|
||||
texture2d = contextPrototype.Get("TEXTURE_2D")
|
||||
textureMagFilter = contextPrototype.Get("TEXTURE_MAG_FILTER")
|
||||
textureMinFilter = contextPrototype.Get("TEXTURE_MIN_FILTER")
|
||||
textureWrapS = contextPrototype.Get("TEXTURE_WRAP_S")
|
||||
textureWrapT = contextPrototype.Get("TEXTURE_WRAP_T")
|
||||
triangles = contextPrototype.Get("TRIANGLES")
|
||||
unpackAlignment = contextPrototype.Get("UNPACK_ALIGNMENT")
|
||||
unsignedByte = contextPrototype.Get("UNSIGNED_BYTE")
|
||||
unsignedShort = contextPrototype.Get("UNSIGNED_SHORT")
|
||||
|
||||
if isWebGL2Available {
|
||||
pixelUnpackBuffer = bufferType(contextPrototype.Get("PIXEL_UNPACK_BUFFER").Int())
|
||||
}
|
||||
}
|
||||
|
||||
type contextImpl struct {
|
||||
gl js.Value
|
||||
@ -124,19 +179,22 @@ func (c *context) ensureGL() {
|
||||
return
|
||||
}
|
||||
|
||||
if jsutil.Equal(js.Global().Get("WebGLRenderingContext"), js.Undefined()) {
|
||||
panic("opengl: WebGL is not supported")
|
||||
}
|
||||
// TODO: Define id?
|
||||
canvas := js.Global().Get("document").Call("querySelector", "canvas")
|
||||
attr := js.Global().Get("Object").New()
|
||||
attr.Set("alpha", true)
|
||||
attr.Set("premultipliedAlpha", true)
|
||||
gl := canvas.Call("getContext", "webgl", attr)
|
||||
if jsutil.Equal(gl, js.Null()) {
|
||||
gl = canvas.Call("getContext", "experimental-webgl", attr)
|
||||
|
||||
var gl js.Value
|
||||
if isWebGL2Available {
|
||||
gl = canvas.Call("getContext", "webgl2", attr)
|
||||
} else {
|
||||
gl = canvas.Call("getContext", "webgl", attr)
|
||||
if jsutil.Equal(gl, js.Null()) {
|
||||
panic("opengl: getContext failed")
|
||||
gl = canvas.Call("getContext", "experimental-webgl", attr)
|
||||
if jsutil.Equal(gl, js.Null()) {
|
||||
panic("opengl: getContext failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,18 +303,6 @@ func (c *context) isTexture(t textureNative) bool {
|
||||
return gl.Call("isTexture", js.Value(t)).Bool()
|
||||
}
|
||||
|
||||
func (c *context) texSubImage2D(t textureNative, pixels []byte, x, y, width, height int) {
|
||||
c.bindTexture(t)
|
||||
c.ensureGL()
|
||||
gl := c.gl
|
||||
// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
|
||||
// GLsizei width, GLsizei height,
|
||||
// GLenum format, GLenum type, ArrayBufferView? pixels);
|
||||
arr := jsutil.TemporaryUint8Array(len(pixels))
|
||||
jsutil.CopySliceToJS(arr, pixels)
|
||||
gl.Call("texSubImage2D", texture2d, 0, x, y, width, height, rgba, unsignedByte, arr)
|
||||
}
|
||||
|
||||
func (c *context) newFramebuffer(t textureNative) (framebufferNative, error) {
|
||||
c.ensureGL()
|
||||
gl := c.gl
|
||||
@ -494,3 +540,54 @@ func (c *context) flush() {
|
||||
func (c *context) needsRestoring() bool {
|
||||
return !web.IsMobileBrowser()
|
||||
}
|
||||
|
||||
func (c *context) canUsePBO() bool {
|
||||
return isWebGL2Available
|
||||
}
|
||||
|
||||
func (c *context) texSubImage2D(t textureNative, width, height int, args []*driver.ReplacePixelsArgs) {
|
||||
c.ensureGL()
|
||||
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", texture2d, 0, a.X, a.Y, a.Width, a.Height, rgba, unsignedByte, arr)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *context) newPixelBufferObject(width, height int) buffer {
|
||||
c.ensureGL()
|
||||
gl := c.gl
|
||||
b := gl.Call("createBuffer")
|
||||
gl.Call("bindBuffer", int(pixelUnpackBuffer), js.Value(b))
|
||||
gl.Call("bufferData", int(pixelUnpackBuffer), 4*width*height, int(streamDraw))
|
||||
gl.Call("bindBuffer", int(pixelUnpackBuffer), nil)
|
||||
return buffer(b)
|
||||
}
|
||||
|
||||
func (c *context) replacePixelsWithPBO(buffer buffer, t textureNative, width, height int, args []*driver.ReplacePixelsArgs) {
|
||||
c.ensureGL()
|
||||
c.bindTexture(t)
|
||||
gl := c.gl
|
||||
gl.Call("bindBuffer", int(pixelUnpackBuffer), js.Value(buffer))
|
||||
|
||||
stride := 4 * width
|
||||
for _, a := range args {
|
||||
arr := jsutil.TemporaryUint8Array(len(a.Pixels))
|
||||
jsutil.CopySliceToJS(arr, a.Pixels)
|
||||
offset := 4 * (a.Y*width + a.X)
|
||||
for j := 0; j < a.Height; j++ {
|
||||
gl.Call("bufferSubData", int(pixelUnpackBuffer), offset+stride*j, arr, 4*a.Width*j, 4*a.Width)
|
||||
}
|
||||
}
|
||||
|
||||
// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
|
||||
// GLsizei width, GLsizei height,
|
||||
// GLenum format, GLenum type, GLintptr offset);
|
||||
gl.Call("texSubImage2D", texture2d, 0, 0, 0, width, height, rgba, unsignedByte, 0)
|
||||
gl.Call("bindBuffer", int(pixelUnpackBuffer), nil)
|
||||
}
|
||||
|
@ -177,12 +177,6 @@ func (c *context) isTexture(t textureNative) bool {
|
||||
return gl.IsTexture(mgl.Texture(t))
|
||||
}
|
||||
|
||||
func (c *context) texSubImage2D(t textureNative, p []byte, x, y, width, height int) {
|
||||
c.bindTexture(t)
|
||||
gl := c.gl
|
||||
gl.TexSubImage2D(mgl.TEXTURE_2D, 0, x, y, width, height, mgl.RGBA, mgl.UNSIGNED_BYTE, p)
|
||||
}
|
||||
|
||||
func (c *context) newFramebuffer(texture textureNative) (framebufferNative, error) {
|
||||
gl := c.gl
|
||||
f := gl.CreateFramebuffer()
|
||||
@ -393,3 +387,45 @@ func (c *context) flush() {
|
||||
func (c *context) needsRestoring() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *context) canUsePBO() bool {
|
||||
// The implementation for PBO is almost done, but not finished yet due to Go mobile interface.
|
||||
// See golang/go#36355.
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *context) texSubImage2D(t textureNative, width, height int, args []*driver.ReplacePixelsArgs) {
|
||||
c.bindTexture(t)
|
||||
gl := c.gl
|
||||
for _, a := range args {
|
||||
gl.TexSubImage2D(mgl.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, mgl.RGBA, mgl.UNSIGNED_BYTE, a.Pixels)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *context) newPixelBufferObject(width, height int) buffer {
|
||||
gl := c.gl
|
||||
b := gl.CreateBuffer()
|
||||
gl.BindBuffer(mgl.PIXEL_UNPACK_BUFFER, b)
|
||||
gl.BufferInit(mgl.PIXEL_UNPACK_BUFFER, 4*width*height, mgl.STREAM_DRAW)
|
||||
gl.BindBuffer(mgl.PIXEL_UNPACK_BUFFER, mgl.Buffer{0})
|
||||
return buffer(b)
|
||||
}
|
||||
|
||||
func (c *context) replacePixelsWithPBO(buffer buffer, t textureNative, width, height int, args []*driver.ReplacePixelsArgs) {
|
||||
c.bindTexture(t)
|
||||
gl := c.gl
|
||||
gl.BindBuffer(mgl.PIXEL_UNPACK_BUFFER, mgl.Buffer(buffer))
|
||||
|
||||
stride := 4 * width
|
||||
for _, a := range args {
|
||||
offset := 4 * (a.Y*width + a.X)
|
||||
for j := 0; j < a.Height; j++ {
|
||||
gl.BufferSubData(mgl.PIXEL_UNPACK_BUFFER, offset+stride*j, a.Pixels[4*a.Width*j:4*a.Width*(j+1)])
|
||||
}
|
||||
}
|
||||
|
||||
// This implementation is still wrong since TexSubImage2D cannot take an offset integer.
|
||||
// See golang/go#36355.
|
||||
gl.TexSubImage2D(mgl.TEXTURE_2D, 0, 0, 0, width, height, mgl.RGBA, mgl.UNSIGNED_BYTE, nil)
|
||||
gl.BindBuffer(mgl.PIXEL_UNPACK_BUFFER, mgl.Buffer{0})
|
||||
}
|
||||
|
@ -105,14 +105,19 @@ func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
|
||||
}
|
||||
i.driver.drawCalled = false
|
||||
|
||||
if !canUsePBO {
|
||||
for _, a := range args {
|
||||
i.driver.context.texSubImage2D(i.textureNative, a.Pixels, a.X, a.Y, a.Width, a.Height)
|
||||
}
|
||||
w, h := i.width, i.height
|
||||
if !i.driver.context.canUsePBO() {
|
||||
i.driver.context.texSubImage2D(i.textureNative, w, h, args)
|
||||
return
|
||||
}
|
||||
if i.pbo.equal(*new(buffer)) {
|
||||
i.pbo = i.driver.context.newPixelBufferObject(w, h)
|
||||
}
|
||||
if i.pbo.equal(*new(buffer)) {
|
||||
panic("opengl: newPixelBufferObject failed")
|
||||
}
|
||||
|
||||
drawPixelsWithPBO(i, args)
|
||||
i.driver.context.replacePixelsWithPBO(i.pbo, i.textureNative, w, h, args)
|
||||
}
|
||||
|
||||
func (i *Image) SetAsSource() {
|
||||
|
@ -1,38 +0,0 @@
|
||||
// Copyright 2019 The Ebiten Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build darwin freebsd linux windows
|
||||
// +build !js
|
||||
// +build !android
|
||||
// +build !ios
|
||||
|
||||
package opengl
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/internal/driver"
|
||||
)
|
||||
|
||||
const canUsePBO = true
|
||||
|
||||
func drawPixelsWithPBO(img *Image, args []*driver.ReplacePixelsArgs) {
|
||||
w, h := img.width, img.height
|
||||
if img.pbo == *new(buffer) {
|
||||
img.pbo = img.driver.context.newPixelBufferObject(w, h)
|
||||
}
|
||||
if img.pbo == *new(buffer) {
|
||||
panic("opengl: newPixelBufferObject failed")
|
||||
}
|
||||
|
||||
img.driver.context.replacePixelsWithPBO(img.pbo, img.textureNative, w, h, args)
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
// Copyright 2019 The Ebiten Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build js android ios
|
||||
|
||||
package opengl
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/internal/driver"
|
||||
)
|
||||
|
||||
const canUsePBO = false
|
||||
|
||||
func drawPixelsWithPBO(img *Image, args []*driver.ReplacePixelsArgs) {
|
||||
panic("opengl: PBO is not available in this environment")
|
||||
}
|
@ -18,7 +18,7 @@ import (
|
||||
"syscall/js"
|
||||
)
|
||||
|
||||
// temporaryBuffer is a temporary buffer used at gl.readPixels.
|
||||
// temporaryBuffer 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 temporaryBuffer = js.Global().Get("ArrayBuffer").New(16)
|
||||
|
Loading…
Reference in New Issue
Block a user