From f611b48c714b3af920fdf414b813bbde1d4203ca Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 18 Oct 2020 18:28:23 +0900 Subject: [PATCH] graphicsdriver/opengl: Use native GLES functions for mobiles After this change, GL functions for mobiles, especially Android, are native ones instead of golang.org/x/mobile/gl functions in order to reduce goroutine context switches. On gomobile-build, golang.org/x/mobile/gl functions are still used. Fixes #1387 --- .../graphicsdriver/opengl/context_mobile.go | 254 ++++++-------- .../graphicsdriver/opengl/gles/default.go | 313 ++++++++++++++++++ .../graphicsdriver/opengl/gles/gomobile.go | 298 +++++++++++++++++ .../graphicsdriver/opengl/gles/interface.go | 77 +++++ internal/graphicsdriver/opengl/gles/str.go | 39 +++ .../graphicsdriver/opengl/graphics_mobile.go | 10 +- internal/uidriver/mobile/ui.go | 56 +--- 7 files changed, 851 insertions(+), 196 deletions(-) create mode 100644 internal/graphicsdriver/opengl/gles/default.go create mode 100644 internal/graphicsdriver/opengl/gles/gomobile.go create mode 100644 internal/graphicsdriver/opengl/gles/interface.go create mode 100644 internal/graphicsdriver/opengl/gles/str.go diff --git a/internal/graphicsdriver/opengl/context_mobile.go b/internal/graphicsdriver/opengl/context_mobile.go index 41752f05a..a4658fb6f 100644 --- a/internal/graphicsdriver/opengl/context_mobile.go +++ b/internal/graphicsdriver/opengl/context_mobile.go @@ -20,20 +20,17 @@ import ( "errors" "fmt" - // TODO: Remove this dependency (#1387) - mgl "golang.org/x/mobile/gl" - "github.com/hajimehoshi/ebiten/v2/internal/driver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gles" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" ) type ( - textureNative mgl.Texture - framebufferNative mgl.Framebuffer - shader mgl.Shader - program mgl.Program - buffer mgl.Buffer + textureNative uint32 + framebufferNative uint32 + shader uint32 + program uint32 + buffer uint32 ) func (t textureNative) equal(rhs textureNative) bool { @@ -59,8 +56,8 @@ func (p program) equal(rhs program) bool { var InvalidTexture textureNative type ( - uniformLocation mgl.Uniform - attribLocation mgl.Attrib + uniformLocation int32 + attribLocation int32 ) func (u uniformLocation) equal(rhs uniformLocation) bool { @@ -69,14 +66,14 @@ func (u uniformLocation) equal(rhs uniformLocation) bool { type programID uint32 -var ( - invalidTexture = textureNative(mgl.Texture{}) - invalidFramebuffer = framebufferNative(mgl.Framebuffer{Value: (1 << 32) - 1}) - invalidUniform = uniformLocation(mgl.Uniform{Value: -1}) +const ( + invalidTexture = 0 + invalidFramebuffer = (1 << 32) - 1 + invalidUniform = -1 ) func getProgramID(p program) programID { - return programID(p.Value) + return programID(p) } const ( @@ -98,7 +95,7 @@ const ( ) type contextImpl struct { - gl mgl.Context + ctx gles.Context } func (c *context) reset() error { @@ -108,115 +105,105 @@ func (c *context) reset() error { c.lastViewportWidth = 0 c.lastViewportHeight = 0 c.lastCompositeMode = driver.CompositeModeUnknown - c.gl.Enable(gles.BLEND) + c.ctx.Enable(gles.BLEND) c.blendFunc(driver.CompositeModeSourceOver) - f := c.gl.GetInteger(gles.FRAMEBUFFER_BINDING) - c.screenFramebuffer = framebufferNative(mgl.Framebuffer{uint32(f)}) + f := make([]int32, 1) + c.ctx.GetIntegerv(f, gles.FRAMEBUFFER_BINDING) + c.screenFramebuffer = framebufferNative(f[0]) // TODO: Need to update screenFramebufferWidth/Height? return nil } func (c *context) blendFunc(mode driver.CompositeMode) { - gl := c.gl if c.lastCompositeMode == mode { return } c.lastCompositeMode = mode s, d := mode.Operations() s2, d2 := convertOperation(s), convertOperation(d) - gl.BlendFunc(mgl.Enum(s2), mgl.Enum(d2)) + c.ctx.BlendFunc(uint32(s2), uint32(d2)) } func (c *context) newTexture(width, height int) (textureNative, error) { - gl := c.gl - t := gl.CreateTexture() - if t.Value <= 0 { - return textureNative{}, errors.New("opengl: creating texture failed") + t := c.ctx.GenTextures(1)[0] + if t <= 0 { + return 0, errors.New("opengl: creating texture failed") } - gl.PixelStorei(gles.UNPACK_ALIGNMENT, 4) + c.ctx.PixelStorei(gles.UNPACK_ALIGNMENT, 4) c.bindTexture(textureNative(t)) - gl.TexParameteri(gles.TEXTURE_2D, gles.TEXTURE_MAG_FILTER, gles.NEAREST) - gl.TexParameteri(gles.TEXTURE_2D, gles.TEXTURE_MIN_FILTER, gles.NEAREST) - gl.TexParameteri(gles.TEXTURE_2D, gles.TEXTURE_WRAP_S, gles.CLAMP_TO_EDGE) - gl.TexParameteri(gles.TEXTURE_2D, gles.TEXTURE_WRAP_T, gles.CLAMP_TO_EDGE) - gl.TexImage2D(gles.TEXTURE_2D, 0, gles.RGBA, width, height, gles.RGBA, gles.UNSIGNED_BYTE, nil) + c.ctx.TexParameteri(gles.TEXTURE_2D, gles.TEXTURE_MAG_FILTER, gles.NEAREST) + c.ctx.TexParameteri(gles.TEXTURE_2D, gles.TEXTURE_MIN_FILTER, gles.NEAREST) + c.ctx.TexParameteri(gles.TEXTURE_2D, gles.TEXTURE_WRAP_S, gles.CLAMP_TO_EDGE) + c.ctx.TexParameteri(gles.TEXTURE_2D, gles.TEXTURE_WRAP_T, gles.CLAMP_TO_EDGE) + c.ctx.TexImage2D(gles.TEXTURE_2D, 0, gles.RGBA, int32(width), int32(height), gles.RGBA, gles.UNSIGNED_BYTE, nil) return textureNative(t), nil } func (c *context) bindFramebufferImpl(f framebufferNative) { - gl := c.gl - gl.BindFramebuffer(gles.FRAMEBUFFER, mgl.Framebuffer(f)) + c.ctx.BindFramebuffer(gles.FRAMEBUFFER, uint32(f)) } func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte { - gl := c.gl - gl.Flush() + c.ctx.Flush() c.bindFramebuffer(f.native) pixels := make([]byte, 4*width*height) - gl.ReadPixels(pixels, 0, 0, width, height, gles.RGBA, gles.UNSIGNED_BYTE) + c.ctx.ReadPixels(pixels, 0, 0, int32(width), int32(height), gles.RGBA, gles.UNSIGNED_BYTE) return pixels } func (c *context) activeTexture(idx int) { - gl := c.gl - gl.ActiveTexture(mgl.Enum(gles.TEXTURE0 + idx)) + c.ctx.ActiveTexture(uint32(gles.TEXTURE0 + idx)) } func (c *context) bindTextureImpl(t textureNative) { - gl := c.gl - gl.BindTexture(gles.TEXTURE_2D, mgl.Texture(t)) + c.ctx.BindTexture(gles.TEXTURE_2D, uint32(t)) } func (c *context) deleteTexture(t textureNative) { - gl := c.gl - if !gl.IsTexture(mgl.Texture(t)) { + if !c.ctx.IsTexture(uint32(t)) { return } if c.lastTexture == t { c.lastTexture = invalidTexture } - gl.DeleteTexture(mgl.Texture(t)) + c.ctx.DeleteTextures([]uint32{uint32(t)}) } func (c *context) isTexture(t textureNative) bool { - gl := c.gl - return gl.IsTexture(mgl.Texture(t)) + return c.ctx.IsTexture(uint32(t)) } func (c *context) newFramebuffer(texture textureNative) (framebufferNative, error) { - gl := c.gl - f := gl.CreateFramebuffer() - if f.Value <= 0 { - return framebufferNative{}, fmt.Errorf("opengl: creating framebuffer failed: the returned value is not positive but %d", f.Value) + f := c.ctx.GenFramebuffers(1)[0] + if f <= 0 { + return 0, fmt.Errorf("opengl: creating framebuffer failed: the returned value is not positive but %d", f) } c.bindFramebuffer(framebufferNative(f)) - gl.FramebufferTexture2D(gles.FRAMEBUFFER, gles.COLOR_ATTACHMENT0, gles.TEXTURE_2D, mgl.Texture(texture), 0) - s := gl.CheckFramebufferStatus(gles.FRAMEBUFFER) + c.ctx.FramebufferTexture2D(gles.FRAMEBUFFER, gles.COLOR_ATTACHMENT0, gles.TEXTURE_2D, uint32(texture), 0) + s := c.ctx.CheckFramebufferStatus(gles.FRAMEBUFFER) if s != gles.FRAMEBUFFER_COMPLETE { if s != 0 { - return framebufferNative{}, fmt.Errorf("opengl: creating framebuffer failed: %v", s) + return 0, fmt.Errorf("opengl: creating framebuffer failed: %v", s) } - if e := gl.GetError(); e != gles.NO_ERROR { - return framebufferNative{}, fmt.Errorf("opengl: creating framebuffer failed: (glGetError) %d", e) + if e := c.ctx.GetError(); e != gles.NO_ERROR { + return 0, fmt.Errorf("opengl: creating framebuffer failed: (glGetError) %d", e) } - return framebufferNative{}, fmt.Errorf("opengl: creating framebuffer failed: unknown error") + return 0, fmt.Errorf("opengl: creating framebuffer failed: unknown error") } return framebufferNative(f), nil } func (c *context) setViewportImpl(width, height int) { - gl := c.gl - gl.Viewport(0, 0, width, height) + c.ctx.Viewport(0, 0, int32(width), int32(height)) } func (c *context) deleteFramebuffer(f framebufferNative) { - gl := c.gl - if !gl.IsFramebuffer(mgl.Framebuffer(f)) { + if !c.ctx.IsFramebuffer(uint32(f)) { return } // If a framebuffer to be deleted is bound, a newly bound framebuffer @@ -227,96 +214,89 @@ func (c *context) deleteFramebuffer(f framebufferNative) { c.lastViewportWidth = 0 c.lastViewportHeight = 0 } - gl.DeleteFramebuffer(mgl.Framebuffer(f)) + c.ctx.DeleteFramebuffers([]uint32{uint32(f)}) } func (c *context) newShader(shaderType shaderType, source string) (shader, error) { - gl := c.gl - s := gl.CreateShader(mgl.Enum(shaderType)) - if s.Value == 0 { - return shader{}, fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType) + s := c.ctx.CreateShader(uint32(shaderType)) + if s == 0 { + return 0, fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType) } - gl.ShaderSource(s, source) - gl.CompileShader(s) + c.ctx.ShaderSource(s, source) + c.ctx.CompileShader(s) - v := gl.GetShaderi(s, gles.COMPILE_STATUS) - if v == gles.FALSE { - log := gl.GetShaderInfoLog(s) - return shader{}, fmt.Errorf("opengl: shader compile failed: %s", log) + v := make([]int32, 1) + c.ctx.GetShaderiv(v, s, gles.COMPILE_STATUS) + if v[0] == gles.FALSE { + log := c.ctx.GetShaderInfoLog(s) + return 0, fmt.Errorf("opengl: shader compile failed: %s", log) } return shader(s), nil } func (c *context) deleteShader(s shader) { - gl := c.gl - gl.DeleteShader(mgl.Shader(s)) + c.ctx.DeleteShader(uint32(s)) } func (c *context) newProgram(shaders []shader, attributes []string) (program, error) { - gl := c.gl - p := gl.CreateProgram() - if p.Value == 0 { - return program{}, errors.New("opengl: glCreateProgram failed") + p := c.ctx.CreateProgram() + if p == 0 { + return 0, errors.New("opengl: glCreateProgram failed") } for _, shader := range shaders { - gl.AttachShader(p, mgl.Shader(shader)) + c.ctx.AttachShader(p, uint32(shader)) } for i, name := range attributes { - gl.BindAttribLocation(p, mgl.Attrib{Value: uint(i)}, name) + c.ctx.BindAttribLocation(p, uint32(i), name) } - gl.LinkProgram(p) - v := gl.GetProgrami(p, gles.LINK_STATUS) - if v == gles.FALSE { - info := gl.GetProgramInfoLog(p) - return program{}, fmt.Errorf("opengl: program error: %s", info) + c.ctx.LinkProgram(p) + v := make([]int32, 1) + c.ctx.GetProgramiv(v, p, gles.LINK_STATUS) + if v[0] == gles.FALSE { + info := c.ctx.GetProgramInfoLog(p) + return 0, fmt.Errorf("opengl: program error: %s", info) } return program(p), nil } func (c *context) useProgram(p program) { - gl := c.gl - gl.UseProgram(mgl.Program(p)) + c.ctx.UseProgram(uint32(p)) } func (c *context) deleteProgram(p program) { - gl := c.gl - if !gl.IsProgram(mgl.Program(p)) { + if !c.ctx.IsProgram(uint32(p)) { return } - gl.DeleteProgram(mgl.Program(p)) + c.ctx.DeleteProgram(uint32(p)) } func (c *context) getUniformLocationImpl(p program, location string) uniformLocation { - gl := c.gl - u := uniformLocation(gl.GetUniformLocation(mgl.Program(p), location)) + u := uniformLocation(c.ctx.GetUniformLocation(uint32(p), location)) return u } func (c *context) uniformInt(p program, location string, v int) bool { - gl := c.gl l := c.locationCache.GetUniformLocation(c, p, location) if l == invalidUniform { return false } - gl.Uniform1i(mgl.Uniform(l), v) + c.ctx.Uniform1i(int32(l), int32(v)) return true } func (c *context) uniformFloat(p program, location string, v float32) bool { - gl := c.gl l := c.locationCache.GetUniformLocation(c, p, location) if l == invalidUniform { return false } - gl.Uniform1f(mgl.Uniform(l), v) + c.ctx.Uniform1f(int32(l), v) return true } func (c *context) uniformFloats(p program, location string, v []float32, typ shaderir.Type) bool { - gl := c.gl l := c.locationCache.GetUniformLocation(c, p, location) if l == invalidUniform { return false @@ -329,19 +309,19 @@ func (c *context) uniformFloats(p program, location string, v []float32, typ sha switch base { case shaderir.Float: - gl.Uniform1fv(mgl.Uniform(l), v) + c.ctx.Uniform1fv(int32(l), v) case shaderir.Vec2: - gl.Uniform2fv(mgl.Uniform(l), v) + c.ctx.Uniform2fv(int32(l), v) case shaderir.Vec3: - gl.Uniform3fv(mgl.Uniform(l), v) + c.ctx.Uniform3fv(int32(l), v) case shaderir.Vec4: - gl.Uniform4fv(mgl.Uniform(l), v) + c.ctx.Uniform4fv(int32(l), v) case shaderir.Mat2: - gl.UniformMatrix2fv(mgl.Uniform(l), v) + c.ctx.UniformMatrix2fv(int32(l), false, v) case shaderir.Mat3: - gl.UniformMatrix3fv(mgl.Uniform(l), v) + c.ctx.UniformMatrix3fv(int32(l), false, v) case shaderir.Mat4: - gl.UniformMatrix4fv(mgl.Uniform(l), v) + c.ctx.UniformMatrix4fv(int32(l), false, v) default: panic(fmt.Sprintf("opengl: unexpected type: %s", typ.String())) } @@ -349,75 +329,64 @@ func (c *context) uniformFloats(p program, location string, v []float32, typ sha } func (c *context) vertexAttribPointer(p program, index int, size int, dataType dataType, stride int, offset int) { - gl := c.gl - gl.VertexAttribPointer(mgl.Attrib{Value: uint(index)}, size, mgl.Enum(dataType), false, stride, offset) + c.ctx.VertexAttribPointer(uint32(index), int32(size), uint32(dataType), false, int32(stride), offset) } func (c *context) enableVertexAttribArray(p program, index int) { - gl := c.gl - gl.EnableVertexAttribArray(mgl.Attrib{Value: uint(index)}) + c.ctx.EnableVertexAttribArray(uint32(index)) } func (c *context) disableVertexAttribArray(p program, index int) { - gl := c.gl - gl.DisableVertexAttribArray(mgl.Attrib{Value: uint(index)}) + c.ctx.DisableVertexAttribArray(uint32(index)) } func (c *context) newArrayBuffer(size int) buffer { - gl := c.gl - b := gl.CreateBuffer() - gl.BindBuffer(mgl.Enum(arrayBuffer), b) - gl.BufferInit(mgl.Enum(arrayBuffer), size, mgl.Enum(dynamicDraw)) + b := c.ctx.GenBuffers(1)[0] + c.ctx.BindBuffer(uint32(arrayBuffer), b) + c.ctx.BufferData(uint32(arrayBuffer), size, nil, uint32(dynamicDraw)) return buffer(b) } func (c *context) newElementArrayBuffer(size int) buffer { - gl := c.gl - b := gl.CreateBuffer() - gl.BindBuffer(mgl.Enum(elementArrayBuffer), b) - gl.BufferInit(mgl.Enum(elementArrayBuffer), size, mgl.Enum(dynamicDraw)) + b := c.ctx.GenBuffers(1)[0] + c.ctx.BindBuffer(uint32(elementArrayBuffer), b) + c.ctx.BufferData(uint32(elementArrayBuffer), size, nil, uint32(dynamicDraw)) return buffer(b) } func (c *context) bindBuffer(bufferType bufferType, b buffer) { - gl := c.gl - gl.BindBuffer(mgl.Enum(bufferType), mgl.Buffer(b)) + c.ctx.BindBuffer(uint32(bufferType), uint32(b)) } func (c *context) arrayBufferSubData(data []float32) { - gl := c.gl - gl.BufferSubData(mgl.Enum(arrayBuffer), 0, float32sToBytes(data)) + c.ctx.BufferSubData(uint32(arrayBuffer), 0, float32sToBytes(data)) } func (c *context) elementArrayBufferSubData(data []uint16) { - gl := c.gl - gl.BufferSubData(mgl.Enum(elementArrayBuffer), 0, uint16sToBytes(data)) + c.ctx.BufferSubData(uint32(elementArrayBuffer), 0, uint16sToBytes(data)) } func (c *context) deleteBuffer(b buffer) { - gl := c.gl - gl.DeleteBuffer(mgl.Buffer(b)) + c.ctx.DeleteBuffers([]uint32{uint32(b)}) } func (c *context) drawElements(len int, offsetInBytes int) { - gl := c.gl - gl.DrawElements(gles.TRIANGLES, len, gles.UNSIGNED_SHORT, offsetInBytes) + c.ctx.DrawElements(gles.TRIANGLES, int32(len), gles.UNSIGNED_SHORT, offsetInBytes) } func (c *context) maxTextureSizeImpl() int { - gl := c.gl - return gl.GetInteger(gles.MAX_TEXTURE_SIZE) + v := make([]int32, 1) + c.ctx.GetIntegerv(v, gles.MAX_TEXTURE_SIZE) + return int(v[0]) } func (c *context) getShaderPrecisionFormatPrecision() int { - gl := c.gl - _, _, p := gl.GetShaderPrecisionFormat(gles.FRAGMENT_SHADER, gles.HIGH_FLOAT) + _, _, p := c.ctx.GetShaderPrecisionFormat(gles.FRAGMENT_SHADER, gles.HIGH_FLOAT) return p } func (c *context) flush() { - gl := c.gl - gl.Flush() + c.ctx.Flush() } func (c *context) needsRestoring() bool { @@ -432,18 +401,16 @@ func (c *context) canUsePBO() bool { func (c *context) texSubImage2D(t textureNative, width, height int, args []*driver.ReplacePixelsArgs) { c.bindTexture(t) - gl := c.gl for _, a := range args { - gl.TexSubImage2D(gles.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, gles.RGBA, gles.UNSIGNED_BYTE, a.Pixels) + c.ctx.TexSubImage2D(gles.TEXTURE_2D, 0, int32(a.X), int32(a.Y), int32(a.Width), int32(a.Height), gles.RGBA, gles.UNSIGNED_BYTE, a.Pixels) } } func (c *context) newPixelBufferObject(width, height int) buffer { - gl := c.gl - b := gl.CreateBuffer() - gl.BindBuffer(gles.PIXEL_UNPACK_BUFFER, b) - gl.BufferInit(gles.PIXEL_UNPACK_BUFFER, 4*width*height, gles.STREAM_DRAW) - gl.BindBuffer(gles.PIXEL_UNPACK_BUFFER, mgl.Buffer{0}) + b := c.ctx.GenBuffers(1)[0] + c.ctx.BindBuffer(gles.PIXEL_UNPACK_BUFFER, b) + c.ctx.BufferData(gles.PIXEL_UNPACK_BUFFER, 4*width*height, nil, gles.STREAM_DRAW) + c.ctx.BindBuffer(gles.PIXEL_UNPACK_BUFFER, 0) return buffer(b) } @@ -451,17 +418,16 @@ func (c *context) replacePixelsWithPBO(buffer buffer, t textureNative, width, he // This implementation is not used yet so far. See the comment at canUsePBO. c.bindTexture(t) - gl := c.gl - gl.BindBuffer(gles.PIXEL_UNPACK_BUFFER, mgl.Buffer(buffer)) + c.ctx.BindBuffer(gles.PIXEL_UNPACK_BUFFER, uint32(buffer)) stride := 4 * width for _, a := range args { offset := 4 * (a.Y*width + a.X) for j := 0; j < a.Height; j++ { - gl.BufferSubData(gles.PIXEL_UNPACK_BUFFER, offset+stride*j, a.Pixels[4*a.Width*j:4*a.Width*(j+1)]) + c.ctx.BufferSubData(gles.PIXEL_UNPACK_BUFFER, offset+stride*j, a.Pixels[4*a.Width*j:4*a.Width*(j+1)]) } } - gl.TexSubImage2D(gles.TEXTURE_2D, 0, 0, 0, width, height, gles.RGBA, gles.UNSIGNED_BYTE, nil) - gl.BindBuffer(gles.PIXEL_UNPACK_BUFFER, mgl.Buffer{0}) + c.ctx.TexSubImage2D(gles.TEXTURE_2D, 0, 0, 0, int32(width), int32(height), gles.RGBA, gles.UNSIGNED_BYTE, nil) + c.ctx.BindBuffer(gles.PIXEL_UNPACK_BUFFER, 0) } diff --git a/internal/graphicsdriver/opengl/gles/default.go b/internal/graphicsdriver/opengl/gles/default.go new file mode 100644 index 000000000..6e0c462cb --- /dev/null +++ b/internal/graphicsdriver/opengl/gles/default.go @@ -0,0 +1,313 @@ +// Copyright 2020 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 android ios + +package gles + +// #cgo android CFLAGS: -Dos_android +// #cgo android LDFLAGS: -lGLESv2 +// #cgo ios CFLAGS: -Dos_ios +// #cgo ios LDFLAGS: -framework OpenGLES +// +// #if defined(os_android) +// #include +// #endif +// +// #if defined(os_ios) +// #include +// #endif +import "C" + +import ( + "unsafe" +) + +func glBool(x bool) C.GLboolean { + if x { + return TRUE + } + return FALSE +} + +type DefaultContext struct{} + +func (DefaultContext) ActiveTexture(texture uint32) { + C.glActiveTexture(C.GLenum(texture)) +} + +func (DefaultContext) AttachShader(program uint32, shader uint32) { + C.glAttachShader(C.GLuint(program), C.GLuint(shader)) +} + +func (DefaultContext) BindAttribLocation(program uint32, index uint32, name string) { + s, free := cString(name) + defer free() + C.glBindAttribLocation(C.GLuint(program), C.GLuint(index), (*C.GLchar)(unsafe.Pointer(s))) +} + +func (DefaultContext) BindBuffer(target uint32, buffer uint32) { + C.glBindBuffer(C.GLenum(target), C.GLuint(buffer)) +} + +func (DefaultContext) BindFramebuffer(target uint32, framebuffer uint32) { + C.glBindFramebuffer(C.GLenum(target), C.GLuint(framebuffer)) +} + +func (DefaultContext) BindTexture(target uint32, texture uint32) { + C.glBindTexture(C.GLenum(target), C.GLuint(texture)) +} + +func (DefaultContext) BlendFunc(sfactor uint32, dfactor uint32) { + C.glBlendFunc(C.GLenum(sfactor), C.GLenum(dfactor)) +} + +func (DefaultContext) BufferData(target uint32, size int, data []byte, usage uint32) { + var p *byte + if data != nil { + p = &data[0] + } + C.glBufferData(C.GLenum(target), C.GLsizeiptr(size), unsafe.Pointer(p), C.GLenum(usage)) +} + +func (DefaultContext) BufferSubData(target uint32, offset int, data []byte) { + C.glBufferSubData(C.GLenum(target), C.GLintptr(offset), C.GLsizeiptr(len(data)), unsafe.Pointer(&data[0])) +} + +func (DefaultContext) CheckFramebufferStatus(target uint32) uint32 { + return uint32(C.glCheckFramebufferStatus(C.GLenum(target))) +} + +func (DefaultContext) CompileShader(shader uint32) { + C.glCompileShader(C.GLuint(shader)) +} + +func (DefaultContext) CreateProgram() uint32 { + return uint32(C.glCreateProgram()) +} + +func (DefaultContext) CreateShader(xtype uint32) uint32 { + return uint32(C.glCreateShader(C.GLenum(xtype))) +} + +func (DefaultContext) DeleteBuffers(buffers []uint32) { + C.glDeleteBuffers(C.GLsizei(len(buffers)), (*C.GLuint)(unsafe.Pointer(&buffers[0]))) +} + +func (DefaultContext) DeleteFramebuffers(framebuffers []uint32) { + C.glDeleteFramebuffers(C.GLsizei(len(framebuffers)), (*C.GLuint)(unsafe.Pointer(&framebuffers[0]))) +} + +func (DefaultContext) DeleteProgram(program uint32) { + C.glDeleteProgram(C.GLuint(program)) +} + +func (DefaultContext) DeleteShader(shader uint32) { + C.glDeleteShader(C.GLuint(shader)) +} + +func (DefaultContext) DeleteTextures(textures []uint32) { + C.glDeleteTextures(C.GLsizei(len(textures)), (*C.GLuint)(unsafe.Pointer(&textures[0]))) +} + +func (DefaultContext) DisableVertexAttribArray(index uint32) { + C.glDisableVertexAttribArray(C.GLuint(index)) +} + +func (DefaultContext) DrawElements(mode uint32, count int32, xtype uint32, offset int) { + C.glDrawElements(C.GLenum(mode), C.GLsizei(count), C.GLenum(xtype), unsafe.Pointer(uintptr(offset))) +} + +func (DefaultContext) Enable(cap uint32) { + C.glEnable(C.GLenum(cap)) +} + +func (DefaultContext) EnableVertexAttribArray(index uint32) { + C.glEnableVertexAttribArray(C.GLuint(index)) +} + +func (DefaultContext) Flush() { + C.glFlush() +} + +func (DefaultContext) FramebufferTexture2D(target uint32, attachment uint32, textarget uint32, texture uint32, level int32) { + C.glFramebufferTexture2D(C.GLenum(target), C.GLenum(attachment), C.GLenum(textarget), C.GLuint(texture), C.GLint(level)) +} + +func (DefaultContext) GenBuffers(n int32) []uint32 { + buffers := make([]uint32, n) + C.glGenBuffers(C.GLsizei(n), (*C.GLuint)(unsafe.Pointer(&buffers[0]))) + return buffers +} + +func (DefaultContext) GenFramebuffers(n int32) []uint32 { + framebuffers := make([]uint32, n) + C.glGenFramebuffers(C.GLsizei(n), (*C.GLuint)(unsafe.Pointer(&framebuffers[0]))) + return framebuffers +} + +func (DefaultContext) GenTextures(n int32) []uint32 { + textures := make([]uint32, n) + C.glGenTextures(C.GLsizei(n), (*C.GLuint)(unsafe.Pointer(&textures[0]))) + return textures +} + +func (DefaultContext) GetError() uint32 { + return uint32(C.glGetError()) +} + +func (DefaultContext) GetIntegerv(dst []int32, pname uint32) { + C.glGetIntegerv(C.GLenum(pname), (*C.GLint)(unsafe.Pointer(&dst[0]))) +} + +func (DefaultContext) GetProgramiv(dst []int32, program uint32, pname uint32) { + C.glGetProgramiv(C.GLuint(program), C.GLenum(pname), (*C.GLint)(unsafe.Pointer(&dst[0]))) +} + +func (d DefaultContext) GetProgramInfoLog(program uint32) string { + buflens := make([]int32, 1) + d.GetProgramiv(buflens, program, INFO_LOG_LENGTH) + buflen := buflens[0] + if buflen == 0 { + return "" + } + buf := make([]byte, buflen) + var length int32 + C.glGetProgramInfoLog(C.GLuint(program), C.GLsizei(buflen), (*C.GLsizei)(unsafe.Pointer(&length)), (*C.GLchar)(unsafe.Pointer(&buf[0]))) + return string(buf[:length]) +} + +func (DefaultContext) GetShaderiv(dst []int32, shader uint32, pname uint32) { + C.glGetShaderiv(C.GLuint(shader), C.GLenum(pname), (*C.GLint)(unsafe.Pointer(&dst[0]))) +} + +func (d DefaultContext) GetShaderInfoLog(shader uint32) string { + buflens := make([]int32, 1) + d.GetShaderiv(buflens, shader, INFO_LOG_LENGTH) + buflen := buflens[0] + if buflen == 0 { + return "" + } + buf := make([]byte, buflen) + var length int32 + C.glGetShaderInfoLog(C.GLuint(shader), C.GLsizei(buflen), (*C.GLsizei)(unsafe.Pointer(&length)), (*C.GLchar)(unsafe.Pointer(&buf[0]))) + return string(buf[:length]) +} + +func (DefaultContext) GetShaderPrecisionFormat(shadertype uint32, precisiontype uint32) (rangeLow, rangeHigh, precision int) { + var r [2]int32 + var p int32 + C.glGetShaderPrecisionFormat(C.GLenum(shadertype), C.GLenum(precisiontype), (*C.GLint)(unsafe.Pointer(&r[0])), (*C.GLint)(unsafe.Pointer(&p))) + return int(r[0]), int(r[1]), int(p) +} + +func (DefaultContext) GetUniformLocation(program uint32, name string) int32 { + s, free := cString(name) + defer free() + return int32(C.glGetUniformLocation(C.GLuint(program), (*C.GLchar)(unsafe.Pointer(s)))) +} + +func (DefaultContext) IsFramebuffer(framebuffer uint32) bool { + return C.glIsFramebuffer(C.GLuint(framebuffer)) != FALSE +} + +func (DefaultContext) IsProgram(program uint32) bool { + return C.glIsProgram(C.GLuint(program)) != FALSE +} + +func (DefaultContext) IsTexture(texture uint32) bool { + return C.glIsTexture(C.GLuint(texture)) != FALSE +} + +func (DefaultContext) LinkProgram(program uint32) { + C.glLinkProgram(C.GLuint(program)) +} + +func (DefaultContext) PixelStorei(pname uint32, param int32) { + C.glPixelStorei(C.GLenum(pname), C.GLint(param)) +} + +func (DefaultContext) ReadPixels(dst []byte, x int32, y int32, width int32, height int32, format uint32, xtype uint32) { + C.glReadPixels(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(xtype), unsafe.Pointer(&dst[0])) +} + +func (DefaultContext) ShaderSource(shader uint32, xstring string) { + s, free := cStringPtr(xstring) + defer free() + C.glShaderSource(C.GLuint(shader), 1, (**C.GLchar)(unsafe.Pointer(s)), nil) +} + +func (DefaultContext) TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) { + var p *byte + if pixels != nil { + p = &pixels[0] + } + C.glTexImage2D(C.GLenum(target), C.GLint(level), C.GLint(internalformat), C.GLsizei(width), C.GLsizei(height), 0 /* border */, C.GLenum(format), C.GLenum(xtype), unsafe.Pointer(p)) +} + +func (DefaultContext) TexParameteri(target uint32, pname uint32, param int32) { + C.glTexParameteri(C.GLenum(target), C.GLenum(pname), C.GLint(param)) +} + +func (DefaultContext) TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) { + C.glTexSubImage2D(C.GLenum(target), C.GLint(level), C.GLint(xoffset), C.GLint(yoffset), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(xtype), unsafe.Pointer(&pixels[0])) +} + +func (DefaultContext) Uniform1f(location int32, v0 float32) { + C.glUniform1f(C.GLint(location), C.GLfloat(v0)) +} + +func (DefaultContext) Uniform1fv(location int32, value []float32) { + C.glUniform1fv(C.GLint(location), C.GLsizei(len(value)), (*C.GLfloat)(unsafe.Pointer(&value[0]))) +} + +func (DefaultContext) Uniform1i(location int32, v0 int32) { + C.glUniform1i(C.GLint(location), C.GLint(v0)) +} + +func (DefaultContext) Uniform2fv(location int32, value []float32) { + C.glUniform2fv(C.GLint(location), C.GLsizei(len(value)/2), (*C.GLfloat)(unsafe.Pointer(&value[0]))) +} + +func (DefaultContext) Uniform3fv(location int32, value []float32) { + C.glUniform3fv(C.GLint(location), C.GLsizei(len(value)/3), (*C.GLfloat)(unsafe.Pointer(&value[0]))) +} + +func (DefaultContext) Uniform4fv(location int32, value []float32) { + C.glUniform4fv(C.GLint(location), C.GLsizei(len(value)/4), (*C.GLfloat)(unsafe.Pointer(&value[0]))) +} + +func (DefaultContext) UniformMatrix2fv(location int32, transpose bool, value []float32) { + C.glUniformMatrix2fv(C.GLint(location), C.GLsizei(len(value)/4), glBool(transpose), (*C.GLfloat)(unsafe.Pointer(&value[0]))) +} + +func (DefaultContext) UniformMatrix3fv(location int32, transpose bool, value []float32) { + C.glUniformMatrix3fv(C.GLint(location), C.GLsizei(len(value)/9), glBool(transpose), (*C.GLfloat)(unsafe.Pointer(&value[0]))) +} + +func (DefaultContext) UniformMatrix4fv(location int32, transpose bool, value []float32) { + C.glUniformMatrix4fv(C.GLint(location), C.GLsizei(len(value)/16), glBool(transpose), (*C.GLfloat)(unsafe.Pointer(&value[0]))) +} + +func (DefaultContext) UseProgram(program uint32) { + C.glUseProgram(C.GLuint(program)) +} + +func (DefaultContext) VertexAttribPointer(index uint32, size int32, xtype uint32, normalized bool, stride int32, offset int) { + C.glVertexAttribPointer(C.GLuint(index), C.GLint(size), C.GLenum(xtype), glBool(normalized), C.GLsizei(stride), unsafe.Pointer(uintptr(offset))) +} + +func (DefaultContext) Viewport(x int32, y int32, width int32, height int32) { + C.glViewport(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height)) +} diff --git a/internal/graphicsdriver/opengl/gles/gomobile.go b/internal/graphicsdriver/opengl/gles/gomobile.go new file mode 100644 index 000000000..e5fdc2593 --- /dev/null +++ b/internal/graphicsdriver/opengl/gles/gomobile.go @@ -0,0 +1,298 @@ +// Copyright 2020 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 android ios + +package gles + +import ( + "golang.org/x/mobile/gl" +) + +type GomobileContext struct { + ctx gl.Context +} + +func gmProgram(program uint32) gl.Program { + return gl.Program{ + Init: true, + Value: program, + } +} + +func NewGomobileContext(ctx gl.Context) *GomobileContext { + return &GomobileContext{ctx} +} + +func (g *GomobileContext) ActiveTexture(texture uint32) { + g.ctx.ActiveTexture(gl.Enum(texture)) +} + +func (g *GomobileContext) AttachShader(program uint32, shader uint32) { + g.ctx.AttachShader(gmProgram(program), gl.Shader{Value: shader}) +} + +func (g *GomobileContext) BindAttribLocation(program uint32, index uint32, name string) { + g.ctx.BindAttribLocation(gmProgram(program), gl.Attrib{Value: uint(index)}, name) +} + +func (g *GomobileContext) BindBuffer(target uint32, buffer uint32) { + g.ctx.BindBuffer(gl.Enum(target), gl.Buffer{Value: buffer}) +} + +func (g *GomobileContext) BindFramebuffer(target uint32, framebuffer uint32) { + g.ctx.BindFramebuffer(gl.Enum(target), gl.Framebuffer{Value: framebuffer}) +} + +func (g *GomobileContext) BindTexture(target uint32, texture uint32) { + g.ctx.BindTexture(gl.Enum(target), gl.Texture{Value: texture}) +} + +func (g *GomobileContext) BlendFunc(sfactor uint32, dfactor uint32) { + g.ctx.BlendFunc(gl.Enum(sfactor), gl.Enum(dfactor)) +} + +func (g *GomobileContext) BufferData(target uint32, size int, data []byte, usage uint32) { + if data == nil { + g.ctx.BufferInit(gl.Enum(target), size, gl.Enum(usage)) + } else { + if size != len(data) { + panic("gles: size and len(data) must be same at BufferData") + } + g.ctx.BufferData(gl.Enum(target), data, gl.Enum(usage)) + } +} + +func (g *GomobileContext) BufferSubData(target uint32, offset int, data []byte) { + g.ctx.BufferSubData(gl.Enum(target), offset, data) +} + +func (g *GomobileContext) CheckFramebufferStatus(target uint32) uint32 { + return uint32(g.ctx.CheckFramebufferStatus(gl.Enum(target))) +} + +func (g *GomobileContext) CompileShader(shader uint32) { + g.ctx.CompileShader(gl.Shader{Value: shader}) +} + +func (g *GomobileContext) CreateProgram() uint32 { + return g.ctx.CreateProgram().Value +} + +func (g *GomobileContext) CreateShader(xtype uint32) uint32 { + return g.ctx.CreateShader(gl.Enum(xtype)).Value +} + +func (g *GomobileContext) DeleteBuffers(buffers []uint32) { + for _, b := range buffers { + g.ctx.DeleteBuffer(gl.Buffer{Value: b}) + } +} + +func (g *GomobileContext) DeleteFramebuffers(framebuffers []uint32) { + for _, b := range framebuffers { + g.ctx.DeleteFramebuffer(gl.Framebuffer{Value: b}) + } +} + +func (g *GomobileContext) DeleteProgram(program uint32) { + g.ctx.DeleteProgram(gmProgram(program)) +} + +func (g *GomobileContext) DeleteShader(shader uint32) { + g.ctx.DeleteShader(gl.Shader{Value: shader}) +} + +func (g *GomobileContext) DeleteTextures(textures []uint32) { + for _, t := range textures { + g.ctx.DeleteTexture(gl.Texture{Value: t}) + } +} + +func (g *GomobileContext) DisableVertexAttribArray(index uint32) { + g.ctx.DisableVertexAttribArray(gl.Attrib{Value: uint(index)}) +} + +func (g *GomobileContext) DrawElements(mode uint32, count int32, xtype uint32, offset int) { + g.ctx.DrawElements(gl.Enum(mode), int(count), gl.Enum(xtype), offset) +} + +func (g *GomobileContext) Enable(cap uint32) { + g.ctx.Enable(gl.Enum(gl.Enum(cap))) +} + +func (g *GomobileContext) EnableVertexAttribArray(index uint32) { + g.ctx.EnableVertexAttribArray(gl.Attrib{Value: uint(index)}) +} + +func (g *GomobileContext) Flush() { + g.ctx.Flush() +} + +func (g *GomobileContext) FramebufferTexture2D(target uint32, attachment uint32, textarget uint32, texture uint32, level int32) { + g.ctx.FramebufferTexture2D(gl.Enum(target), gl.Enum(attachment), gl.Enum(textarget), gl.Texture{Value: texture}, int(level)) +} + +func (g *GomobileContext) GenBuffers(n int32) []uint32 { + buffers := make([]uint32, n) + for i := range buffers { + buffers[i] = g.ctx.CreateBuffer().Value + } + return buffers +} + +func (g *GomobileContext) GenFramebuffers(n int32) []uint32 { + framebuffers := make([]uint32, n) + for i := range framebuffers { + framebuffers[i] = g.ctx.CreateFramebuffer().Value + } + return framebuffers +} + +func (g *GomobileContext) GenTextures(n int32) []uint32 { + textures := make([]uint32, n) + for i := range textures { + textures[i] = g.ctx.CreateTexture().Value + } + return textures +} + +func (g *GomobileContext) GetError() uint32 { + return uint32(g.ctx.GetError()) +} + +func (g *GomobileContext) GetIntegerv(dst []int32, pname uint32) { + g.ctx.GetIntegerv(dst, gl.Enum(pname)) +} + +func (g *GomobileContext) GetProgramiv(dst []int32, program uint32, pname uint32) { + dst[0] = int32(g.ctx.GetProgrami(gmProgram(program), gl.Enum(pname))) +} + +func (g *GomobileContext) GetProgramInfoLog(program uint32) string { + return g.ctx.GetProgramInfoLog(gmProgram(program)) +} + +func (g *GomobileContext) GetShaderiv(dst []int32, shader uint32, pname uint32) { + dst[0] = int32(g.ctx.GetShaderi(gl.Shader{Value: shader}, gl.Enum(pname))) +} + +func (g *GomobileContext) GetShaderInfoLog(shader uint32) string { + return g.ctx.GetShaderInfoLog(gl.Shader{Value: shader}) +} + +func (g *GomobileContext) GetShaderPrecisionFormat(shadertype uint32, precisiontype uint32) (rangeLow, rangeHigh, precision int) { + return g.ctx.GetShaderPrecisionFormat(gl.Enum(shadertype), gl.Enum(precisiontype)) +} + +func (g *GomobileContext) GetUniformLocation(program uint32, name string) int32 { + return g.ctx.GetUniformLocation(gmProgram(program), name).Value +} + +func (g *GomobileContext) IsFramebuffer(framebuffer uint32) bool { + return g.ctx.IsFramebuffer(gl.Framebuffer{Value: framebuffer}) +} + +func (g *GomobileContext) IsProgram(program uint32) bool { + return g.ctx.IsProgram(gmProgram(program)) +} + +func (g *GomobileContext) IsTexture(texture uint32) bool { + return g.ctx.IsTexture(gl.Texture{Value: texture}) +} + +func (g *GomobileContext) LinkProgram(program uint32) { + g.ctx.LinkProgram(gmProgram(program)) +} + +func (g *GomobileContext) PixelStorei(pname uint32, param int32) { + g.ctx.PixelStorei(gl.Enum(pname), param) +} + +func (g *GomobileContext) ReadPixels(dst []byte, x int32, y int32, width int32, height int32, format uint32, xtype uint32) { + g.ctx.ReadPixels(dst, int(x), int(y), int(width), int(height), gl.Enum(format), gl.Enum(xtype)) +} + +func (g *GomobileContext) ShaderSource(shader uint32, xstring string) { + g.ctx.ShaderSource(gl.Shader{Value: shader}, xstring) +} + +func (g *GomobileContext) TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) { + g.ctx.TexImage2D(gl.Enum(target), int(level), int(internalformat), int(width), int(height), gl.Enum(format), gl.Enum(xtype), pixels) +} + +func (g *GomobileContext) TexParameteri(target uint32, pname uint32, param int32) { + g.ctx.TexParameteri(gl.Enum(target), gl.Enum(pname), int(param)) +} + +func (g *GomobileContext) TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) { + g.ctx.TexSubImage2D(gl.Enum(target), int(level), int(xoffset), int(yoffset), int(width), int(height), gl.Enum(format), gl.Enum(xtype), pixels) +} + +func (g *GomobileContext) Uniform1f(location int32, v0 float32) { + g.ctx.Uniform1f(gl.Uniform{Value: location}, v0) +} + +func (g *GomobileContext) Uniform1fv(location int32, value []float32) { + g.ctx.Uniform1fv(gl.Uniform{Value: location}, value) +} + +func (g *GomobileContext) Uniform1i(location int32, v0 int32) { + g.ctx.Uniform1i(gl.Uniform{Value: location}, int(v0)) +} + +func (g *GomobileContext) Uniform2fv(location int32, value []float32) { + g.ctx.Uniform2fv(gl.Uniform{Value: location}, value) +} + +func (g *GomobileContext) Uniform3fv(location int32, value []float32) { + g.ctx.Uniform3fv(gl.Uniform{Value: location}, value) +} + +func (g *GomobileContext) Uniform4fv(location int32, value []float32) { + g.ctx.Uniform4fv(gl.Uniform{Value: location}, value) +} + +func (g *GomobileContext) UniformMatrix2fv(location int32, transpose bool, value []float32) { + if transpose { + panic("gles: UniformMatrix2fv with transpose is not implemented") + } + g.ctx.UniformMatrix2fv(gl.Uniform{Value: location}, value) +} + +func (g *GomobileContext) UniformMatrix3fv(location int32, transpose bool, value []float32) { + if transpose { + panic("gles: UniformMatrix3fv with transpose is not implemented") + } + g.ctx.UniformMatrix3fv(gl.Uniform{Value: location}, value) +} + +func (g *GomobileContext) UniformMatrix4fv(location int32, transpose bool, value []float32) { + if transpose { + panic("gles: UniformMatrix4fv with transpose is not implemented") + } + g.ctx.UniformMatrix4fv(gl.Uniform{Value: location}, value) +} + +func (g *GomobileContext) UseProgram(program uint32) { + g.ctx.UseProgram(gmProgram(program)) +} + +func (g *GomobileContext) VertexAttribPointer(index uint32, size int32, xtype uint32, normalized bool, stride int32, offset int) { + g.ctx.VertexAttribPointer(gl.Attrib{Value: uint(index)}, int(size), gl.Enum(xtype), normalized, int(stride), int(offset)) +} + +func (g *GomobileContext) Viewport(x int32, y int32, width int32, height int32) { + g.ctx.Viewport(int(x), int(y), int(width), int(height)) +} diff --git a/internal/graphicsdriver/opengl/gles/interface.go b/internal/graphicsdriver/opengl/gles/interface.go new file mode 100644 index 000000000..5572fdfbe --- /dev/null +++ b/internal/graphicsdriver/opengl/gles/interface.go @@ -0,0 +1,77 @@ +// Copyright 2020 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 android ios + +package gles + +type Context interface { + ActiveTexture(texture uint32) + AttachShader(program uint32, shader uint32) + BindAttribLocation(program uint32, index uint32, name string) + BindBuffer(target uint32, buffer uint32) + BindFramebuffer(target uint32, framebuffer uint32) + BindTexture(target uint32, texture uint32) + BlendFunc(sfactor uint32, dfactor uint32) + BufferData(target uint32, size int, data []byte, usage uint32) + BufferSubData(target uint32, offset int, data []byte) + CheckFramebufferStatus(target uint32) uint32 + CompileShader(shader uint32) + CreateProgram() uint32 + CreateShader(xtype uint32) uint32 + DeleteBuffers(buffers []uint32) + DeleteFramebuffers(framebuffers []uint32) + DeleteProgram(program uint32) + DeleteShader(shader uint32) + DeleteTextures(textures []uint32) + DisableVertexAttribArray(index uint32) + DrawElements(mode uint32, count int32, xtype uint32, offset int) + Enable(cap uint32) + EnableVertexAttribArray(index uint32) + Flush() + FramebufferTexture2D(target uint32, attachment uint32, textarget uint32, texture uint32, level int32) + GenBuffers(n int32) []uint32 + GenFramebuffers(n int32) []uint32 + GenTextures(n int32) []uint32 + GetError() uint32 + GetIntegerv(dst []int32, pname uint32) + GetProgramiv(dst []int32, program uint32, pname uint32) + GetProgramInfoLog(program uint32) string + GetShaderiv(dst []int32, shader uint32, pname uint32) + GetShaderInfoLog(shader uint32) string + GetShaderPrecisionFormat(shadertype uint32, precisiontype uint32) (rangeLow, rangeHigh, precision int) + GetUniformLocation(program uint32, name string) int32 + IsFramebuffer(framebuffer uint32) bool + IsProgram(program uint32) bool + IsTexture(texture uint32) bool + LinkProgram(program uint32) + PixelStorei(pname uint32, param int32) + ReadPixels(dst []byte, x int32, y int32, width int32, height int32, format uint32, xtype uint32) + ShaderSource(shader uint32, xstring string) + TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) + TexParameteri(target uint32, pname uint32, param int32) + TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) + Uniform1f(location int32, v0 float32) + Uniform1fv(location int32, value []float32) + Uniform1i(location int32, v0 int32) + Uniform2fv(location int32, value []float32) + Uniform3fv(location int32, value []float32) + Uniform4fv(location int32, value []float32) + UniformMatrix2fv(location int32, transpose bool, value []float32) + UniformMatrix3fv(location int32, transpose bool, value []float32) + UniformMatrix4fv(location int32, transpose bool, value []float32) + UseProgram(program uint32) + VertexAttribPointer(index uint32, size int32, xtype uint32, normalized bool, stride int32, offset int) + Viewport(x int32, y int32, width int32, height int32) +} diff --git a/internal/graphicsdriver/opengl/gles/str.go b/internal/graphicsdriver/opengl/gles/str.go new file mode 100644 index 000000000..dad01924c --- /dev/null +++ b/internal/graphicsdriver/opengl/gles/str.go @@ -0,0 +1,39 @@ +// Copyright 2020 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 android ios + +package gles + +// #include +import "C" + +import ( + "unsafe" +) + +func cString(str string) (uintptr, func()) { + ptr := C.CString(str) + return uintptr(unsafe.Pointer(ptr)), func() { C.free(unsafe.Pointer(ptr)) } +} + +func cStringPtr(str string) (uintptr, func()) { + s, free := cString(str) + ptr := C.malloc(C.size_t(unsafe.Sizeof((*int)(nil)))) + *(*uintptr)(ptr) = s + return uintptr(ptr), func() { + free() + C.free(ptr) + } +} diff --git a/internal/graphicsdriver/opengl/graphics_mobile.go b/internal/graphicsdriver/opengl/graphics_mobile.go index afa5f1f4e..fa2e4464b 100644 --- a/internal/graphicsdriver/opengl/graphics_mobile.go +++ b/internal/graphicsdriver/opengl/graphics_mobile.go @@ -18,8 +18,14 @@ package opengl import ( "golang.org/x/mobile/gl" + + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gles" ) -func (g *Graphics) SetMobileGLContext(context gl.Context) { - g.context.gl = context +func init() { + theGraphics.context.ctx = gles.DefaultContext{} +} + +func (g *Graphics) SetGomobileGLContext(context gl.Context) { + g.context.ctx = gles.NewGomobileContext(context) } diff --git a/internal/uidriver/mobile/ui.go b/internal/uidriver/mobile/ui.go index 9d5351716..ef7b2698b 100644 --- a/internal/uidriver/mobile/ui.go +++ b/internal/uidriver/mobile/ui.go @@ -21,7 +21,6 @@ import ( "runtime/debug" "sync" "sync/atomic" - "time" "unicode" "golang.org/x/mobile/app" @@ -84,45 +83,6 @@ func (u *UserInterface) Update() error { } renderCh <- struct{}{} - if u.Graphics().IsGL() { - if u.glWorker == nil { - panic("mobile: glWorker must be initialized but not") - } - - workAvailable := u.glWorker.WorkAvailable() - for { - // When the two channels don't receive for a while, call DoWork forcibly to avoid freeze - // (#1322, #1332). - // - // In theory, this timeout should not be necessary. However, it looks like this 'select' - // statement sometimes blocks forever on some Android devices like Pixel 4(a). Apparently - // workAvailable sometimes not receives even though there are queued OpenGL functions. - // Call DoWork for such case as a symptomatic treatment. - // - // Calling DoWork without waiting for workAvailable is safe. If there are no tasks, DoWork - // should return immediately. - // - // TODO: Fix the root cause. Note that this is pretty hard since e.g., logging affects the - // scheduling and freezing might not happen with logging. - t := time.NewTimer(100 * time.Millisecond) - - select { - case <-workAvailable: - if !t.Stop() { - <-t.C - } - u.glWorker.DoWork() - case <-renderEndCh: - if !t.Stop() { - <-t.C - } - return nil - case <-t.C: - u.glWorker.DoWork() - } - } - } - go func() { <-renderEndCh u.t.Call(func() error { @@ -151,8 +111,7 @@ type UserInterface struct { input Input - t *thread.Thread - glWorker gl.Worker + t *thread.Thread m sync.RWMutex } @@ -306,14 +265,11 @@ func (u *UserInterface) run(context driver.UIContext, mainloop bool) (err error) u.context = context - if u.Graphics().IsGL() { - var ctx gl.Context - if mainloop { - ctx = <-glContextCh - } else { - ctx, u.glWorker = gl.NewContext() - } - u.Graphics().(*opengl.Graphics).SetMobileGLContext(ctx) + if mainloop { + // When mainloop is true, gomobile-build is used. In this case, GL functions must be called via + // gl.Context so that they are called on the appropriate thread. + ctx := <-glContextCh + u.Graphics().(*opengl.Graphics).SetGomobileGLContext(ctx) } else { u.t = thread.New() graphicscommand.SetMainThread(u.t)