internal/graphicsdriver/opengl: refactoring: move gl_js to gl/context_js.go

This unifies the OpenGL context interface for all the platforms.
This commit is contained in:
Hajime Hoshi 2022-11-16 01:07:16 +09:00
parent f08983e14d
commit 74d9515345
9 changed files with 844 additions and 500 deletions

View File

@ -105,7 +105,7 @@ type context struct {
}
func (c *context) bindTexture(t textureNative) {
if c.lastTexture.equal(t) {
if c.lastTexture == t {
return
}
c.bindTextureImpl(t)
@ -113,7 +113,7 @@ func (c *context) bindTexture(t textureNative) {
}
func (c *context) bindRenderbuffer(r renderbufferNative) {
if c.lastRenderbuffer.equal(r) {
if c.lastRenderbuffer == r {
return
}
c.bindRenderbufferImpl(r)
@ -121,7 +121,7 @@ func (c *context) bindRenderbuffer(r renderbufferNative) {
}
func (c *context) bindFramebuffer(f framebufferNative) {
if c.lastFramebuffer.equal(f) {
if c.lastFramebuffer == f {
return
}
c.bindFramebufferImpl(f)
@ -139,7 +139,7 @@ func (c *context) setViewport(f *framebuffer) {
// glViewport must be called at least at every frame on iOS.
// As the screen framebuffer is the last render target, next SetViewport should be
// the first call at a frame.
if f.native.equal(c.screenFramebuffer) {
if f.native == c.screenFramebuffer {
c.lastViewportWidth = 0
c.lastViewportHeight = 0
} else {

View File

@ -20,62 +20,31 @@ import (
"os"
"strings"
"syscall/js"
"unsafe"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
"github.com/hajimehoshi/ebiten/v2/internal/jsutil"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)
type (
textureNative js.Value
renderbufferNative js.Value
framebufferNative js.Value
shader js.Value
buffer js.Value
uniformLocation js.Value
attribLocation int
programID int
program struct {
value js.Value
id programID
}
textureNative uint32
renderbufferNative uint32
framebufferNative uint32
shader uint32
program uint32
buffer uint32
)
func (t textureNative) equal(rhs textureNative) bool {
return js.Value(t).Equal(js.Value(rhs))
}
type (
uniformLocation int32
attribLocation int32
)
func (r renderbufferNative) equal(rhs renderbufferNative) bool {
return js.Value(r).Equal(js.Value(rhs))
}
func (f framebufferNative) equal(rhs framebufferNative) bool {
return js.Value(f).Equal(js.Value(rhs))
}
func (s shader) equal(rhs shader) bool {
return js.Value(s).Equal(js.Value(rhs))
}
func (b buffer) equal(rhs buffer) bool {
return js.Value(b).Equal(js.Value(rhs))
}
func (u uniformLocation) equal(rhs uniformLocation) bool {
return js.Value(u).Equal(js.Value(rhs))
}
func (p program) equal(rhs program) bool {
return p.value.Equal(rhs.value) && p.id == rhs.id
}
var invalidUniform = uniformLocation(js.Null())
func getProgramID(p program) programID {
return p.id
}
const (
invalidFramebuffer = (1 << 32) - 1
invalidUniform = -1
)
type webGLVersion int
@ -97,10 +66,12 @@ func webGL2MightBeAvailable() bool {
}
type contextImpl struct {
gl *jsGL
canvas js.Value
lastProgramID programID
webGLVersion webGLVersion
ctx gl.Context
canvas js.Value
webGLVersion webGLVersion
fnGetExtension js.Value
fnIsContextLost js.Value
}
func (c *context) usesWebGL2() bool {
@ -110,7 +81,7 @@ func (c *context) usesWebGL2() bool {
func (c *context) initGL() error {
c.webGLVersion = webGLVersionUnknown
var gl js.Value
var glContext js.Value
if doc := js.Global().Get("document"); doc.Truthy() {
canvas := c.canvas
@ -120,36 +91,44 @@ func (c *context) initGL() error {
attr.Set("stencil", true)
if webGL2MightBeAvailable() {
gl = canvas.Call("getContext", "webgl2", attr)
if gl.Truthy() {
glContext = canvas.Call("getContext", "webgl2", attr)
if glContext.Truthy() {
c.webGLVersion = webGLVersion2
}
}
// Even though WebGL2RenderingContext exists, getting a webgl2 context might fail (#1738).
if !gl.Truthy() {
gl = canvas.Call("getContext", "webgl", attr)
if !gl.Truthy() {
gl = canvas.Call("getContext", "experimental-webgl", attr)
if !glContext.Truthy() {
glContext = canvas.Call("getContext", "webgl", attr)
if !glContext.Truthy() {
glContext = canvas.Call("getContext", "experimental-webgl", attr)
}
if gl.Truthy() {
if glContext.Truthy() {
c.webGLVersion = webGLVersion1
}
}
if !gl.Truthy() {
if !glContext.Truthy() {
return fmt.Errorf("opengl: getContext failed")
}
}
c.gl = c.newJSGL(gl)
ctx, err := gl.NewDefaultContext(glContext)
if err != nil {
return err
}
c.ctx = ctx
c.fnGetExtension = glContext.Get("getExtension").Call("bind", glContext)
c.fnIsContextLost = glContext.Get("isContextLost").Call("bind", glContext)
return nil
}
func (c *context) reset() error {
c.locationCache = newLocationCache()
c.lastTexture = textureNative(js.Null())
c.lastFramebuffer = framebufferNative(js.Null())
c.lastTexture = 0
c.lastFramebuffer = invalidFramebuffer
c.lastViewportWidth = 0
c.lastViewportHeight = 0
c.lastBlend = graphicsdriver.Blend{}
@ -158,17 +137,17 @@ func (c *context) reset() error {
return err
}
if c.gl.isContextLost.Invoke().Bool() {
if c.fnIsContextLost.Invoke().Bool() {
return graphicsdriver.GraphicsNotReady
}
c.gl.enable.Invoke(gl.BLEND)
c.gl.enable.Invoke(gl.SCISSOR_TEST)
c.ctx.Enable(gl.BLEND)
c.ctx.Enable(gl.SCISSOR_TEST)
c.blend(graphicsdriver.BlendSourceOver)
f := c.gl.getParameter.Invoke(gl.FRAMEBUFFER_BINDING)
f := c.ctx.GetInteger(gl.FRAMEBUFFER_BINDING)
c.screenFramebuffer = framebufferNative(f)
if !c.usesWebGL2() {
c.gl.getExtension.Invoke("OES_standard_derivatives")
c.fnGetExtension.Invoke("OES_standard_derivatives")
}
return nil
}
@ -178,87 +157,85 @@ func (c *context) blend(blend graphicsdriver.Blend) {
return
}
c.lastBlend = blend
c.gl.blendFuncSeparate.Invoke(
int(convertBlendFactor(blend.BlendFactorSourceRGB)),
int(convertBlendFactor(blend.BlendFactorDestinationRGB)),
int(convertBlendFactor(blend.BlendFactorSourceAlpha)),
int(convertBlendFactor(blend.BlendFactorDestinationAlpha)),
c.ctx.BlendFuncSeparate(
uint32(convertBlendFactor(blend.BlendFactorSourceRGB)),
uint32(convertBlendFactor(blend.BlendFactorDestinationRGB)),
uint32(convertBlendFactor(blend.BlendFactorSourceAlpha)),
uint32(convertBlendFactor(blend.BlendFactorDestinationAlpha)),
)
c.gl.blendEquationSeparate.Invoke(
int(convertBlendOperation(blend.BlendOperationRGB)),
int(convertBlendOperation(blend.BlendOperationAlpha)),
c.ctx.BlendEquationSeparate(
uint32(convertBlendOperation(blend.BlendOperationRGB)),
uint32(convertBlendOperation(blend.BlendOperationAlpha)),
)
}
func (c *context) scissor(x, y, width, height int) {
c.gl.scissor.Invoke(x, y, width, height)
c.ctx.Scissor(int32(x), int32(y), int32(width), int32(height))
}
func (c *context) newTexture(width, height int) (textureNative, error) {
t := c.gl.createTexture.Invoke()
if !t.Truthy() {
return textureNative(js.Null()), errors.New("opengl: createTexture failed")
t := c.ctx.CreateTexture()
if t <= 0 {
return 0, errors.New("opengl: createTexture failed")
}
c.bindTexture(textureNative(t))
c.gl.texParameteri.Invoke(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
c.gl.texParameteri.Invoke(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
c.gl.texParameteri.Invoke(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
c.gl.texParameteri.Invoke(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
c.ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
c.ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
c.ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
c.ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
c.ctx.PixelStorei(gl.UNPACK_ALIGNMENT, 4)
c.gl.pixelStorei.Invoke(gl.UNPACK_ALIGNMENT, 4)
// Firefox warns the usage of textures without specifying pixels (#629)
//
// Error: WebGL warning: drawElements: This operation requires zeroing texture data. This is slow.
//
// In Ebitengine, textures are filled with pixels laster by the filter that ignores destination, so it is fine
// In Ebitengine, textures are filled with pixels later by the filter that ignores destination, so it is fine
// to leave textures as uninitialized here. Rather, extra memory allocating for initialization should be
// avoided.
c.gl.texImage2D.Invoke(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, nil)
c.ctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, nil)
return textureNative(t), nil
}
func (c *context) bindFramebufferImpl(f framebufferNative) {
c.gl.bindFramebuffer.Invoke(gl.FRAMEBUFFER, js.Value(f))
c.ctx.BindFramebuffer(gl.FRAMEBUFFER, uint32(f))
}
func (c *context) framebufferPixels(buf []byte, f *framebuffer, x, y, width, height int) {
c.bindFramebuffer(f.native)
if got, want := len(buf), 4*width*height; got != want {
panic(fmt.Sprintf("opengl: len(buf) must be %d but %d", got, want))
}
p := jsutil.TemporaryUint8ArrayFromUint8Slice(len(buf), nil)
c.gl.readPixels.Invoke(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, p)
js.CopyBytesToGo(buf, p)
c.bindFramebuffer(f.native)
c.ctx.ReadPixels(buf, int32(x), int32(y), int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE)
}
func (c *context) framebufferPixelsToBuffer(f *framebuffer, buffer buffer, width, height int) {
c.bindFramebuffer(f.native)
c.gl.bindBuffer.Invoke(gl.PIXEL_PACK_BUFFER, js.Value(buffer))
c.ctx.BindBuffer(gl.PIXEL_PACK_BUFFER, uint32(buffer))
// void gl.readPixels(x, y, width, height, format, type, GLintptr offset);
c.gl.readPixels.Invoke(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, 0)
c.gl.bindBuffer.Invoke(gl.PIXEL_PACK_BUFFER, nil)
c.ctx.ReadPixels(nil, 0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE)
c.ctx.BindBuffer(gl.PIXEL_PACK_BUFFER, 0)
}
func (c *context) activeTexture(idx int) {
c.gl.activeTexture.Invoke(gl.TEXTURE0 + idx)
c.ctx.ActiveTexture(uint32(gl.TEXTURE0 + idx))
}
func (c *context) bindTextureImpl(t textureNative) {
c.gl.bindTexture.Invoke(gl.TEXTURE_2D, js.Value(t))
c.ctx.BindTexture(gl.TEXTURE_2D, uint32(t))
}
func (c *context) deleteTexture(t textureNative) {
if !c.gl.isTexture.Invoke(js.Value(t)).Bool() {
if !c.ctx.IsTexture(uint32(t)) {
return
}
if c.lastTexture.equal(t) {
c.lastTexture = textureNative(js.Null())
if c.lastTexture == t {
c.lastTexture = 0
}
c.gl.deleteTexture.Invoke(js.Value(t))
c.ctx.DeleteTexture(uint32(t))
}
func (c *context) isTexture(t textureNative) bool {
@ -267,40 +244,45 @@ func (c *context) isTexture(t textureNative) bool {
}
func (c *context) newRenderbuffer(width, height int) (renderbufferNative, error) {
r := c.gl.createRenderbuffer.Invoke()
if !r.Truthy() {
return renderbufferNative(js.Null()), errors.New("opengl: createRenderbuffer failed")
r := c.ctx.CreateRenderbuffer()
if r <= 0 {
return 0, errors.New("opengl: createRenderbuffer failed")
}
c.bindRenderbuffer(renderbufferNative(r))
renderbuffer := renderbufferNative(r)
c.bindRenderbuffer(renderbuffer)
// TODO: Is STENCIL_INDEX8 portable?
// https://stackoverflow.com/questions/11084961/binding-a-stencil-render-buffer-to-a-frame-buffer-in-opengl
c.gl.renderbufferStorage.Invoke(gl.RENDERBUFFER, gl.STENCIL_INDEX8, width, height)
c.ctx.RenderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, int32(width), int32(height))
return renderbufferNative(r), nil
return renderbuffer, nil
}
func (c *context) bindRenderbufferImpl(r renderbufferNative) {
c.gl.bindRenderbuffer.Invoke(gl.RENDERBUFFER, js.Value(r))
c.ctx.BindRenderbuffer(gl.RENDERBUFFER, uint32(r))
}
func (c *context) deleteRenderbuffer(r renderbufferNative) {
if !c.gl.isRenderbuffer.Invoke(js.Value(r)).Bool() {
if !c.ctx.IsRenderbuffer(uint32(r)) {
return
}
if c.lastRenderbuffer.equal(r) {
c.lastRenderbuffer = renderbufferNative(js.Null())
if c.lastRenderbuffer == r {
c.lastRenderbuffer = 0
}
c.gl.deleteRenderbuffer.Invoke(js.Value(r))
c.ctx.DeleteRenderbuffer(uint32(r))
}
func (c *context) newFramebuffer(t textureNative) (framebufferNative, error) {
f := c.gl.createFramebuffer.Invoke()
f := c.ctx.CreateFramebuffer()
if f <= 0 {
return 0, fmt.Errorf("opengl: creating framebuffer failed: the returned value is not positive but %d", f)
}
c.bindFramebuffer(framebufferNative(f))
c.gl.framebufferTexture2D.Invoke(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, js.Value(t), 0)
if s := c.gl.checkFramebufferStatus.Invoke(gl.FRAMEBUFFER); s.Int() != gl.FRAMEBUFFER_COMPLETE {
return framebufferNative(js.Null()), errors.New(fmt.Sprintf("opengl: creating framebuffer failed: %d", s.Int()))
c.ctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, uint32(t), 0)
if s := c.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE {
return 0, errors.New(fmt.Sprintf("opengl: creating framebuffer failed: %d", s))
}
return framebufferNative(f), nil
@ -309,30 +291,30 @@ func (c *context) newFramebuffer(t textureNative) (framebufferNative, error) {
func (c *context) bindStencilBuffer(f framebufferNative, r renderbufferNative) error {
c.bindFramebuffer(f)
c.gl.framebufferRenderbuffer.Invoke(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, js.Value(r))
if s := c.gl.checkFramebufferStatus.Invoke(gl.FRAMEBUFFER); s.Int() != gl.FRAMEBUFFER_COMPLETE {
return errors.New(fmt.Sprintf("opengl: framebufferRenderbuffer failed: %d", s.Int()))
c.ctx.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, uint32(r))
if s := c.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE {
return errors.New(fmt.Sprintf("opengl: framebufferRenderbuffer failed: %d", s))
}
return nil
}
func (c *context) setViewportImpl(width, height int) {
c.gl.viewport.Invoke(0, 0, width, height)
c.ctx.Viewport(0, 0, int32(width), int32(height))
}
func (c *context) deleteFramebuffer(f framebufferNative) {
if !c.gl.isFramebuffer.Invoke(js.Value(f)).Bool() {
if !c.ctx.IsFramebuffer(uint32(f)) {
return
}
// If a framebuffer to be deleted is bound, a newly bound framebuffer
// will be a default framebuffer.
// https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteFramebuffers.xml
if c.lastFramebuffer.equal(f) {
c.lastFramebuffer = framebufferNative(js.Null())
if c.lastFramebuffer == f {
c.lastFramebuffer = invalidFramebuffer
c.lastViewportWidth = 0
c.lastViewportHeight = 0
}
c.gl.deleteFramebuffer.Invoke(js.Value(f))
c.ctx.DeleteFramebuffer(uint32(f))
}
func (c *context) newVertexShader(source string) (shader, error) {
@ -343,83 +325,78 @@ func (c *context) newFragmentShader(source string) (shader, error) {
return c.newShader(gl.FRAGMENT_SHADER, source)
}
func (c *context) newShader(shaderType int, source string) (shader, error) {
s := c.gl.createShader.Invoke(int(shaderType))
if !s.Truthy() {
return shader(js.Null()), fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType)
func (c *context) newShader(shaderType uint32, source string) (shader, error) {
s := c.ctx.CreateShader(shaderType)
if s == 0 {
return 0, fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType)
}
c.gl.shaderSource.Invoke(js.Value(s), source)
c.gl.compileShader.Invoke(js.Value(s))
c.ctx.ShaderSource(s, source)
c.ctx.CompileShader(s)
if !c.gl.getShaderParameter.Invoke(js.Value(s), gl.COMPILE_STATUS).Bool() {
log := c.gl.getShaderInfoLog.Invoke(js.Value(s))
return shader(js.Null()), fmt.Errorf("opengl: shader compile failed: %s", log)
if c.ctx.GetShaderi(s, gl.COMPILE_STATUS) == gl.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) {
c.gl.deleteShader.Invoke(js.Value(s))
c.ctx.DeleteShader(uint32(s))
}
func (c *context) newProgram(shaders []shader, attributes []string) (program, error) {
v := c.gl.createProgram.Invoke()
if !v.Truthy() {
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 {
c.gl.attachShader.Invoke(v, js.Value(shader))
c.ctx.AttachShader(p, uint32(shader))
}
for i, name := range attributes {
c.gl.bindAttribLocation.Invoke(v, i, name)
c.ctx.BindAttribLocation(p, uint32(i), name)
}
c.gl.linkProgram.Invoke(v)
if !c.gl.getProgramParameter.Invoke(v, gl.LINK_STATUS).Bool() {
info := c.gl.getProgramInfoLog.Invoke(v).String()
return program{}, fmt.Errorf("opengl: program error: %s", info)
c.ctx.LinkProgram(p)
if c.ctx.GetProgrami(p, gl.LINK_STATUS) == gl.FALSE {
info := c.ctx.GetProgramInfoLog(p)
return 0, fmt.Errorf("opengl: program error: %s", info)
}
id := c.lastProgramID
c.lastProgramID++
return program{
value: v,
id: id,
}, nil
return program(p), nil
}
func (c *context) useProgram(p program) {
c.gl.useProgram.Invoke(p.value)
c.ctx.UseProgram(uint32(p))
}
func (c *context) deleteProgram(p program) {
c.locationCache.deleteProgram(p)
if !c.gl.isProgram.Invoke(p.value).Bool() {
if !c.ctx.IsProgram(uint32(p)) {
return
}
c.gl.deleteProgram.Invoke(p.value)
c.ctx.DeleteProgram(uint32(p))
}
func (c *context) getUniformLocationImpl(p program, location string) uniformLocation {
return uniformLocation(c.gl.getUniformLocation.Invoke(p.value, location))
return uniformLocation(c.ctx.GetUniformLocation(uint32(p), location))
}
func (c *context) uniformInt(p program, location string, v int) bool {
l := c.locationCache.GetUniformLocation(c, p, location)
if l.equal(invalidUniform) {
if l == invalidUniform {
return false
}
c.gl.uniform1i.Invoke(js.Value(l), v)
c.ctx.Uniform1i(int32(l), int32(v))
return true
}
func (c *context) uniforms(p program, location string, v []uint32, typ shaderir.Type) bool {
l := c.locationCache.GetUniformLocation(c, p, location)
if l.equal(invalidUniform) {
if l == invalidUniform {
return false
}
@ -430,178 +407,115 @@ func (c *context) uniforms(p program, location string, v []uint32, typ shaderir.
switch base {
case shaderir.Float:
arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v))
if c.usesWebGL2() {
c.gl.uniform1fv.Invoke(js.Value(l), arr, 0, len(v))
} else {
c.gl.uniform1fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
}
c.ctx.Uniform1fv(int32(l), uint32sToFloat32s(v))
case shaderir.Int:
arr := jsutil.TemporaryInt32Array(len(v), uint32sToInt32s(v))
if c.usesWebGL2() {
c.gl.uniform1iv.Invoke(js.Value(l), arr, 0, len(v))
} else {
c.gl.uniform1iv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
}
c.ctx.Uniform1iv(int32(l), uint32sToInt32s(v))
case shaderir.Vec2:
arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v))
if c.usesWebGL2() {
c.gl.uniform2fv.Invoke(js.Value(l), arr, 0, len(v))
} else {
c.gl.uniform2fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
}
c.ctx.Uniform2fv(int32(l), uint32sToFloat32s(v))
case shaderir.Vec3:
arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v))
if c.usesWebGL2() {
c.gl.uniform3fv.Invoke(js.Value(l), arr, 0, len(v))
} else {
c.gl.uniform3fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
}
c.ctx.Uniform3fv(int32(l), uint32sToFloat32s(v))
case shaderir.Vec4:
arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v))
if c.usesWebGL2() {
c.gl.uniform4fv.Invoke(js.Value(l), arr, 0, len(v))
} else {
c.gl.uniform4fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
}
c.ctx.Uniform4fv(int32(l), uint32sToFloat32s(v))
case shaderir.Mat2:
arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v))
if c.usesWebGL2() {
c.gl.uniformMatrix2fv.Invoke(js.Value(l), false, arr, 0, len(v))
} else {
c.gl.uniformMatrix2fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v)))
}
c.ctx.UniformMatrix2fv(int32(l), uint32sToFloat32s(v))
case shaderir.Mat3:
arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v))
if c.usesWebGL2() {
c.gl.uniformMatrix3fv.Invoke(js.Value(l), false, arr, 0, len(v))
} else {
c.gl.uniformMatrix3fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v)))
}
c.ctx.UniformMatrix3fv(int32(l), uint32sToFloat32s(v))
case shaderir.Mat4:
arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v))
if c.usesWebGL2() {
c.gl.uniformMatrix4fv.Invoke(js.Value(l), false, arr, 0, len(v))
} else {
c.gl.uniformMatrix4fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v)))
}
c.ctx.UniformMatrix4fv(int32(l), uint32sToFloat32s(v))
default:
panic(fmt.Sprintf("opengl: unexpected type: %s", typ.String()))
}
return true
}
func (c *context) vertexAttribPointer(index int, size int, stride int, offset int) {
c.gl.vertexAttribPointer.Invoke(index, size, gl.FLOAT, false, stride, offset)
c.ctx.VertexAttribPointer(uint32(index), int32(size), gl.FLOAT, false, int32(stride), offset)
}
func (c *context) enableVertexAttribArray(index int) {
c.gl.enableVertexAttribArray.Invoke(index)
c.ctx.EnableVertexAttribArray(uint32(index))
}
func (c *context) disableVertexAttribArray(index int) {
c.gl.disableVertexAttribArray.Invoke(index)
c.ctx.DisableVertexAttribArray(uint32(index))
}
func (c *context) newArrayBuffer(size int) buffer {
b := c.gl.createBuffer.Invoke()
c.gl.bindBuffer.Invoke(gl.ARRAY_BUFFER, js.Value(b))
c.gl.bufferData.Invoke(gl.ARRAY_BUFFER, size, gl.DYNAMIC_DRAW)
b := c.ctx.CreateBuffer()
c.ctx.BindBuffer(gl.ARRAY_BUFFER, b)
c.ctx.BufferInit(gl.ARRAY_BUFFER, size, gl.DYNAMIC_DRAW)
return buffer(b)
}
func (c *context) newElementArrayBuffer(size int) buffer {
b := c.gl.createBuffer.Invoke()
c.gl.bindBuffer.Invoke(gl.ELEMENT_ARRAY_BUFFER, js.Value(b))
c.gl.bufferData.Invoke(gl.ELEMENT_ARRAY_BUFFER, size, gl.DYNAMIC_DRAW)
b := c.ctx.CreateBuffer()
c.ctx.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, b)
c.ctx.BufferInit(gl.ELEMENT_ARRAY_BUFFER, size, gl.DYNAMIC_DRAW)
return buffer(b)
}
func (c *context) bindArrayBuffer(b buffer) {
c.gl.bindBuffer.Invoke(gl.ARRAY_BUFFER, js.Value(b))
c.ctx.BindBuffer(gl.ARRAY_BUFFER, uint32(b))
}
func (c *context) bindElementArrayBuffer(b buffer) {
c.gl.bindBuffer.Invoke(gl.ELEMENT_ARRAY_BUFFER, js.Value(b))
c.ctx.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, uint32(b))
}
func (c *context) arrayBufferSubData(data []float32) {
l := len(data) * 4
arr := jsutil.TemporaryUint8ArrayFromFloat32Slice(l, data)
if c.usesWebGL2() {
c.gl.bufferSubData.Invoke(gl.ARRAY_BUFFER, 0, arr, 0, l)
} else {
c.gl.bufferSubData.Invoke(gl.ARRAY_BUFFER, 0, arr.Call("subarray", 0, l))
}
s := unsafe.Slice((*byte)(unsafe.Pointer(&data[0])), len(data)*4)
c.ctx.BufferSubData(gl.ARRAY_BUFFER, 0, s)
}
func (c *context) elementArrayBufferSubData(data []uint16) {
l := len(data) * 2
arr := jsutil.TemporaryUint8ArrayFromUint16Slice(l, data)
if c.usesWebGL2() {
c.gl.bufferSubData.Invoke(gl.ELEMENT_ARRAY_BUFFER, 0, arr, 0, l)
} else {
c.gl.bufferSubData.Invoke(gl.ELEMENT_ARRAY_BUFFER, 0, arr.Call("subarray", 0, l))
}
s := unsafe.Slice((*byte)(unsafe.Pointer(&data[0])), len(data)*2)
c.ctx.BufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, s)
}
func (c *context) deleteBuffer(b buffer) {
c.gl.deleteBuffer.Invoke(js.Value(b))
c.ctx.DeleteBuffer(uint32(b))
}
func (c *context) drawElements(len int, offsetInBytes int) {
c.gl.drawElements.Invoke(gl.TRIANGLES, len, gl.UNSIGNED_SHORT, offsetInBytes)
c.ctx.DrawElements(gl.TRIANGLES, int32(len), gl.UNSIGNED_SHORT, offsetInBytes)
}
func (c *context) maxTextureSizeImpl() int {
return c.gl.getParameter.Invoke(gl.MAX_TEXTURE_SIZE).Int()
return c.ctx.GetInteger(gl.MAX_TEXTURE_SIZE)
}
func (c *context) flush() {
c.gl.flush.Invoke()
c.ctx.Flush()
}
func (c *context) texSubImage2D(t textureNative, args []*graphicsdriver.WritePixelsArgs) {
c.bindTexture(t)
for _, a := range args {
arr := jsutil.TemporaryUint8ArrayFromUint8Slice(len(a.Pixels), a.Pixels)
if c.usesWebGL2() {
// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
// GLsizei width, GLsizei height,
// GLenum format, GLenum type, ArrayBufferView pixels, srcOffset);
c.gl.texSubImage2D.Invoke(gl.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, gl.RGBA, gl.UNSIGNED_BYTE, arr, 0)
} else {
// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
// GLsizei width, GLsizei height,
// GLenum format, GLenum type, ArrayBufferView? pixels);
c.gl.texSubImage2D.Invoke(gl.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, gl.RGBA, gl.UNSIGNED_BYTE, arr)
}
c.ctx.TexSubImage2D(gl.TEXTURE_2D, 0, int32(a.X), int32(a.Y), int32(a.Width), int32(a.Height), gl.RGBA, gl.UNSIGNED_BYTE, a.Pixels)
}
}
func (c *context) enableStencilTest() {
c.gl.enable.Invoke(gl.STENCIL_TEST)
c.ctx.Enable(gl.STENCIL_TEST)
}
func (c *context) disableStencilTest() {
c.gl.disable.Invoke(gl.STENCIL_TEST)
c.ctx.Disable(gl.STENCIL_TEST)
}
func (c *context) beginStencilWithEvenOddRule() {
c.gl.clear.Invoke(gl.STENCIL_BUFFER_BIT)
c.gl.stencilFunc.Invoke(gl.ALWAYS, 0x00, 0xff)
c.gl.stencilOp.Invoke(gl.KEEP, gl.KEEP, gl.INVERT)
c.gl.colorMask.Invoke(false, false, false, false)
c.ctx.Clear(gl.STENCIL_BUFFER_BIT)
c.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff)
c.ctx.StencilOp(gl.KEEP, gl.KEEP, gl.INVERT)
c.ctx.ColorMask(false, false, false, false)
}
func (c *context) endStencilWithEvenOddRule() {
c.gl.stencilFunc.Invoke(gl.NOTEQUAL, 0x00, 0xff)
c.gl.stencilOp.Invoke(gl.KEEP, gl.KEEP, gl.KEEP)
c.gl.colorMask.Invoke(true, true, true, true)
c.ctx.StencilFunc(gl.NOTEQUAL, 0x00, 0xff)
c.ctx.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP)
c.ctx.ColorMask(true, true, true, true)
}
func (c *context) isES() bool {
// WebGL is compatible with GLES.
return true
return c.ctx.IsES()
}

View File

@ -35,50 +35,16 @@ type (
buffer uint32
)
func (t textureNative) equal(rhs textureNative) bool {
return t == rhs
}
func (r renderbufferNative) equal(rhs renderbufferNative) bool {
return r == rhs
}
func (f framebufferNative) equal(rhs framebufferNative) bool {
return f == rhs
}
func (s shader) equal(rhs shader) bool {
return s == rhs
}
func (b buffer) equal(rhs buffer) bool {
return b == rhs
}
func (p program) equal(rhs program) bool {
return p == rhs
}
type (
uniformLocation int32
attribLocation int32
)
func (u uniformLocation) equal(rhs uniformLocation) bool {
return u == rhs
}
type programID uint32
const (
invalidFramebuffer = (1 << 32) - 1
invalidUniform = -1
)
func getProgramID(p program) programID {
return programID(p)
}
type contextImpl struct {
ctx gl.Context
init bool
@ -150,10 +116,12 @@ func (c *context) bindFramebufferImpl(f framebufferNative) {
}
func (c *context) framebufferPixels(buf []byte, f *framebuffer, x, y, width, height int) {
if got, want := len(buf), 4*width*height; got != want {
panic(fmt.Sprintf("opengl: len(buf) must be %d but %d", got, want))
}
c.ctx.Flush()
c.bindFramebuffer(f.native)
c.ctx.ReadPixels(buf, int32(x), int32(y), int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE)
}
@ -213,7 +181,7 @@ func (c *context) deleteRenderbuffer(r renderbufferNative) {
if !c.ctx.IsRenderbuffer(uint32(r)) {
return
}
if c.lastRenderbuffer.equal(r) {
if c.lastRenderbuffer == r {
c.lastRenderbuffer = 0
}
c.ctx.DeleteRenderbuffer(uint32(r))
@ -332,8 +300,7 @@ func (c *context) deleteProgram(p program) {
}
func (c *context) getUniformLocationImpl(p program, location string) uniformLocation {
u := uniformLocation(c.ctx.GetUniformLocation(uint32(p), location))
return u
return uniformLocation(c.ctx.GetUniformLocation(uint32(p), location))
}
func (c *context) uniformInt(p program, location string, v int) bool {

View File

@ -45,7 +45,7 @@ func newScreenFramebuffer(context *context, width, height int) *framebuffer {
}
func (f *framebuffer) delete(context *context) {
if !f.native.equal(context.getScreenFramebuffer()) {
if f.native != context.getScreenFramebuffer() {
context.deleteFramebuffer(f.native)
}
}

View File

@ -0,0 +1,645 @@
// Copyright 2022 The Ebitengine 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.
package gl
import (
"fmt"
"syscall/js"
"github.com/hajimehoshi/ebiten/v2/internal/jsutil"
)
type defaultContext struct {
fnActiveTexture js.Value
fnAttachShader js.Value
fnBindAttribLocation js.Value
fnBindBuffer js.Value
fnBindFramebuffer js.Value
fnBindRenderbuffer js.Value
fnBindTexture js.Value
fnBlendEquationSeparate js.Value
fnBlendFuncSeparate js.Value
fnBufferData js.Value
fnBufferSubData js.Value
fnCheckFramebufferStatus js.Value
fnClear js.Value
fnColorMask js.Value
fnCompileShader js.Value
fnCreateBuffer js.Value
fnCreateFramebuffer js.Value
fnCreateProgram js.Value
fnCreateRenderbuffer js.Value
fnCreateShader js.Value
fnCreateTexture js.Value
fnDeleteBuffer js.Value
fnDeleteFramebuffer js.Value
fnDeleteProgram js.Value
fnDeleteRenderbuffer js.Value
fnDeleteShader js.Value
fnDeleteTexture js.Value
fnDisable js.Value
fnDisableVertexAttribArray js.Value
fnDrawElements js.Value
fnEnable js.Value
fnEnableVertexAttribArray js.Value
fnFramebufferRenderbuffer js.Value
fnFramebufferTexture2D js.Value
fnFlush js.Value
fnGetError js.Value
fnGetParameter js.Value
fnGetProgramInfoLog js.Value
fnGetProgramParameter js.Value
fnGetShaderInfoLog js.Value
fnGetShaderParameter js.Value
fnGetUniformLocation js.Value
fnIsFramebuffer js.Value
fnIsProgram js.Value
fnIsRenderbuffer js.Value
fnIsTexture js.Value
fnLinkProgram js.Value
fnPixelStorei js.Value
fnReadPixels js.Value
fnRenderbufferStorage js.Value
fnScissor js.Value
fnShaderSource js.Value
fnStencilFunc js.Value
fnStencilMask js.Value
fnStencilOp js.Value
fnTexImage2D js.Value
fnTexSubImage2D js.Value
fnTexParameteri js.Value
fnUniform1fv js.Value
fnUniform1i js.Value
fnUniform1iv js.Value
fnUniform2fv js.Value
fnUniform3fv js.Value
fnUniform4fv js.Value
fnUniformMatrix2fv js.Value
fnUniformMatrix3fv js.Value
fnUniformMatrix4fv js.Value
fnUseProgram js.Value
fnVertexAttribPointer js.Value
fnViewport js.Value
webGL2 bool
buffers values
framebuffers values
programs values
renderbuffers values
shaders values
textures values
uniformLocations map[uint32]*values
}
type values struct {
idToValue map[uint32]js.Value
lastID uint32
}
func (v *values) create(value js.Value) uint32 {
v.lastID++
id := v.lastID
if v.idToValue == nil {
v.idToValue = map[uint32]js.Value{}
}
v.idToValue[id] = value
return id
}
func (v *values) get(id uint32) js.Value {
return v.idToValue[id]
}
func (v *values) getID(value js.Value) (uint32, bool) {
for id, v := range v.idToValue {
if v.Equal(value) {
return id, true
}
}
return 0, false
}
func (v *values) getOrCreate(value js.Value) uint32 {
id, ok := v.getID(value)
if ok {
return id
}
return v.create(value)
}
func (v *values) delete(id uint32) {
delete(v.idToValue, id)
}
func NewDefaultContext(v js.Value) (Context, error) {
// Passing a Go string to the JS world is expensive. This causes conversion to UTF-16 (#1438).
// In order to reduce the cost when calling functions, create the function objects by bind and use them.
g := &defaultContext{
fnActiveTexture: v.Get("activeTexture").Call("bind", v),
fnAttachShader: v.Get("attachShader").Call("bind", v),
fnBindAttribLocation: v.Get("bindAttribLocation").Call("bind", v),
fnBindBuffer: v.Get("bindBuffer").Call("bind", v),
fnBindFramebuffer: v.Get("bindFramebuffer").Call("bind", v),
fnBindRenderbuffer: v.Get("bindRenderbuffer").Call("bind", v),
fnBindTexture: v.Get("bindTexture").Call("bind", v),
fnBlendEquationSeparate: v.Get("blendEquationSeparate").Call("bind", v),
fnBlendFuncSeparate: v.Get("blendFuncSeparate").Call("bind", v),
fnBufferData: v.Get("bufferData").Call("bind", v),
fnBufferSubData: v.Get("bufferSubData").Call("bind", v),
fnCheckFramebufferStatus: v.Get("checkFramebufferStatus").Call("bind", v),
fnClear: v.Get("clear").Call("bind", v),
fnColorMask: v.Get("colorMask").Call("bind", v),
fnCompileShader: v.Get("compileShader").Call("bind", v),
fnCreateBuffer: v.Get("createBuffer").Call("bind", v),
fnCreateFramebuffer: v.Get("createFramebuffer").Call("bind", v),
fnCreateProgram: v.Get("createProgram").Call("bind", v),
fnCreateRenderbuffer: v.Get("createRenderbuffer").Call("bind", v),
fnCreateShader: v.Get("createShader").Call("bind", v),
fnCreateTexture: v.Get("createTexture").Call("bind", v),
fnDeleteBuffer: v.Get("deleteBuffer").Call("bind", v),
fnDeleteFramebuffer: v.Get("deleteFramebuffer").Call("bind", v),
fnDeleteProgram: v.Get("deleteProgram").Call("bind", v),
fnDeleteRenderbuffer: v.Get("deleteRenderbuffer").Call("bind", v),
fnDeleteShader: v.Get("deleteShader").Call("bind", v),
fnDeleteTexture: v.Get("deleteTexture").Call("bind", v),
fnDisable: v.Get("disable").Call("bind", v),
fnDisableVertexAttribArray: v.Get("disableVertexAttribArray").Call("bind", v),
fnDrawElements: v.Get("drawElements").Call("bind", v),
fnEnable: v.Get("enable").Call("bind", v),
fnEnableVertexAttribArray: v.Get("enableVertexAttribArray").Call("bind", v),
fnFramebufferRenderbuffer: v.Get("framebufferRenderbuffer").Call("bind", v),
fnFramebufferTexture2D: v.Get("framebufferTexture2D").Call("bind", v),
fnFlush: v.Get("flush").Call("bind", v),
fnGetError: v.Get("getError").Call("bind", v),
fnGetParameter: v.Get("getParameter").Call("bind", v),
fnGetProgramInfoLog: v.Get("getProgramInfoLog").Call("bind", v),
fnGetProgramParameter: v.Get("getProgramParameter").Call("bind", v),
fnGetShaderInfoLog: v.Get("getShaderInfoLog").Call("bind", v),
fnGetShaderParameter: v.Get("getShaderParameter").Call("bind", v),
fnGetUniformLocation: v.Get("getUniformLocation").Call("bind", v),
fnIsFramebuffer: v.Get("isFramebuffer").Call("bind", v),
fnIsProgram: v.Get("isProgram").Call("bind", v),
fnIsRenderbuffer: v.Get("isRenderbuffer").Call("bind", v),
fnIsTexture: v.Get("isTexture").Call("bind", v),
fnLinkProgram: v.Get("linkProgram").Call("bind", v),
fnPixelStorei: v.Get("pixelStorei").Call("bind", v),
fnReadPixels: v.Get("readPixels").Call("bind", v),
fnRenderbufferStorage: v.Get("renderbufferStorage").Call("bind", v),
fnScissor: v.Get("scissor").Call("bind", v),
fnShaderSource: v.Get("shaderSource").Call("bind", v),
fnStencilFunc: v.Get("stencilFunc").Call("bind", v),
fnStencilMask: v.Get("stencilMask").Call("bind", v),
fnStencilOp: v.Get("stencilOp").Call("bind", v),
fnTexImage2D: v.Get("texImage2D").Call("bind", v),
fnTexSubImage2D: v.Get("texSubImage2D").Call("bind", v),
fnTexParameteri: v.Get("texParameteri").Call("bind", v),
fnUniform1fv: v.Get("uniform1fv").Call("bind", v),
fnUniform1i: v.Get("uniform1i").Call("bind", v),
fnUniform1iv: v.Get("uniform1iv").Call("bind", v),
fnUniform2fv: v.Get("uniform2fv").Call("bind", v),
fnUniform3fv: v.Get("uniform3fv").Call("bind", v),
fnUniform4fv: v.Get("uniform4fv").Call("bind", v),
fnUniformMatrix2fv: v.Get("uniformMatrix2fv").Call("bind", v),
fnUniformMatrix3fv: v.Get("uniformMatrix3fv").Call("bind", v),
fnUniformMatrix4fv: v.Get("uniformMatrix4fv").Call("bind", v),
fnUseProgram: v.Get("useProgram").Call("bind", v),
fnVertexAttribPointer: v.Get("vertexAttribPointer").Call("bind", v),
fnViewport: v.Get("viewport").Call("bind", v),
}
if webGL2 := js.Global().Get("WebGL2RenderingContext"); webGL2.Truthy() {
g.webGL2 = v.InstanceOf(webGL2)
}
return g, nil
}
func (c *defaultContext) getUniformLocation(location int32) js.Value {
program := uint32(location) >> 5
return c.uniformLocations[program].get(uint32(location) & ((1 << 5) - 1))
}
func (c *defaultContext) LoadFunctions() error {
return nil
}
func (c *defaultContext) IsES() bool {
// WebGL is compatible with GLES.
return true
}
func (c *defaultContext) ActiveTexture(texture uint32) {
c.fnActiveTexture.Invoke(texture)
}
func (c *defaultContext) AttachShader(program uint32, shader uint32) {
c.fnAttachShader.Invoke(c.programs.get(program), c.shaders.get(shader))
}
func (c *defaultContext) BindAttribLocation(program uint32, index uint32, name string) {
c.fnBindAttribLocation.Invoke(c.programs.get(program), index, name)
}
func (c *defaultContext) BindBuffer(target uint32, buffer uint32) {
c.fnBindBuffer.Invoke(target, c.buffers.get(buffer))
}
func (c *defaultContext) BindFramebuffer(target uint32, framebuffer uint32) {
c.fnBindFramebuffer.Invoke(target, c.framebuffers.get(framebuffer))
}
func (c *defaultContext) BindRenderbuffer(target uint32, renderbuffer uint32) {
c.fnBindRenderbuffer.Invoke(target, c.renderbuffers.get(renderbuffer))
}
func (c *defaultContext) BindTexture(target uint32, texture uint32) {
c.fnBindTexture.Invoke(target, c.textures.get(texture))
}
func (c *defaultContext) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
c.fnBlendEquationSeparate.Invoke(modeRGB, modeAlpha)
}
func (c *defaultContext) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) {
c.fnBlendFuncSeparate.Invoke(srcRGB, dstRGB, srcAlpha, dstAlpha)
}
func (c *defaultContext) BufferInit(target uint32, size int, usage uint32) {
c.fnBufferData.Invoke(target, size, usage)
}
func (c *defaultContext) BufferSubData(target uint32, offset int, data []byte) {
l := len(data)
arr := jsutil.TemporaryUint8ArrayFromUint8Slice(l, data)
if c.webGL2 {
c.fnBufferSubData.Invoke(target, offset, arr, 0, l)
} else {
c.fnBufferSubData.Invoke(target, offset, arr.Call("subarray", 0, l))
}
}
func (c *defaultContext) CheckFramebufferStatus(target uint32) uint32 {
return uint32(c.fnCheckFramebufferStatus.Invoke(target).Int())
}
func (c *defaultContext) Clear(mask uint32) {
c.fnClear.Invoke(mask)
}
func (c *defaultContext) ColorMask(red, green, blue, alpha bool) {
c.fnColorMask.Invoke(red, green, blue, alpha)
}
func (c *defaultContext) CompileShader(shader uint32) {
c.fnCompileShader.Invoke(c.shaders.get(shader))
}
func (c *defaultContext) CreateBuffer() uint32 {
return c.buffers.create(c.fnCreateBuffer.Invoke())
}
func (c *defaultContext) CreateFramebuffer() uint32 {
return c.framebuffers.create(c.fnCreateFramebuffer.Invoke())
}
func (c *defaultContext) CreateProgram() uint32 {
return c.programs.create(c.fnCreateProgram.Invoke())
}
func (c *defaultContext) CreateRenderbuffer() uint32 {
return c.renderbuffers.create(c.fnCreateRenderbuffer.Invoke())
}
func (c *defaultContext) CreateShader(xtype uint32) uint32 {
return c.shaders.create(c.fnCreateShader.Invoke(xtype))
}
func (c *defaultContext) CreateTexture() uint32 {
return c.textures.create(c.fnCreateTexture.Invoke())
}
func (c *defaultContext) DeleteBuffer(buffer uint32) {
c.fnDeleteBuffer.Invoke(c.buffers.get(buffer))
c.buffers.delete(buffer)
}
func (c *defaultContext) DeleteFramebuffer(framebuffer uint32) {
c.fnDeleteFramebuffer.Invoke(c.framebuffers.get(framebuffer))
c.framebuffers.delete(framebuffer)
}
func (c *defaultContext) DeleteProgram(program uint32) {
c.fnDeleteProgram.Invoke(c.programs.get(program))
c.programs.delete(program)
delete(c.uniformLocations, program)
}
func (c *defaultContext) DeleteRenderbuffer(renderbuffer uint32) {
c.fnDeleteRenderbuffer.Invoke(c.renderbuffers.get(renderbuffer))
c.renderbuffers.delete(renderbuffer)
}
func (c *defaultContext) DeleteShader(shader uint32) {
c.fnDeleteShader.Invoke(c.shaders.get(shader))
c.shaders.delete(shader)
}
func (c *defaultContext) DeleteTexture(texture uint32) {
c.fnDeleteTexture.Invoke(c.textures.get(texture))
c.textures.delete(texture)
}
func (c *defaultContext) Disable(cap uint32) {
c.fnDisable.Invoke(cap)
}
func (c *defaultContext) DisableVertexAttribArray(index uint32) {
c.fnDisableVertexAttribArray.Invoke(index)
}
func (c *defaultContext) DrawElements(mode uint32, count int32, xtype uint32, offset int) {
c.fnDrawElements.Invoke(mode, count, xtype, offset)
}
func (c *defaultContext) Enable(cap uint32) {
c.fnEnable.Invoke(cap)
}
func (c *defaultContext) EnableVertexAttribArray(index uint32) {
c.fnEnableVertexAttribArray.Invoke(index)
}
func (c *defaultContext) Flush() {
c.fnFlush.Invoke()
}
func (c *defaultContext) FramebufferRenderbuffer(target uint32, attachment uint32, renderbuffertarget uint32, renderbuffer uint32) {
c.fnFramebufferRenderbuffer.Invoke(target, attachment, renderbuffertarget, c.renderbuffers.get(renderbuffer))
}
func (c *defaultContext) FramebufferTexture2D(target uint32, attachment uint32, textarget uint32, texture uint32, level int32) {
c.fnFramebufferTexture2D.Invoke(target, attachment, textarget, c.textures.get(texture), level)
}
func (c *defaultContext) GetError() uint32 {
return uint32(c.fnGetError.Invoke().Int())
}
func (c *defaultContext) GetInteger(pname uint32) int {
ret := c.fnGetParameter.Invoke(pname)
switch pname {
case FRAMEBUFFER_BINDING:
id, ok := c.framebuffers.getID(ret)
if !ok {
return 0
}
return int(id)
case MAX_TEXTURE_SIZE:
return ret.Int()
default:
panic(fmt.Sprintf("gl: unexpected pname at GetInteger: %d", pname))
}
}
func (c *defaultContext) GetProgramInfoLog(program uint32) string {
return c.fnGetProgramInfoLog.Invoke(c.programs.get(program)).String()
}
func (c *defaultContext) GetProgrami(program uint32, pname uint32) int {
v := c.fnGetProgramParameter.Invoke(c.programs.get(program), pname)
switch v.Type() {
case js.TypeNumber:
return v.Int()
case js.TypeBoolean:
if v.Bool() {
return TRUE
}
return FALSE
default:
panic(fmt.Sprintf("gl: unexpected return type at GetProgrami: %v", v))
}
}
func (c *defaultContext) GetShaderInfoLog(shader uint32) string {
return c.fnGetShaderInfoLog.Invoke(c.shaders.get(shader)).String()
}
func (c *defaultContext) GetShaderi(shader uint32, pname uint32) int {
v := c.fnGetShaderParameter.Invoke(c.shaders.get(shader), pname)
switch v.Type() {
case js.TypeNumber:
return v.Int()
case js.TypeBoolean:
if v.Bool() {
return TRUE
}
return FALSE
default:
panic(fmt.Sprintf("gl: unexpected return type at GetShaderi: %v", v))
}
}
func (c *defaultContext) GetUniformLocation(program uint32, name string) int32 {
location := c.fnGetUniformLocation.Invoke(c.programs.get(program), name)
if c.uniformLocations == nil {
c.uniformLocations = map[uint32]*values{}
}
vs, ok := c.uniformLocations[program]
if !ok {
vs = &values{}
c.uniformLocations[program] = vs
}
idx := vs.getOrCreate(location)
return int32((program << 5) | idx)
}
func (c *defaultContext) IsFramebuffer(framebuffer uint32) bool {
return c.fnIsFramebuffer.Invoke(c.framebuffers.get(framebuffer)).Bool()
}
func (c *defaultContext) IsProgram(program uint32) bool {
return c.fnIsProgram.Invoke(c.programs.get(program)).Bool()
}
func (c *defaultContext) IsRenderbuffer(renderbuffer uint32) bool {
return c.fnIsRenderbuffer.Invoke(c.renderbuffers.get(renderbuffer)).Bool()
}
func (c *defaultContext) IsTexture(texture uint32) bool {
return c.fnIsTexture.Invoke(c.textures.get(texture)).Bool()
}
func (c *defaultContext) LinkProgram(program uint32) {
c.fnLinkProgram.Invoke(c.programs.get(program))
}
func (c *defaultContext) PixelStorei(pname uint32, param int32) {
c.fnPixelStorei.Invoke(pname, param)
}
func (c *defaultContext) ReadPixels(dst []byte, x int32, y int32, width int32, height int32, format uint32, xtype uint32) {
if dst == nil {
c.fnReadPixels.Invoke(x, y, width, height, format, xtype, 0)
return
}
p := jsutil.TemporaryUint8ArrayFromUint8Slice(len(dst), nil)
c.fnReadPixels.Invoke(x, y, width, height, format, xtype, p)
js.CopyBytesToGo(dst, p)
}
func (c *defaultContext) RenderbufferStorage(target uint32, internalFormat uint32, width int32, height int32) {
c.fnRenderbufferStorage.Invoke(target, internalFormat, width, height)
}
func (c *defaultContext) Scissor(x, y, width, height int32) {
c.fnScissor.Invoke(x, y, width, height)
}
func (c *defaultContext) ShaderSource(shader uint32, xstring string) {
c.fnShaderSource.Invoke(c.shaders.get(shader), xstring)
}
func (c *defaultContext) StencilFunc(func_ uint32, ref int32, mask uint32) {
c.fnStencilFunc.Invoke(func_, ref, mask)
}
func (c *defaultContext) StencilOp(sfail, dpfail, dppass uint32) {
c.fnStencilOp.Invoke(sfail, dpfail, dppass)
}
func (c *defaultContext) TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) {
if pixels != nil {
panic("gl: TexImage2D with non-nil pixels is not implemented")
}
c.fnTexImage2D.Invoke(target, level, internalformat, width, height, 0, format, xtype, nil)
}
func (c *defaultContext) TexParameteri(target uint32, pname uint32, param int32) {
c.fnTexParameteri.Invoke(target, pname, param)
}
func (c *defaultContext) TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) {
arr := jsutil.TemporaryUint8ArrayFromUint8Slice(len(pixels), pixels)
if c.webGL2 {
// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
// GLsizei width, GLsizei height,
// GLenum format, GLenum type, ArrayBufferView pixels, srcOffset);
c.fnTexSubImage2D.Invoke(target, level, xoffset, yoffset, width, height, format, xtype, arr, 0)
} else {
// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
// GLsizei width, GLsizei height,
// GLenum format, GLenum type, ArrayBufferView? pixels);
c.fnTexSubImage2D.Invoke(target, level, xoffset, yoffset, width, height, format, xtype, arr)
}
}
func (c *defaultContext) Uniform1fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
if c.webGL2 {
c.fnUniform1fv.Invoke(l, arr, 0, len(value))
} else {
c.fnUniform1fv.Invoke(l, arr.Call("subarray", 0, len(value)))
}
}
func (c *defaultContext) Uniform1i(location int32, v0 int32) {
l := c.getUniformLocation(location)
c.fnUniform1i.Invoke(l, v0)
}
func (c *defaultContext) Uniform1iv(location int32, value []int32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryInt32Array(len(value), value)
if c.webGL2 {
c.fnUniform1iv.Invoke(l, arr, 0, len(value))
} else {
c.fnUniform1iv.Invoke(l, arr.Call("subarray", 0, len(value)))
}
}
func (c *defaultContext) Uniform2fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
if c.webGL2 {
c.fnUniform2fv.Invoke(l, arr, 0, len(value))
} else {
c.fnUniform2fv.Invoke(l, arr.Call("subarray", 0, len(value)))
}
}
func (c *defaultContext) Uniform3fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
if c.webGL2 {
c.fnUniform3fv.Invoke(l, arr, 0, len(value))
} else {
c.fnUniform3fv.Invoke(l, arr.Call("subarray", 0, len(value)))
}
}
func (c *defaultContext) Uniform4fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
if c.webGL2 {
c.fnUniform4fv.Invoke(l, arr, 0, len(value))
} else {
c.fnUniform4fv.Invoke(l, arr.Call("subarray", 0, len(value)))
}
}
func (c *defaultContext) UniformMatrix2fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
if c.webGL2 {
c.fnUniformMatrix2fv.Invoke(l, false, arr, 0, len(value))
} else {
c.fnUniformMatrix2fv.Invoke(l, false, arr.Call("subarray", 0, len(value)))
}
}
func (c *defaultContext) UniformMatrix3fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
if c.webGL2 {
c.fnUniformMatrix3fv.Invoke(l, false, arr, 0, len(value))
} else {
c.fnUniformMatrix3fv.Invoke(l, false, arr.Call("subarray", 0, len(value)))
}
}
func (c *defaultContext) UniformMatrix4fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
if c.webGL2 {
c.fnUniformMatrix4fv.Invoke(l, false, arr, 0, len(value))
} else {
c.fnUniformMatrix4fv.Invoke(l, false, arr.Call("subarray", 0, len(value)))
}
}
func (c *defaultContext) UseProgram(program uint32) {
c.fnUseProgram.Invoke(c.programs.get(program))
}
func (c *defaultContext) VertexAttribPointer(index uint32, size int32, xtype uint32, normalized bool, stride int32, offset int) {
c.fnVertexAttribPointer.Invoke(index, size, xtype, normalized, stride, offset)
}
func (c *defaultContext) Viewport(x int32, y int32, width int32, height int32) {
c.fnViewport.Invoke(x, y, width, height)
}

View File

@ -1,174 +0,0 @@
// Copyright 2021 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.
package opengl
import (
"syscall/js"
)
type jsGL struct {
activeTexture js.Value
attachShader js.Value
bindAttribLocation js.Value
bindBuffer js.Value
bindFramebuffer js.Value
bindRenderbuffer js.Value
bindTexture js.Value
blendEquationSeparate js.Value
blendFuncSeparate js.Value
bufferData js.Value
bufferSubData js.Value
checkFramebufferStatus js.Value
clear js.Value
colorMask js.Value
compileShader js.Value
createBuffer js.Value
createFramebuffer js.Value
createProgram js.Value
createRenderbuffer js.Value
createShader js.Value
createTexture js.Value
deleteBuffer js.Value
deleteFramebuffer js.Value
deleteProgram js.Value
deleteRenderbuffer js.Value
deleteShader js.Value
deleteTexture js.Value
disable js.Value
disableVertexAttribArray js.Value
drawElements js.Value
enable js.Value
enableVertexAttribArray js.Value
framebufferRenderbuffer js.Value
framebufferTexture2D js.Value
flush js.Value
getExtension js.Value
getParameter js.Value
getProgramInfoLog js.Value
getProgramParameter js.Value
getShaderInfoLog js.Value
getShaderParameter js.Value
getUniformLocation js.Value
isContextLost js.Value
isFramebuffer js.Value
isProgram js.Value
isRenderbuffer js.Value
isTexture js.Value
linkProgram js.Value
pixelStorei js.Value
readPixels js.Value
renderbufferStorage js.Value
scissor js.Value
shaderSource js.Value
stencilFunc js.Value
stencilMask js.Value
stencilOp js.Value
texImage2D js.Value
texSubImage2D js.Value
texParameteri js.Value
uniform1fv js.Value
uniform1i js.Value
uniform1iv js.Value
uniform2fv js.Value
uniform3fv js.Value
uniform4fv js.Value
uniformMatrix2fv js.Value
uniformMatrix3fv js.Value
uniformMatrix4fv js.Value
useProgram js.Value
vertexAttribPointer js.Value
viewport js.Value
}
func (c *context) newJSGL(v js.Value) *jsGL {
// Passing a Go string to the JS world is expensive. This causes conversion to UTF-16 (#1438).
// In order to reduce the cost when calling functions, create the function objects by bind and use them.
g := &jsGL{
activeTexture: v.Get("activeTexture").Call("bind", v),
attachShader: v.Get("attachShader").Call("bind", v),
bindAttribLocation: v.Get("bindAttribLocation").Call("bind", v),
bindBuffer: v.Get("bindBuffer").Call("bind", v),
bindFramebuffer: v.Get("bindFramebuffer").Call("bind", v),
bindRenderbuffer: v.Get("bindRenderbuffer").Call("bind", v),
bindTexture: v.Get("bindTexture").Call("bind", v),
blendEquationSeparate: v.Get("blendEquationSeparate").Call("bind", v),
blendFuncSeparate: v.Get("blendFuncSeparate").Call("bind", v),
bufferData: v.Get("bufferData").Call("bind", v),
bufferSubData: v.Get("bufferSubData").Call("bind", v),
checkFramebufferStatus: v.Get("checkFramebufferStatus").Call("bind", v),
clear: v.Get("clear").Call("bind", v),
colorMask: v.Get("colorMask").Call("bind", v),
compileShader: v.Get("compileShader").Call("bind", v),
createBuffer: v.Get("createBuffer").Call("bind", v),
createFramebuffer: v.Get("createFramebuffer").Call("bind", v),
createProgram: v.Get("createProgram").Call("bind", v),
createRenderbuffer: v.Get("createRenderbuffer").Call("bind", v),
createShader: v.Get("createShader").Call("bind", v),
createTexture: v.Get("createTexture").Call("bind", v),
deleteBuffer: v.Get("deleteBuffer").Call("bind", v),
deleteFramebuffer: v.Get("deleteFramebuffer").Call("bind", v),
deleteProgram: v.Get("deleteProgram").Call("bind", v),
deleteRenderbuffer: v.Get("deleteRenderbuffer").Call("bind", v),
deleteShader: v.Get("deleteShader").Call("bind", v),
deleteTexture: v.Get("deleteTexture").Call("bind", v),
disable: v.Get("disable").Call("bind", v),
disableVertexAttribArray: v.Get("disableVertexAttribArray").Call("bind", v),
drawElements: v.Get("drawElements").Call("bind", v),
enable: v.Get("enable").Call("bind", v),
enableVertexAttribArray: v.Get("enableVertexAttribArray").Call("bind", v),
framebufferRenderbuffer: v.Get("framebufferRenderbuffer").Call("bind", v),
framebufferTexture2D: v.Get("framebufferTexture2D").Call("bind", v),
flush: v.Get("flush").Call("bind", v),
getParameter: v.Get("getParameter").Call("bind", v),
getProgramInfoLog: v.Get("getProgramInfoLog").Call("bind", v),
getProgramParameter: v.Get("getProgramParameter").Call("bind", v),
getShaderInfoLog: v.Get("getShaderInfoLog").Call("bind", v),
getShaderParameter: v.Get("getShaderParameter").Call("bind", v),
getUniformLocation: v.Get("getUniformLocation").Call("bind", v),
isContextLost: v.Get("isContextLost").Call("bind", v),
isFramebuffer: v.Get("isFramebuffer").Call("bind", v),
isProgram: v.Get("isProgram").Call("bind", v),
isRenderbuffer: v.Get("isRenderbuffer").Call("bind", v),
isTexture: v.Get("isTexture").Call("bind", v),
linkProgram: v.Get("linkProgram").Call("bind", v),
pixelStorei: v.Get("pixelStorei").Call("bind", v),
readPixels: v.Get("readPixels").Call("bind", v),
renderbufferStorage: v.Get("renderbufferStorage").Call("bind", v),
scissor: v.Get("scissor").Call("bind", v),
shaderSource: v.Get("shaderSource").Call("bind", v),
stencilFunc: v.Get("stencilFunc").Call("bind", v),
stencilMask: v.Get("stencilMask").Call("bind", v),
stencilOp: v.Get("stencilOp").Call("bind", v),
texImage2D: v.Get("texImage2D").Call("bind", v),
texSubImage2D: v.Get("texSubImage2D").Call("bind", v),
texParameteri: v.Get("texParameteri").Call("bind", v),
uniform1fv: v.Get("uniform1fv").Call("bind", v),
uniform1i: v.Get("uniform1i").Call("bind", v),
uniform1iv: v.Get("uniform1iv").Call("bind", v),
uniform2fv: v.Get("uniform2fv").Call("bind", v),
uniform3fv: v.Get("uniform3fv").Call("bind", v),
uniform4fv: v.Get("uniform4fv").Call("bind", v),
uniformMatrix2fv: v.Get("uniformMatrix2fv").Call("bind", v),
uniformMatrix3fv: v.Get("uniformMatrix3fv").Call("bind", v),
uniformMatrix4fv: v.Get("uniformMatrix4fv").Call("bind", v),
useProgram: v.Get("useProgram").Call("bind", v),
vertexAttribPointer: v.Get("vertexAttribPointer").Call("bind", v),
viewport: v.Get("viewport").Call("bind", v),
}
if !c.usesWebGL2() {
g.getExtension = v.Get("getExtension").Call("bind", v)
}
return g
}

View File

@ -44,10 +44,10 @@ func (i *Image) Dispose() {
if i.framebuffer != nil {
i.framebuffer.delete(&i.graphics.context)
}
if !i.texture.equal(*new(textureNative)) {
if i.texture != 0 {
i.graphics.context.deleteTexture(i.texture)
}
if !i.stencil.equal(*new(renderbufferNative)) {
if i.stencil != 0 {
i.graphics.context.deleteRenderbuffer(i.stencil)
}
@ -100,7 +100,7 @@ func (i *Image) ensureFramebuffer() error {
}
func (i *Image) ensureStencilBuffer() error {
if !i.stencil.equal(*new(renderbufferNative)) {
if i.stencil != 0 {
return nil
}

View File

@ -14,31 +14,28 @@
package opengl
// Since js.Object (Program) can't be keys of a map, use integers (programID) instead.
type locationCache struct {
uniformLocationCache map[programID]map[string]uniformLocation
uniformLocationCache map[program]map[string]uniformLocation
}
func newLocationCache() *locationCache {
return &locationCache{
uniformLocationCache: map[programID]map[string]uniformLocation{},
uniformLocationCache: map[program]map[string]uniformLocation{},
}
}
func (c *locationCache) GetUniformLocation(context *context, p program, location string) uniformLocation {
id := getProgramID(p)
if _, ok := c.uniformLocationCache[id]; !ok {
c.uniformLocationCache[id] = map[string]uniformLocation{}
if _, ok := c.uniformLocationCache[p]; !ok {
c.uniformLocationCache[p] = map[string]uniformLocation{}
}
l, ok := c.uniformLocationCache[id][location]
l, ok := c.uniformLocationCache[p][location]
if !ok {
l = context.getUniformLocationImpl(p, location)
c.uniformLocationCache[id][location] = l
c.uniformLocationCache[p][location] = l
}
return l
}
func (c *locationCache) deleteProgram(p program) {
delete(c.uniformLocationCache, getProgramID(p))
delete(c.uniformLocationCache, p)
}

View File

@ -127,19 +127,14 @@ type openGLState struct {
lastActiveTexture int
}
var (
zeroBuffer buffer
zeroProgram program
)
// reset resets or initializes the OpenGL state.
func (s *openGLState) reset(context *context) error {
if err := context.reset(); err != nil {
return err
}
s.lastProgram = zeroProgram
context.useProgram(zeroProgram)
s.lastProgram = 0
context.useProgram(0)
for key := range s.lastUniforms {
delete(s.lastUniforms, key)
}
@ -147,10 +142,10 @@ func (s *openGLState) reset(context *context) error {
// On browsers (at least Chrome), buffers are already detached from the context
// and must not be deleted by DeleteBuffer.
if runtime.GOOS != "js" {
if !s.arrayBuffer.equal(zeroBuffer) {
if s.arrayBuffer != 0 {
context.deleteBuffer(s.arrayBuffer)
}
if !s.elementArrayBuffer.equal(zeroBuffer) {
if s.elementArrayBuffer != 0 {
context.deleteBuffer(s.elementArrayBuffer)
}
}
@ -203,9 +198,9 @@ func (g *Graphics) textureVariableName(idx int) string {
// useProgram uses the program (programTexture).
func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textures [graphics.ShaderImageCount]textureVariable) error {
if !g.state.lastProgram.equal(program) {
if g.state.lastProgram != program {
g.context.useProgram(program)
if g.state.lastProgram.equal(zeroProgram) {
if g.state.lastProgram == 0 {
theArrayBufferLayout.enable(&g.context)
g.context.bindArrayBuffer(g.state.arrayBuffer)
g.context.bindElementArrayBuffer(g.state.elementArrayBuffer)
@ -251,7 +246,7 @@ loop:
// If the texture is already bound, set the texture variable to point to the texture.
// Rebinding the same texture seems problematic (#1193).
for _, at := range g.activatedTextures {
if t.native.equal(at.textureNative) {
if t.native == at.textureNative {
g.context.uniformInt(program, g.textureVariableName(i), at.index)
continue loop
}