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
This commit is contained in:
Hajime Hoshi 2020-10-18 18:28:23 +09:00
parent 2740938460
commit f611b48c71
7 changed files with 851 additions and 196 deletions

View File

@ -20,20 +20,17 @@ import (
"errors" "errors"
"fmt" "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/driver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gles" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gles"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/shaderir"
) )
type ( type (
textureNative mgl.Texture textureNative uint32
framebufferNative mgl.Framebuffer framebufferNative uint32
shader mgl.Shader shader uint32
program mgl.Program program uint32
buffer mgl.Buffer buffer uint32
) )
func (t textureNative) equal(rhs textureNative) bool { func (t textureNative) equal(rhs textureNative) bool {
@ -59,8 +56,8 @@ func (p program) equal(rhs program) bool {
var InvalidTexture textureNative var InvalidTexture textureNative
type ( type (
uniformLocation mgl.Uniform uniformLocation int32
attribLocation mgl.Attrib attribLocation int32
) )
func (u uniformLocation) equal(rhs uniformLocation) bool { func (u uniformLocation) equal(rhs uniformLocation) bool {
@ -69,14 +66,14 @@ func (u uniformLocation) equal(rhs uniformLocation) bool {
type programID uint32 type programID uint32
var ( const (
invalidTexture = textureNative(mgl.Texture{}) invalidTexture = 0
invalidFramebuffer = framebufferNative(mgl.Framebuffer{Value: (1 << 32) - 1}) invalidFramebuffer = (1 << 32) - 1
invalidUniform = uniformLocation(mgl.Uniform{Value: -1}) invalidUniform = -1
) )
func getProgramID(p program) programID { func getProgramID(p program) programID {
return programID(p.Value) return programID(p)
} }
const ( const (
@ -98,7 +95,7 @@ const (
) )
type contextImpl struct { type contextImpl struct {
gl mgl.Context ctx gles.Context
} }
func (c *context) reset() error { func (c *context) reset() error {
@ -108,115 +105,105 @@ func (c *context) reset() error {
c.lastViewportWidth = 0 c.lastViewportWidth = 0
c.lastViewportHeight = 0 c.lastViewportHeight = 0
c.lastCompositeMode = driver.CompositeModeUnknown c.lastCompositeMode = driver.CompositeModeUnknown
c.gl.Enable(gles.BLEND) c.ctx.Enable(gles.BLEND)
c.blendFunc(driver.CompositeModeSourceOver) c.blendFunc(driver.CompositeModeSourceOver)
f := c.gl.GetInteger(gles.FRAMEBUFFER_BINDING) f := make([]int32, 1)
c.screenFramebuffer = framebufferNative(mgl.Framebuffer{uint32(f)}) c.ctx.GetIntegerv(f, gles.FRAMEBUFFER_BINDING)
c.screenFramebuffer = framebufferNative(f[0])
// TODO: Need to update screenFramebufferWidth/Height? // TODO: Need to update screenFramebufferWidth/Height?
return nil return nil
} }
func (c *context) blendFunc(mode driver.CompositeMode) { func (c *context) blendFunc(mode driver.CompositeMode) {
gl := c.gl
if c.lastCompositeMode == mode { if c.lastCompositeMode == mode {
return return
} }
c.lastCompositeMode = mode c.lastCompositeMode = mode
s, d := mode.Operations() s, d := mode.Operations()
s2, d2 := convertOperation(s), convertOperation(d) 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) { func (c *context) newTexture(width, height int) (textureNative, error) {
gl := c.gl t := c.ctx.GenTextures(1)[0]
t := gl.CreateTexture() if t <= 0 {
if t.Value <= 0 { return 0, errors.New("opengl: creating texture failed")
return textureNative{}, errors.New("opengl: creating texture failed")
} }
gl.PixelStorei(gles.UNPACK_ALIGNMENT, 4) c.ctx.PixelStorei(gles.UNPACK_ALIGNMENT, 4)
c.bindTexture(textureNative(t)) c.bindTexture(textureNative(t))
gl.TexParameteri(gles.TEXTURE_2D, gles.TEXTURE_MAG_FILTER, gles.NEAREST) c.ctx.TexParameteri(gles.TEXTURE_2D, gles.TEXTURE_MAG_FILTER, gles.NEAREST)
gl.TexParameteri(gles.TEXTURE_2D, gles.TEXTURE_MIN_FILTER, gles.NEAREST) c.ctx.TexParameteri(gles.TEXTURE_2D, gles.TEXTURE_MIN_FILTER, gles.NEAREST)
gl.TexParameteri(gles.TEXTURE_2D, gles.TEXTURE_WRAP_S, gles.CLAMP_TO_EDGE) c.ctx.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) c.ctx.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.TexImage2D(gles.TEXTURE_2D, 0, gles.RGBA, int32(width), int32(height), gles.RGBA, gles.UNSIGNED_BYTE, nil)
return textureNative(t), nil return textureNative(t), nil
} }
func (c *context) bindFramebufferImpl(f framebufferNative) { func (c *context) bindFramebufferImpl(f framebufferNative) {
gl := c.gl c.ctx.BindFramebuffer(gles.FRAMEBUFFER, uint32(f))
gl.BindFramebuffer(gles.FRAMEBUFFER, mgl.Framebuffer(f))
} }
func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte { func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte {
gl := c.gl c.ctx.Flush()
gl.Flush()
c.bindFramebuffer(f.native) c.bindFramebuffer(f.native)
pixels := make([]byte, 4*width*height) 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 return pixels
} }
func (c *context) activeTexture(idx int) { func (c *context) activeTexture(idx int) {
gl := c.gl c.ctx.ActiveTexture(uint32(gles.TEXTURE0 + idx))
gl.ActiveTexture(mgl.Enum(gles.TEXTURE0 + idx))
} }
func (c *context) bindTextureImpl(t textureNative) { func (c *context) bindTextureImpl(t textureNative) {
gl := c.gl c.ctx.BindTexture(gles.TEXTURE_2D, uint32(t))
gl.BindTexture(gles.TEXTURE_2D, mgl.Texture(t))
} }
func (c *context) deleteTexture(t textureNative) { func (c *context) deleteTexture(t textureNative) {
gl := c.gl if !c.ctx.IsTexture(uint32(t)) {
if !gl.IsTexture(mgl.Texture(t)) {
return return
} }
if c.lastTexture == t { if c.lastTexture == t {
c.lastTexture = invalidTexture c.lastTexture = invalidTexture
} }
gl.DeleteTexture(mgl.Texture(t)) c.ctx.DeleteTextures([]uint32{uint32(t)})
} }
func (c *context) isTexture(t textureNative) bool { func (c *context) isTexture(t textureNative) bool {
gl := c.gl return c.ctx.IsTexture(uint32(t))
return gl.IsTexture(mgl.Texture(t))
} }
func (c *context) newFramebuffer(texture textureNative) (framebufferNative, error) { func (c *context) newFramebuffer(texture textureNative) (framebufferNative, error) {
gl := c.gl f := c.ctx.GenFramebuffers(1)[0]
f := gl.CreateFramebuffer() if f <= 0 {
if f.Value <= 0 { return 0, fmt.Errorf("opengl: creating framebuffer failed: the returned value is not positive but %d", f)
return framebufferNative{}, fmt.Errorf("opengl: creating framebuffer failed: the returned value is not positive but %d", f.Value)
} }
c.bindFramebuffer(framebufferNative(f)) c.bindFramebuffer(framebufferNative(f))
gl.FramebufferTexture2D(gles.FRAMEBUFFER, gles.COLOR_ATTACHMENT0, gles.TEXTURE_2D, mgl.Texture(texture), 0) c.ctx.FramebufferTexture2D(gles.FRAMEBUFFER, gles.COLOR_ATTACHMENT0, gles.TEXTURE_2D, uint32(texture), 0)
s := gl.CheckFramebufferStatus(gles.FRAMEBUFFER) s := c.ctx.CheckFramebufferStatus(gles.FRAMEBUFFER)
if s != gles.FRAMEBUFFER_COMPLETE { if s != gles.FRAMEBUFFER_COMPLETE {
if s != 0 { 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 { if e := c.ctx.GetError(); e != gles.NO_ERROR {
return framebufferNative{}, fmt.Errorf("opengl: creating framebuffer failed: (glGetError) %d", e) 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 return framebufferNative(f), nil
} }
func (c *context) setViewportImpl(width, height int) { func (c *context) setViewportImpl(width, height int) {
gl := c.gl c.ctx.Viewport(0, 0, int32(width), int32(height))
gl.Viewport(0, 0, width, height)
} }
func (c *context) deleteFramebuffer(f framebufferNative) { func (c *context) deleteFramebuffer(f framebufferNative) {
gl := c.gl if !c.ctx.IsFramebuffer(uint32(f)) {
if !gl.IsFramebuffer(mgl.Framebuffer(f)) {
return return
} }
// If a framebuffer to be deleted is bound, a newly bound framebuffer // 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.lastViewportWidth = 0
c.lastViewportHeight = 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) { func (c *context) newShader(shaderType shaderType, source string) (shader, error) {
gl := c.gl s := c.ctx.CreateShader(uint32(shaderType))
s := gl.CreateShader(mgl.Enum(shaderType)) if s == 0 {
if s.Value == 0 { return 0, fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType)
return shader{}, fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType)
} }
gl.ShaderSource(s, source) c.ctx.ShaderSource(s, source)
gl.CompileShader(s) c.ctx.CompileShader(s)
v := gl.GetShaderi(s, gles.COMPILE_STATUS) v := make([]int32, 1)
if v == gles.FALSE { c.ctx.GetShaderiv(v, s, gles.COMPILE_STATUS)
log := gl.GetShaderInfoLog(s) if v[0] == gles.FALSE {
return shader{}, fmt.Errorf("opengl: shader compile failed: %s", log) log := c.ctx.GetShaderInfoLog(s)
return 0, fmt.Errorf("opengl: shader compile failed: %s", log)
} }
return shader(s), nil return shader(s), nil
} }
func (c *context) deleteShader(s shader) { func (c *context) deleteShader(s shader) {
gl := c.gl c.ctx.DeleteShader(uint32(s))
gl.DeleteShader(mgl.Shader(s))
} }
func (c *context) newProgram(shaders []shader, attributes []string) (program, error) { func (c *context) newProgram(shaders []shader, attributes []string) (program, error) {
gl := c.gl p := c.ctx.CreateProgram()
p := gl.CreateProgram() if p == 0 {
if p.Value == 0 { return 0, errors.New("opengl: glCreateProgram failed")
return program{}, errors.New("opengl: glCreateProgram failed")
} }
for _, shader := range shaders { for _, shader := range shaders {
gl.AttachShader(p, mgl.Shader(shader)) c.ctx.AttachShader(p, uint32(shader))
} }
for i, name := range attributes { for i, name := range attributes {
gl.BindAttribLocation(p, mgl.Attrib{Value: uint(i)}, name) c.ctx.BindAttribLocation(p, uint32(i), name)
} }
gl.LinkProgram(p) c.ctx.LinkProgram(p)
v := gl.GetProgrami(p, gles.LINK_STATUS) v := make([]int32, 1)
if v == gles.FALSE { c.ctx.GetProgramiv(v, p, gles.LINK_STATUS)
info := gl.GetProgramInfoLog(p) if v[0] == gles.FALSE {
return program{}, fmt.Errorf("opengl: program error: %s", info) info := c.ctx.GetProgramInfoLog(p)
return 0, fmt.Errorf("opengl: program error: %s", info)
} }
return program(p), nil return program(p), nil
} }
func (c *context) useProgram(p program) { func (c *context) useProgram(p program) {
gl := c.gl c.ctx.UseProgram(uint32(p))
gl.UseProgram(mgl.Program(p))
} }
func (c *context) deleteProgram(p program) { func (c *context) deleteProgram(p program) {
gl := c.gl if !c.ctx.IsProgram(uint32(p)) {
if !gl.IsProgram(mgl.Program(p)) {
return return
} }
gl.DeleteProgram(mgl.Program(p)) c.ctx.DeleteProgram(uint32(p))
} }
func (c *context) getUniformLocationImpl(p program, location string) uniformLocation { func (c *context) getUniformLocationImpl(p program, location string) uniformLocation {
gl := c.gl u := uniformLocation(c.ctx.GetUniformLocation(uint32(p), location))
u := uniformLocation(gl.GetUniformLocation(mgl.Program(p), location))
return u return u
} }
func (c *context) uniformInt(p program, location string, v int) bool { func (c *context) uniformInt(p program, location string, v int) bool {
gl := c.gl
l := c.locationCache.GetUniformLocation(c, p, location) l := c.locationCache.GetUniformLocation(c, p, location)
if l == invalidUniform { if l == invalidUniform {
return false return false
} }
gl.Uniform1i(mgl.Uniform(l), v) c.ctx.Uniform1i(int32(l), int32(v))
return true return true
} }
func (c *context) uniformFloat(p program, location string, v float32) bool { func (c *context) uniformFloat(p program, location string, v float32) bool {
gl := c.gl
l := c.locationCache.GetUniformLocation(c, p, location) l := c.locationCache.GetUniformLocation(c, p, location)
if l == invalidUniform { if l == invalidUniform {
return false return false
} }
gl.Uniform1f(mgl.Uniform(l), v) c.ctx.Uniform1f(int32(l), v)
return true return true
} }
func (c *context) uniformFloats(p program, location string, v []float32, typ shaderir.Type) bool { func (c *context) uniformFloats(p program, location string, v []float32, typ shaderir.Type) bool {
gl := c.gl
l := c.locationCache.GetUniformLocation(c, p, location) l := c.locationCache.GetUniformLocation(c, p, location)
if l == invalidUniform { if l == invalidUniform {
return false return false
@ -329,19 +309,19 @@ func (c *context) uniformFloats(p program, location string, v []float32, typ sha
switch base { switch base {
case shaderir.Float: case shaderir.Float:
gl.Uniform1fv(mgl.Uniform(l), v) c.ctx.Uniform1fv(int32(l), v)
case shaderir.Vec2: case shaderir.Vec2:
gl.Uniform2fv(mgl.Uniform(l), v) c.ctx.Uniform2fv(int32(l), v)
case shaderir.Vec3: case shaderir.Vec3:
gl.Uniform3fv(mgl.Uniform(l), v) c.ctx.Uniform3fv(int32(l), v)
case shaderir.Vec4: case shaderir.Vec4:
gl.Uniform4fv(mgl.Uniform(l), v) c.ctx.Uniform4fv(int32(l), v)
case shaderir.Mat2: case shaderir.Mat2:
gl.UniformMatrix2fv(mgl.Uniform(l), v) c.ctx.UniformMatrix2fv(int32(l), false, v)
case shaderir.Mat3: case shaderir.Mat3:
gl.UniformMatrix3fv(mgl.Uniform(l), v) c.ctx.UniformMatrix3fv(int32(l), false, v)
case shaderir.Mat4: case shaderir.Mat4:
gl.UniformMatrix4fv(mgl.Uniform(l), v) c.ctx.UniformMatrix4fv(int32(l), false, v)
default: default:
panic(fmt.Sprintf("opengl: unexpected type: %s", typ.String())) 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) { func (c *context) vertexAttribPointer(p program, index int, size int, dataType dataType, stride int, offset int) {
gl := c.gl c.ctx.VertexAttribPointer(uint32(index), int32(size), uint32(dataType), false, int32(stride), offset)
gl.VertexAttribPointer(mgl.Attrib{Value: uint(index)}, size, mgl.Enum(dataType), false, stride, offset)
} }
func (c *context) enableVertexAttribArray(p program, index int) { func (c *context) enableVertexAttribArray(p program, index int) {
gl := c.gl c.ctx.EnableVertexAttribArray(uint32(index))
gl.EnableVertexAttribArray(mgl.Attrib{Value: uint(index)})
} }
func (c *context) disableVertexAttribArray(p program, index int) { func (c *context) disableVertexAttribArray(p program, index int) {
gl := c.gl c.ctx.DisableVertexAttribArray(uint32(index))
gl.DisableVertexAttribArray(mgl.Attrib{Value: uint(index)})
} }
func (c *context) newArrayBuffer(size int) buffer { func (c *context) newArrayBuffer(size int) buffer {
gl := c.gl b := c.ctx.GenBuffers(1)[0]
b := gl.CreateBuffer() c.ctx.BindBuffer(uint32(arrayBuffer), b)
gl.BindBuffer(mgl.Enum(arrayBuffer), b) c.ctx.BufferData(uint32(arrayBuffer), size, nil, uint32(dynamicDraw))
gl.BufferInit(mgl.Enum(arrayBuffer), size, mgl.Enum(dynamicDraw))
return buffer(b) return buffer(b)
} }
func (c *context) newElementArrayBuffer(size int) buffer { func (c *context) newElementArrayBuffer(size int) buffer {
gl := c.gl b := c.ctx.GenBuffers(1)[0]
b := gl.CreateBuffer() c.ctx.BindBuffer(uint32(elementArrayBuffer), b)
gl.BindBuffer(mgl.Enum(elementArrayBuffer), b) c.ctx.BufferData(uint32(elementArrayBuffer), size, nil, uint32(dynamicDraw))
gl.BufferInit(mgl.Enum(elementArrayBuffer), size, mgl.Enum(dynamicDraw))
return buffer(b) return buffer(b)
} }
func (c *context) bindBuffer(bufferType bufferType, b buffer) { func (c *context) bindBuffer(bufferType bufferType, b buffer) {
gl := c.gl c.ctx.BindBuffer(uint32(bufferType), uint32(b))
gl.BindBuffer(mgl.Enum(bufferType), mgl.Buffer(b))
} }
func (c *context) arrayBufferSubData(data []float32) { func (c *context) arrayBufferSubData(data []float32) {
gl := c.gl c.ctx.BufferSubData(uint32(arrayBuffer), 0, float32sToBytes(data))
gl.BufferSubData(mgl.Enum(arrayBuffer), 0, float32sToBytes(data))
} }
func (c *context) elementArrayBufferSubData(data []uint16) { func (c *context) elementArrayBufferSubData(data []uint16) {
gl := c.gl c.ctx.BufferSubData(uint32(elementArrayBuffer), 0, uint16sToBytes(data))
gl.BufferSubData(mgl.Enum(elementArrayBuffer), 0, uint16sToBytes(data))
} }
func (c *context) deleteBuffer(b buffer) { func (c *context) deleteBuffer(b buffer) {
gl := c.gl c.ctx.DeleteBuffers([]uint32{uint32(b)})
gl.DeleteBuffer(mgl.Buffer(b))
} }
func (c *context) drawElements(len int, offsetInBytes int) { func (c *context) drawElements(len int, offsetInBytes int) {
gl := c.gl c.ctx.DrawElements(gles.TRIANGLES, int32(len), gles.UNSIGNED_SHORT, offsetInBytes)
gl.DrawElements(gles.TRIANGLES, len, gles.UNSIGNED_SHORT, offsetInBytes)
} }
func (c *context) maxTextureSizeImpl() int { func (c *context) maxTextureSizeImpl() int {
gl := c.gl v := make([]int32, 1)
return gl.GetInteger(gles.MAX_TEXTURE_SIZE) c.ctx.GetIntegerv(v, gles.MAX_TEXTURE_SIZE)
return int(v[0])
} }
func (c *context) getShaderPrecisionFormatPrecision() int { func (c *context) getShaderPrecisionFormatPrecision() int {
gl := c.gl _, _, p := c.ctx.GetShaderPrecisionFormat(gles.FRAGMENT_SHADER, gles.HIGH_FLOAT)
_, _, p := gl.GetShaderPrecisionFormat(gles.FRAGMENT_SHADER, gles.HIGH_FLOAT)
return p return p
} }
func (c *context) flush() { func (c *context) flush() {
gl := c.gl c.ctx.Flush()
gl.Flush()
} }
func (c *context) needsRestoring() bool { 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) { func (c *context) texSubImage2D(t textureNative, width, height int, args []*driver.ReplacePixelsArgs) {
c.bindTexture(t) c.bindTexture(t)
gl := c.gl
for _, a := range args { 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 { func (c *context) newPixelBufferObject(width, height int) buffer {
gl := c.gl b := c.ctx.GenBuffers(1)[0]
b := gl.CreateBuffer() c.ctx.BindBuffer(gles.PIXEL_UNPACK_BUFFER, b)
gl.BindBuffer(gles.PIXEL_UNPACK_BUFFER, b) c.ctx.BufferData(gles.PIXEL_UNPACK_BUFFER, 4*width*height, nil, gles.STREAM_DRAW)
gl.BufferInit(gles.PIXEL_UNPACK_BUFFER, 4*width*height, gles.STREAM_DRAW) c.ctx.BindBuffer(gles.PIXEL_UNPACK_BUFFER, 0)
gl.BindBuffer(gles.PIXEL_UNPACK_BUFFER, mgl.Buffer{0})
return buffer(b) 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. // This implementation is not used yet so far. See the comment at canUsePBO.
c.bindTexture(t) c.bindTexture(t)
gl := c.gl c.ctx.BindBuffer(gles.PIXEL_UNPACK_BUFFER, uint32(buffer))
gl.BindBuffer(gles.PIXEL_UNPACK_BUFFER, mgl.Buffer(buffer))
stride := 4 * width stride := 4 * width
for _, a := range args { for _, a := range args {
offset := 4 * (a.Y*width + a.X) offset := 4 * (a.Y*width + a.X)
for j := 0; j < a.Height; j++ { 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) c.ctx.TexSubImage2D(gles.TEXTURE_2D, 0, 0, 0, int32(width), int32(height), gles.RGBA, gles.UNSIGNED_BYTE, nil)
gl.BindBuffer(gles.PIXEL_UNPACK_BUFFER, mgl.Buffer{0}) c.ctx.BindBuffer(gles.PIXEL_UNPACK_BUFFER, 0)
} }

View File

@ -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 <GLES2/gl2.h>
// #endif
//
// #if defined(os_ios)
// #include <OpenGLES/ES2/glext.h>
// #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))
}

View File

@ -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))
}

View File

@ -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)
}

View File

@ -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 <stdlib.h>
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)
}
}

View File

@ -18,8 +18,14 @@ package opengl
import ( import (
"golang.org/x/mobile/gl" "golang.org/x/mobile/gl"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gles"
) )
func (g *Graphics) SetMobileGLContext(context gl.Context) { func init() {
g.context.gl = context theGraphics.context.ctx = gles.DefaultContext{}
}
func (g *Graphics) SetGomobileGLContext(context gl.Context) {
g.context.ctx = gles.NewGomobileContext(context)
} }

View File

@ -21,7 +21,6 @@ import (
"runtime/debug" "runtime/debug"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time"
"unicode" "unicode"
"golang.org/x/mobile/app" "golang.org/x/mobile/app"
@ -84,45 +83,6 @@ func (u *UserInterface) Update() error {
} }
renderCh <- struct{}{} 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() { go func() {
<-renderEndCh <-renderEndCh
u.t.Call(func() error { u.t.Call(func() error {
@ -152,7 +112,6 @@ type UserInterface struct {
input Input input Input
t *thread.Thread t *thread.Thread
glWorker gl.Worker
m sync.RWMutex m sync.RWMutex
} }
@ -306,14 +265,11 @@ func (u *UserInterface) run(context driver.UIContext, mainloop bool) (err error)
u.context = context u.context = context
if u.Graphics().IsGL() {
var ctx gl.Context
if mainloop { if mainloop {
ctx = <-glContextCh // When mainloop is true, gomobile-build is used. In this case, GL functions must be called via
} else { // gl.Context so that they are called on the appropriate thread.
ctx, u.glWorker = gl.NewContext() ctx := <-glContextCh
} u.Graphics().(*opengl.Graphics).SetGomobileGLContext(ctx)
u.Graphics().(*opengl.Graphics).SetMobileGLContext(ctx)
} else { } else {
u.t = thread.New() u.t = thread.New()
graphicscommand.SetMainThread(u.t) graphicscommand.SetMainThread(u.t)