updated ebiten version from 2.7.9 to 2.9.9

This commit is contained in:
2026-06-15 19:06:55 +02:00
parent 21edbc41c4
commit db1b625069
405 changed files with 31913 additions and 12595 deletions
@@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"image"
"runtime"
"sync"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
@@ -89,7 +90,6 @@ type (
type (
uniformLocation int32
attribLocation int32
)
const (
@@ -100,19 +100,19 @@ const (
type context struct {
ctx gl.Context
locationCache *locationCache
screenFramebuffer framebufferNative // This might not be the default frame buffer '0' (e.g. iOS).
lastFramebuffer framebufferNative
lastTexture textureNative
lastRenderbuffer renderbufferNative
lastViewportWidth int
lastViewportHeight int
lastBlend graphicsdriver.Blend
maxTextureSize int
maxTextureSizeOnce sync.Once
highp bool
highpOnce sync.Once
initOnce sync.Once
locationCache *locationCache
screenFramebuffer framebufferNative // This might not be the default frame buffer '0' (e.g. iOS).
lastFramebuffer framebufferNative
lastTexture textureNative
lastRenderbuffer renderbufferNative
lastViewportWidth int
lastViewportHeight int
lastBlend graphicsdriver.Blend
maxTextureSize int
maxTextureSizeOnce sync.Once
initOnce sync.Once
hasKHRParallelShaderCompile bool
hasKHRParallelShaderCompileOnce sync.Once
}
func (c *context) bindTexture(t textureNative) {
@@ -141,14 +141,14 @@ func (c *context) bindFramebuffer(f framebufferNative) {
func (c *context) setViewport(f *framebuffer) {
c.bindFramebuffer(f.native)
if c.lastViewportWidth == f.width && c.lastViewportHeight == f.height {
if c.lastViewportWidth == f.viewportWidth && c.lastViewportHeight == f.viewportHeight {
return
}
// On some environments, viewport size must be within the framebuffer size.
// e.g. Edge (#71), Chrome on GPD Pocket (#420), macOS Mojave (#691).
// Use the same size of the framebuffer here.
c.ctx.Viewport(0, 0, int32(f.width), int32(f.height))
c.ctx.Viewport(0, 0, int32(f.viewportWidth), int32(f.viewportHeight))
// glViewport must be called at least at every frame on iOS.
// As the screen framebuffer is the last render target, next SetViewport should be
@@ -157,16 +157,16 @@ func (c *context) setViewport(f *framebuffer) {
c.lastViewportWidth = 0
c.lastViewportHeight = 0
} else {
c.lastViewportWidth = f.width
c.lastViewportHeight = f.height
c.lastViewportWidth = f.viewportWidth
c.lastViewportHeight = f.viewportHeight
}
}
func (c *context) newScreenFramebuffer(width, height int) *framebuffer {
return &framebuffer{
native: c.screenFramebuffer,
width: width,
height: height,
native: c.screenFramebuffer,
viewportWidth: width,
viewportHeight: height,
}
}
@@ -319,9 +319,6 @@ func (c *context) newRenderbuffer(width, height int) (renderbufferNative, error)
}
func (c *context) deleteRenderbuffer(r renderbufferNative) {
if !c.ctx.IsRenderbuffer(uint32(r)) {
return
}
if c.lastRenderbuffer == r {
c.lastRenderbuffer = 0
}
@@ -336,19 +333,23 @@ func (c *context) newFramebuffer(texture textureNative, width, height int) (*fra
c.bindFramebuffer(framebufferNative(f))
c.ctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, uint32(texture), 0)
if s := c.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE {
if s != 0 {
return nil, fmt.Errorf("opengl: creating framebuffer failed: %v", s)
if shouldCheckFramebufferStatus() {
if s := c.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE {
if s != 0 {
return nil, fmt.Errorf("opengl: creating framebuffer failed: %v", s)
}
if e := c.ctx.GetError(); e != gl.NO_ERROR {
return nil, fmt.Errorf("opengl: creating framebuffer failed: (glGetError) %d", e)
}
return nil, fmt.Errorf("opengl: creating framebuffer failed: unknown error")
}
if e := c.ctx.GetError(); e != gl.NO_ERROR {
return nil, fmt.Errorf("opengl: creating framebuffer failed: (glGetError) %d", e)
}
return nil, fmt.Errorf("opengl: creating framebuffer failed: unknown error")
}
return &framebuffer{
native: framebufferNative(f),
width: width,
height: height,
native: framebufferNative(f),
viewportWidth: width,
viewportHeight: height,
}, nil
}
@@ -356,9 +357,13 @@ func (c *context) bindStencilBuffer(f framebufferNative, r renderbufferNative) e
c.bindFramebuffer(f)
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: glFramebufferRenderbuffer failed: %d", s))
if shouldCheckFramebufferStatus() {
if s := c.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE {
return fmt.Errorf("opengl: glFramebufferRenderbuffer failed: %d", s)
}
}
return nil
}
@@ -366,9 +371,6 @@ func (c *context) deleteFramebuffer(f framebufferNative) {
if f == c.screenFramebuffer {
return
}
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
@@ -389,10 +391,6 @@ func (c *context) newShader(shaderType uint32, source string) (shader, error) {
c.ctx.ShaderSource(s, source)
c.ctx.CompileShader(s)
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
}
@@ -411,10 +409,6 @@ func (c *context) newProgram(shaders []shader, attributes []string) (program, er
}
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)
}
return program(p), nil
}
@@ -448,6 +442,8 @@ func (c *context) uniforms(p program, location string, v []uint32, typ shaderir.
}
switch base {
case shaderir.Bool:
c.ctx.Uniform1iv(int32(l), uint32sToInt32s(v))
case shaderir.Float:
c.ctx.Uniform1fv(int32(l), uint32sToFloat32s(v))
case shaderir.Int:
@@ -496,3 +492,22 @@ func (c *context) glslVersion() glsl.GLSLVersion {
}
return glsl.GLSLVersionDefault
}
func shouldCheckFramebufferStatus() bool {
// CheckFramebufferStatus is slow and should be avoided especially in browsers.
// See https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#avoid_blocking_api_calls_in_production
//
// TODO: Should this be avoided in all environments?
return runtime.GOOS != "js"
}
func (c *context) hasParallelShaderCompile() bool {
c.hasKHRParallelShaderCompileOnce.Do(func() {
if runtime.GOOS != "js" {
return
}
ext := c.ctx.GetExtension("KHR_parallel_shader_compile")
c.hasKHRParallelShaderCompile = ext != nil
})
return c.hasKHRParallelShaderCompile
}
@@ -0,0 +1,103 @@
// Copyright 2019 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gl
import (
"syscall/js"
)
var (
object = js.Global().Get("Object")
arrayBuffer = js.Global().Get("ArrayBuffer")
uint8Array = js.Global().Get("Uint8Array")
float32Array = js.Global().Get("Float32Array")
int32Array = js.Global().Get("Int32Array")
)
var (
tmpArrayBufferByteLength = 16
// tmpArrayBuffer is a temporary buffer used at gl.readPixels or gl.texSubImage2D.
// The read data is converted to Go's byte slice as soon as possible.
// To avoid often allocating ArrayBuffer, reuse the buffer whenever possible.
tmpArrayBuffer = arrayBuffer.New(tmpArrayBufferByteLength)
// tmpUint8Array is a Uint8ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
tmpUint8Array = uint8Array.New(tmpArrayBuffer)
// tmpFloat32Array is a Float32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
tmpFloat32Array = float32Array.New(tmpArrayBuffer)
// tmpInt32Array is a Float32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
tmpInt32Array = int32Array.New(tmpArrayBuffer)
)
func ensureTemporaryArrayBufferSize(byteLength int) {
if bufl := tmpArrayBufferByteLength; bufl < byteLength {
for bufl < byteLength {
bufl *= 2
}
tmpArrayBufferByteLength = bufl
tmpArrayBuffer = arrayBuffer.New(bufl)
tmpUint8Array = uint8Array.New(tmpArrayBuffer)
tmpFloat32Array = float32Array.New(tmpArrayBuffer)
tmpInt32Array = int32Array.New(tmpArrayBuffer)
}
}
// tmpUint8ArrayFromUint8Slice returns a Uint8Array whose length is at least minLength from an uint8 slice.
// Be careful that the length can exceed the given minLength.
// data must be a slice of a numeric type for initialization, or nil if you don't need initialization.
func tmpUint8ArrayFromUint8Slice(minLength int, data []uint8) js.Value {
ensureTemporaryArrayBufferSize(minLength)
copyUint8SliceToTemporaryArrayBuffer(data)
return tmpUint8Array
}
// tmpUint8ArrayFromUint16Slice returns a Uint8Array whose length is at least minLength from an uint16 slice.
// Be careful that the length can exceed the given minLength.
// data must be a slice of a numeric type for initialization, or nil if you don't need initialization.
func tmpUint8ArrayFromUint16Slice(minLength int, data []uint16) js.Value {
ensureTemporaryArrayBufferSize(minLength * 2)
copySliceToTemporaryArrayBuffer(data)
return tmpUint8Array
}
// tmpUint8ArrayFromFloat32Slice returns a Uint8Array whose length is at least minLength from a float32 slice.
// Be careful that the length can exceed the given minLength.
// data must be a slice of a numeric type for initialization, or nil if you don't need initialization.
func tmpUint8ArrayFromFloat32Slice(minLength int, data []float32) js.Value {
ensureTemporaryArrayBufferSize(minLength * 4)
copySliceToTemporaryArrayBuffer(data)
return tmpUint8Array
}
// tmpFloat32ArrayFromFloat32Slice returns a Float32Array whose length is at least minLength.
// Be careful that the length can exceed the given minLength.
// data must be a slice of a numeric type for initialization, or nil if you don't need initialization.
func tmpFloat32ArrayFromFloat32Slice(minLength int, data []float32) js.Value {
ensureTemporaryArrayBufferSize(minLength * 4)
copySliceToTemporaryArrayBuffer(data)
return tmpFloat32Array
}
// tmpInt32ArrayFromInt32Slice returns a Int32Array whose length is at least minLength.
// Be careful that the length can exceed the given minLength.
// data must be a slice of a numeric type for initialization, or nil if you don't need initialization.
func tmpInt32ArrayFromInt32Slice(minLength int, data []int32) js.Value {
ensureTemporaryArrayBufferSize(minLength * 4)
copySliceToTemporaryArrayBuffer(data)
return tmpInt32Array
}
@@ -23,6 +23,7 @@ const (
BLEND = 0x0BE2
CLAMP_TO_EDGE = 0x812F
COLOR_ATTACHMENT0 = 0x8CE0
COMPLETION_STATUS_KHR = 0x91B1
COMPILE_STATUS = 0x8B81
DECR_WRAP = 0x8508
DEPTH24_STENCIL8 = 0x88F0
@@ -347,6 +347,15 @@ func (d *DebugContext) GetError() uint32 {
return out0
}
func (d *DebugContext) GetExtension(arg0 string) any {
out0 := d.Context.GetExtension(arg0)
fmt.Fprintln(os.Stderr, "GetExtension")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at GetExtension", e))
}
return out0
}
func (d *DebugContext) GetInteger(arg0 uint32) int {
out0 := d.Context.GetInteger(arg0)
fmt.Fprintln(os.Stderr, "GetInteger")
@@ -406,15 +415,6 @@ func (d *DebugContext) IsES() bool {
return out0
}
func (d *DebugContext) IsFramebuffer(arg0 uint32) bool {
out0 := d.Context.IsFramebuffer(arg0)
fmt.Fprintln(os.Stderr, "IsFramebuffer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at IsFramebuffer", e))
}
return out0
}
func (d *DebugContext) IsProgram(arg0 uint32) bool {
out0 := d.Context.IsProgram(arg0)
fmt.Fprintln(os.Stderr, "IsProgram")
@@ -424,15 +424,6 @@ func (d *DebugContext) IsProgram(arg0 uint32) bool {
return out0
}
func (d *DebugContext) IsRenderbuffer(arg0 uint32) bool {
out0 := d.Context.IsRenderbuffer(arg0)
fmt.Fprintln(os.Stderr, "IsRenderbuffer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at IsRenderbuffer", e))
}
return out0
}
func (d *DebugContext) LinkProgram(arg0 uint32) {
d.Context.LinkProgram(arg0)
fmt.Fprintln(os.Stderr, "LinkProgram")
@@ -2,7 +2,7 @@
// SPDX-FileCopyrightText: 2014 Eric Woroshow
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build !darwin && !js && !windows && !playstation5
//go:build nintendosdk
package gl
@@ -20,298 +20,505 @@ package gl
// typedef ptrdiff_t GLintptr;
// typedef ptrdiff_t GLsizeiptr;
//
// #cgo noescape glowActiveTexture
// #cgo nocallback glowActiveTexture
// static void glowActiveTexture(uintptr_t fnptr, GLenum texture) {
// typedef void (*fn)(GLenum texture);
// ((fn)(fnptr))(texture);
// }
//
// #cgo noescape glowAttachShader
// #cgo nocallback glowAttachShader
// static void glowAttachShader(uintptr_t fnptr, GLuint program, GLuint shader) {
// typedef void (*fn)(GLuint program, GLuint shader);
// ((fn)(fnptr))(program, shader);
// }
//
// #cgo noescape glowBindAttribLocation
// #cgo nocallback glowBindAttribLocation
// static void glowBindAttribLocation(uintptr_t fnptr, GLuint program, GLuint index, const GLchar* name) {
// typedef void (*fn)(GLuint program, GLuint index, const GLchar* name);
// ((fn)(fnptr))(program, index, name);
// }
//
// #cgo noescape glowBindBuffer
// #cgo nocallback glowBindBuffer
// static void glowBindBuffer(uintptr_t fnptr, GLenum target, GLuint buffer) {
// typedef void (*fn)(GLenum target, GLuint buffer);
// ((fn)(fnptr))(target, buffer);
// }
//
// #cgo noescape glowBindFramebuffer
// #cgo nocallback glowBindFramebuffer
// static void glowBindFramebuffer(uintptr_t fnptr, GLenum target, GLuint framebuffer) {
// typedef void (*fn)(GLenum target, GLuint framebuffer);
// ((fn)(fnptr))(target, framebuffer);
// }
//
// #cgo noescape glowBindRenderbuffer
// #cgo nocallback glowBindRenderbuffer
// static void glowBindRenderbuffer(uintptr_t fnptr, GLenum target, GLuint renderbuffer) {
// typedef void (*fn)(GLenum target, GLuint renderbuffer);
// ((fn)(fnptr))(target, renderbuffer);
// }
//
// #cgo noescape glowBindTexture
// #cgo nocallback glowBindTexture
// static void glowBindTexture(uintptr_t fnptr, GLenum target, GLuint texture) {
// typedef void (*fn)(GLenum target, GLuint texture);
// ((fn)(fnptr))(target, texture);
// }
//
// #cgo noescape glowBindVertexArray
// #cgo nocallback glowBindVertexArray
// static void glowBindVertexArray(uintptr_t fnptr, GLuint array) {
// typedef void (*fn)(GLuint array);
// ((fn)(fnptr))(array);
// }
//
// #cgo noescape glowBlendEquationSeparate
// #cgo nocallback glowBlendEquationSeparate
// static void glowBlendEquationSeparate(uintptr_t fnptr, GLenum modeRGB, GLenum modeAlpha) {
// typedef void (*fn)(GLenum modeRGB, GLenum modeAlpha);
// ((fn)(fnptr))(modeRGB, modeAlpha);
// }
//
// #cgo noescape glowBlendFuncSeparate
// #cgo nocallback glowBlendFuncSeparate
// static void glowBlendFuncSeparate(uintptr_t fnptr, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) {
// typedef void (*fn)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
// ((fn)(fnptr))(srcRGB, dstRGB, srcAlpha, dstAlpha);
// }
//
// #cgo noescape glowBufferData
// #cgo nocallback glowBufferData
// static void glowBufferData(uintptr_t fnptr, GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
// typedef void (*fn)(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
// ((fn)(fnptr))(target, size, data, usage);
// }
//
// #cgo noescape glowBufferSubData
// #cgo nocallback glowBufferSubData
// static void glowBufferSubData(uintptr_t fnptr, GLenum target, GLintptr offset, GLsizeiptr size, const void* data) {
// typedef void (*fn)(GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
// ((fn)(fnptr))(target, offset, size, data);
// }
//
// #cgo noescape glowCheckFramebufferStatus
// #cgo nocallback glowCheckFramebufferStatus
// static GLenum glowCheckFramebufferStatus(uintptr_t fnptr, GLenum target) {
// typedef GLenum (*fn)(GLenum target);
// return ((fn)(fnptr))(target);
// }
//
// #cgo noescape glowClear
// #cgo nocallback glowClear
// static void glowClear(uintptr_t fnptr, GLbitfield mask) {
// typedef void (*fn)(GLbitfield mask);
// ((fn)(fnptr))(mask);
// }
//
// #cgo noescape glowColorMask
// #cgo nocallback glowColorMask
// static void glowColorMask(uintptr_t fnptr, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {
// typedef void (*fn)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
// ((fn)(fnptr))(red, green, blue, alpha);
// }
//
// #cgo noescape glowCompileShader
// #cgo nocallback glowCompileShader
// static void glowCompileShader(uintptr_t fnptr, GLuint shader) {
// typedef void (*fn)(GLuint shader);
// ((fn)(fnptr))(shader);
// }
//
// #cgo noescape glowCreateProgram
// #cgo nocallback glowCreateProgram
// static GLuint glowCreateProgram(uintptr_t fnptr) {
// typedef GLuint (*fn)();
// return ((fn)(fnptr))();
// }
//
// #cgo noescape glowCreateShader
// #cgo nocallback glowCreateShader
// static GLuint glowCreateShader(uintptr_t fnptr, GLenum type) {
// typedef GLuint (*fn)(GLenum type);
// return ((fn)(fnptr))(type);
// }
//
// #cgo noescape glowDeleteBuffers
// #cgo nocallback glowDeleteBuffers
// static void glowDeleteBuffers(uintptr_t fnptr, GLsizei n, const GLuint* buffers) {
// typedef void (*fn)(GLsizei n, const GLuint* buffers);
// ((fn)(fnptr))(n, buffers);
// }
//
// #cgo noescape glowDeleteFramebuffers
// #cgo nocallback glowDeleteFramebuffers
// static void glowDeleteFramebuffers(uintptr_t fnptr, GLsizei n, const GLuint* framebuffers) {
// typedef void (*fn)(GLsizei n, const GLuint* framebuffers);
// ((fn)(fnptr))(n, framebuffers);
// }
//
// #cgo noescape glowDeleteProgram
// #cgo nocallback glowDeleteProgram
// static void glowDeleteProgram(uintptr_t fnptr, GLuint program) {
// typedef void (*fn)(GLuint program);
// ((fn)(fnptr))(program);
// }
//
// #cgo noescape glowDeleteRenderbuffers
// #cgo nocallback glowDeleteRenderbuffers
// static void glowDeleteRenderbuffers(uintptr_t fnptr, GLsizei n, const GLuint* renderbuffers) {
// typedef void (*fn)(GLsizei n, const GLuint* renderbuffers);
// ((fn)(fnptr))(n, renderbuffers);
// }
//
// #cgo noescape glowDeleteShader
// #cgo nocallback glowDeleteShader
// static void glowDeleteShader(uintptr_t fnptr, GLuint shader) {
// typedef void (*fn)(GLuint shader);
// ((fn)(fnptr))(shader);
// }
//
// #cgo noescape glowDeleteTextures
// #cgo nocallback glowDeleteTextures
// static void glowDeleteTextures(uintptr_t fnptr, GLsizei n, const GLuint* textures) {
// typedef void (*fn)(GLsizei n, const GLuint* textures);
// ((fn)(fnptr))(n, textures);
// }
//
// #cgo noescape glowDeleteVertexArrays
// #cgo nocallback glowDeleteVertexArrays
// static void glowDeleteVertexArrays(uintptr_t fnptr, GLsizei n, const GLuint* arrays) {
// typedef void (*fn)(GLsizei n, const GLuint* arrays);
// ((fn)(fnptr))(n, arrays);
// }
//
// #cgo noescape glowDisable
// #cgo nocallback glowDisable
// static void glowDisable(uintptr_t fnptr, GLenum cap) {
// typedef void (*fn)(GLenum cap);
// ((fn)(fnptr))(cap);
// }
//
// #cgo noescape glowDisableVertexAttribArray
// #cgo nocallback glowDisableVertexAttribArray
// static void glowDisableVertexAttribArray(uintptr_t fnptr, GLuint index) {
// typedef void (*fn)(GLuint index);
// ((fn)(fnptr))(index);
// }
//
// #cgo noescape glowDrawElements
// #cgo nocallback glowDrawElements
// static void glowDrawElements(uintptr_t fnptr, GLenum mode, GLsizei count, GLenum type, const uintptr_t indices) {
// typedef void (*fn)(GLenum mode, GLsizei count, GLenum type, const uintptr_t indices);
// ((fn)(fnptr))(mode, count, type, indices);
// }
//
// #cgo noescape glowEnable
// #cgo nocallback glowEnable
// static void glowEnable(uintptr_t fnptr, GLenum cap) {
// typedef void (*fn)(GLenum cap);
// ((fn)(fnptr))(cap);
// }
//
// #cgo noescape glowEnableVertexAttribArray
// #cgo nocallback glowEnableVertexAttribArray
// static void glowEnableVertexAttribArray(uintptr_t fnptr, GLuint index) {
// typedef void (*fn)(GLuint index);
// ((fn)(fnptr))(index);
// }
//
// #cgo noescape glowFlush
// #cgo nocallback glowFlush
// static void glowFlush(uintptr_t fnptr) {
// typedef void (*fn)();
// ((fn)(fnptr))();
// }
//
// #cgo noescape glowFramebufferRenderbuffer
// #cgo nocallback glowFramebufferRenderbuffer
// static void glowFramebufferRenderbuffer(uintptr_t fnptr, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) {
// typedef void (*fn)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
// ((fn)(fnptr))(target, attachment, renderbuffertarget, renderbuffer);
// }
//
// #cgo noescape glowFramebufferTexture2D
// #cgo nocallback glowFramebufferTexture2D
// static void glowFramebufferTexture2D(uintptr_t fnptr, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {
// typedef void (*fn)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
// ((fn)(fnptr))(target, attachment, textarget, texture, level);
// }
//
// #cgo noescape glowGenBuffers
// #cgo nocallback glowGenBuffers
// static void glowGenBuffers(uintptr_t fnptr, GLsizei n, GLuint* buffers) {
// typedef void (*fn)(GLsizei n, GLuint* buffers);
// ((fn)(fnptr))(n, buffers);
// }
//
// #cgo noescape glowGenFramebuffers
// #cgo nocallback glowGenFramebuffers
// static void glowGenFramebuffers(uintptr_t fnptr, GLsizei n, GLuint* framebuffers) {
// typedef void (*fn)(GLsizei n, GLuint* framebuffers);
// ((fn)(fnptr))(n, framebuffers);
// }
//
// #cgo noescape glowGenRenderbuffers
// #cgo nocallback glowGenRenderbuffers
// static void glowGenRenderbuffers(uintptr_t fnptr, GLsizei n, GLuint* renderbuffers) {
// typedef void (*fn)(GLsizei n, GLuint* renderbuffers);
// ((fn)(fnptr))(n, renderbuffers);
// }
//
// #cgo noescape glowGenTextures
// #cgo nocallback glowGenTextures
// static void glowGenTextures(uintptr_t fnptr, GLsizei n, GLuint* textures) {
// typedef void (*fn)(GLsizei n, GLuint* textures);
// ((fn)(fnptr))(n, textures);
// }
//
// #cgo noescape glowGenVertexArrays
// #cgo nocallback glowGenVertexArrays
// static void glowGenVertexArrays(uintptr_t fnptr, GLsizei n, GLuint* arrays) {
// typedef void (*fn)(GLsizei n, GLuint* arrays);
// ((fn)(fnptr))(n, arrays);
// }
//
// #cgo noescape glowGetError
// #cgo nocallback glowGetError
// static GLenum glowGetError(uintptr_t fnptr) {
// typedef GLenum (*fn)();
// return ((fn)(fnptr))();
// }
//
// #cgo noescape glowGetIntegerv
// #cgo nocallback glowGetIntegerv
// static void glowGetIntegerv(uintptr_t fnptr, GLenum pname, GLint* data) {
// typedef void (*fn)(GLenum pname, GLint* data);
// ((fn)(fnptr))(pname, data);
// }
//
// #cgo noescape glowGetProgramInfoLog
// #cgo nocallback glowGetProgramInfoLog
// static void glowGetProgramInfoLog(uintptr_t fnptr, GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog) {
// typedef void (*fn)(GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
// ((fn)(fnptr))(program, bufSize, length, infoLog);
// }
//
// #cgo noescape glowGetProgramiv
// #cgo nocallback glowGetProgramiv
// static void glowGetProgramiv(uintptr_t fnptr, GLuint program, GLenum pname, GLint* params) {
// typedef void (*fn)(GLuint program, GLenum pname, GLint* params);
// ((fn)(fnptr))(program, pname, params);
// }
//
// #cgo noescape glowGetShaderInfoLog
// #cgo nocallback glowGetShaderInfoLog
// static void glowGetShaderInfoLog(uintptr_t fnptr, GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog) {
// typedef void (*fn)(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
// ((fn)(fnptr))(shader, bufSize, length, infoLog);
// }
//
// #cgo noescape glowGetShaderiv
// #cgo nocallback glowGetShaderiv
// static void glowGetShaderiv(uintptr_t fnptr, GLuint shader, GLenum pname, GLint* params) {
// typedef void (*fn)(GLuint shader, GLenum pname, GLint* params);
// ((fn)(fnptr))(shader, pname, params);
// }
//
// #cgo noescape glowGetUniformLocation
// #cgo nocallback glowGetUniformLocation
// static GLint glowGetUniformLocation(uintptr_t fnptr, GLuint program, const GLchar* name) {
// typedef GLint (*fn)(GLuint program, const GLchar* name);
// return ((fn)(fnptr))(program, name);
// }
// static GLboolean glowIsFramebuffer(uintptr_t fnptr, GLuint framebuffer) {
// typedef GLboolean (*fn)(GLuint framebuffer);
// return ((fn)(fnptr))(framebuffer);
// }
//
// #cgo noescape glowIsProgram
// #cgo nocallback glowIsProgram
// static GLboolean glowIsProgram(uintptr_t fnptr, GLuint program) {
// typedef GLboolean (*fn)(GLuint program);
// return ((fn)(fnptr))(program);
// }
// static GLboolean glowIsRenderbuffer(uintptr_t fnptr, GLuint renderbuffer) {
// typedef GLboolean (*fn)(GLuint renderbuffer);
// return ((fn)(fnptr))(renderbuffer);
// }
//
// #cgo noescape glowLinkProgram
// #cgo nocallback glowLinkProgram
// static void glowLinkProgram(uintptr_t fnptr, GLuint program) {
// typedef void (*fn)(GLuint program);
// ((fn)(fnptr))(program);
// }
//
// #cgo noescape glowPixelStorei
// #cgo nocallback glowPixelStorei
// static void glowPixelStorei(uintptr_t fnptr, GLenum pname, GLint param) {
// typedef void (*fn)(GLenum pname, GLint param);
// ((fn)(fnptr))(pname, param);
// }
//
// #cgo noescape glowReadPixels
// #cgo nocallback glowReadPixels
// static void glowReadPixels(uintptr_t fnptr, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {
// typedef void (*fn)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels);
// ((fn)(fnptr))(x, y, width, height, format, type, pixels);
// }
//
// #cgo noescape glowRenderbufferStorage
// #cgo nocallback glowRenderbufferStorage
// static void glowRenderbufferStorage(uintptr_t fnptr, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
// typedef void (*fn)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
// ((fn)(fnptr))(target, internalformat, width, height);
// }
//
// #cgo noescape glowScissor
// #cgo nocallback glowScissor
// static void glowScissor(uintptr_t fnptr, GLint x, GLint y, GLsizei width, GLsizei height) {
// typedef void (*fn)(GLint x, GLint y, GLsizei width, GLsizei height);
// ((fn)(fnptr))(x, y, width, height);
// }
//
// #cgo noescape glowShaderSource
// #cgo nocallback glowShaderSource
// static void glowShaderSource(uintptr_t fnptr, GLuint shader, GLsizei count, const GLchar*const* string, const GLint* length) {
// typedef void (*fn)(GLuint shader, GLsizei count, const GLchar*const* string, const GLint* length);
// ((fn)(fnptr))(shader, count, string, length);
// }
//
// #cgo noescape glowStencilFunc
// #cgo nocallback glowStencilFunc
// static void glowStencilFunc(uintptr_t fnptr, GLenum func, GLint ref, GLuint mask) {
// typedef void (*fn)(GLenum func, GLint ref, GLuint mask);
// ((fn)(fnptr))(func, ref, mask);
// }
//
// #cgo noescape glowStencilOpSeparate
// #cgo nocallback glowStencilOpSeparate
// static void glowStencilOpSeparate(uintptr_t fnptr, GLenum face, GLenum fail, GLenum zfail, GLenum zpass) {
// typedef void (*fn)(GLenum face, GLenum fail, GLenum zfail, GLenum zpass);
// ((fn)(fnptr))(face, fail, zfail, zpass);
// }
//
// #cgo noescape glowTexImage2D
// #cgo nocallback glowTexImage2D
// static void glowTexImage2D(uintptr_t fnptr, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels) {
// typedef void (*fn)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels);
// ((fn)(fnptr))(target, level, internalformat, width, height, border, format, type, pixels);
// }
//
// #cgo noescape glowTexParameteri
// #cgo nocallback glowTexParameteri
// static void glowTexParameteri(uintptr_t fnptr, GLenum target, GLenum pname, GLint param) {
// typedef void (*fn)(GLenum target, GLenum pname, GLint param);
// ((fn)(fnptr))(target, pname, param);
// }
//
// #cgo noescape glowTexSubImage2D
// #cgo nocallback glowTexSubImage2D
// static void glowTexSubImage2D(uintptr_t fnptr, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) {
// typedef void (*fn)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels);
// ((fn)(fnptr))(target, level, xoffset, yoffset, width, height, format, type, pixels);
// }
//
// #cgo noescape glowUniform1fv
// #cgo nocallback glowUniform1fv
// static void glowUniform1fv(uintptr_t fnptr, GLint location, GLsizei count, const GLfloat* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLfloat* value);
// ((fn)(fnptr))(location, count, value);
// }
//
// #cgo noescape glowUniform1i
// #cgo nocallback glowUniform1i
// static void glowUniform1i(uintptr_t fnptr, GLint location, GLint v0) {
// typedef void (*fn)(GLint location, GLint v0);
// ((fn)(fnptr))(location, v0);
// }
//
// #cgo noescape glowUniform1iv
// #cgo nocallback glowUniform1iv
// static void glowUniform1iv(uintptr_t fnptr, GLint location, GLsizei count, const GLint* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLint* value);
// ((fn)(fnptr))(location, count, value);
// }
//
// #cgo noescape glowUniform2fv
// #cgo nocallback glowUniform2fv
// static void glowUniform2fv(uintptr_t fnptr, GLint location, GLsizei count, const GLfloat* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLfloat* value);
// ((fn)(fnptr))(location, count, value);
// }
//
// #cgo noescape glowUniform2iv
// #cgo nocallback glowUniform2iv
// static void glowUniform2iv(uintptr_t fnptr, GLint location, GLsizei count, const GLint* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLint* value);
// ((fn)(fnptr))(location, count, value);
// }
//
// #cgo noescape glowUniform3fv
// #cgo nocallback glowUniform3fv
// static void glowUniform3fv(uintptr_t fnptr, GLint location, GLsizei count, const GLfloat* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLfloat* value);
// ((fn)(fnptr))(location, count, value);
// }
//
// #cgo noescape glowUniform3iv
// #cgo nocallback glowUniform3iv
// static void glowUniform3iv(uintptr_t fnptr, GLint location, GLsizei count, const GLint* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLint* value);
// ((fn)(fnptr))(location, count, value);
// }
//
// #cgo noescape glowUniform4fv
// #cgo nocallback glowUniform4fv
// static void glowUniform4fv(uintptr_t fnptr, GLint location, GLsizei count, const GLfloat* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLfloat* value);
// ((fn)(fnptr))(location, count, value);
// }
//
// #cgo noescape glowUniform4iv
// #cgo nocallback glowUniform4iv
// static void glowUniform4iv(uintptr_t fnptr, GLint location, GLsizei count, const GLint* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLint* value);
// ((fn)(fnptr))(location, count, value);
// }
//
// #cgo noescape glowUniformMatrix2fv
// #cgo nocallback glowUniformMatrix2fv
// static void glowUniformMatrix2fv(uintptr_t fnptr, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
// typedef void (*fn)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
// ((fn)(fnptr))(location, count, transpose, value);
// }
//
// #cgo noescape glowUniformMatrix3fv
// #cgo nocallback glowUniformMatrix3fv
// static void glowUniformMatrix3fv(uintptr_t fnptr, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
// typedef void (*fn)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
// ((fn)(fnptr))(location, count, transpose, value);
// }
//
// #cgo noescape glowUniformMatrix4fv
// #cgo nocallback glowUniformMatrix4fv
// static void glowUniformMatrix4fv(uintptr_t fnptr, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
// typedef void (*fn)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
// ((fn)(fnptr))(location, count, transpose, value);
// }
//
// #cgo noescape glowUseProgram
// #cgo nocallback glowUseProgram
// static void glowUseProgram(uintptr_t fnptr, GLuint program) {
// typedef void (*fn)(GLuint program);
// ((fn)(fnptr))(program);
// }
//
// #cgo noescape glowVertexAttribPointer
// #cgo nocallback glowVertexAttribPointer
// static void glowVertexAttribPointer(uintptr_t fnptr, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const uintptr_t pointer) {
// typedef void (*fn)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const uintptr_t pointer);
// ((fn)(fnptr))(index, size, type, normalized, stride, pointer);
// }
//
// #cgo noescape glowViewport
// #cgo nocallback glowViewport
// static void glowViewport(uintptr_t fnptr, GLint x, GLint y, GLsizei width, GLsizei height) {
// typedef void (*fn)(GLint x, GLint y, GLsizei width, GLsizei height);
// ((fn)(fnptr))(x, y, width, height);
@@ -369,9 +576,7 @@ type defaultContext struct {
gpGetShaderInfoLog C.uintptr_t
gpGetShaderiv C.uintptr_t
gpGetUniformLocation C.uintptr_t
gpIsFramebuffer C.uintptr_t
gpIsProgram C.uintptr_t
gpIsRenderbuffer C.uintptr_t
gpLinkProgram C.uintptr_t
gpPixelStorei C.uintptr_t
gpReadPixels C.uintptr_t
@@ -594,6 +799,10 @@ func (c *defaultContext) GetError() uint32 {
return uint32(ret)
}
func (c *defaultContext) GetExtension(name string) any {
return nil
}
func (c *defaultContext) GetInteger(pname uint32) int {
var dst int32
C.glowGetIntegerv(c.gpGetIntegerv, C.GLenum(pname), (*C.GLint)(unsafe.Pointer(&dst)))
@@ -602,6 +811,9 @@ func (c *defaultContext) GetInteger(pname uint32) int {
func (c *defaultContext) GetProgramInfoLog(program uint32) string {
bufSize := c.GetProgrami(program, INFO_LOG_LENGTH)
if bufSize == 0 {
return ""
}
infoLog := make([]byte, bufSize)
C.glowGetProgramInfoLog(c.gpGetProgramInfoLog, C.GLuint(program), C.GLsizei(bufSize), nil, (*C.GLchar)(unsafe.Pointer(&infoLog[0])))
return string(infoLog)
@@ -615,6 +827,9 @@ func (c *defaultContext) GetProgrami(program uint32, pname uint32) int {
func (c *defaultContext) GetShaderInfoLog(shader uint32) string {
bufSize := c.GetShaderi(shader, INFO_LOG_LENGTH)
if bufSize == 0 {
return ""
}
infoLog := make([]byte, bufSize)
C.glowGetShaderInfoLog(c.gpGetShaderInfoLog, C.GLuint(shader), C.GLsizei(bufSize), nil, (*C.GLchar)(unsafe.Pointer(&infoLog[0])))
return string(infoLog)
@@ -633,21 +848,11 @@ func (c *defaultContext) GetUniformLocation(program uint32, name string) int32 {
return int32(ret)
}
func (c *defaultContext) IsFramebuffer(framebuffer uint32) bool {
ret := C.glowIsFramebuffer(c.gpIsFramebuffer, C.GLuint(framebuffer))
return ret == TRUE
}
func (c *defaultContext) IsProgram(program uint32) bool {
ret := C.glowIsProgram(c.gpIsProgram, C.GLuint(program))
return ret == TRUE
}
func (c *defaultContext) IsRenderbuffer(renderbuffer uint32) bool {
ret := C.glowIsRenderbuffer(c.gpIsRenderbuffer, C.GLuint(renderbuffer))
return ret == TRUE
}
func (c *defaultContext) LinkProgram(program uint32) {
C.glowLinkProgram(c.gpLinkProgram, C.GLuint(program))
}
@@ -819,9 +1024,7 @@ func (c *defaultContext) LoadFunctions() error {
c.gpGetShaderInfoLog = C.uintptr_t(g.get("glGetShaderInfoLog"))
c.gpGetShaderiv = C.uintptr_t(g.get("glGetShaderiv"))
c.gpGetUniformLocation = C.uintptr_t(g.get("glGetUniformLocation"))
c.gpIsFramebuffer = C.uintptr_t(g.get("glIsFramebuffer"))
c.gpIsProgram = C.uintptr_t(g.get("glIsProgram"))
c.gpIsRenderbuffer = C.uintptr_t(g.get("glIsRenderbuffer"))
c.gpLinkProgram = C.uintptr_t(g.get("glLinkProgram"))
c.gpPixelStorei = C.uintptr_t(g.get("glPixelStorei"))
c.gpReadPixels = C.uintptr_t(g.get("glReadPixels"))
@@ -17,8 +17,6 @@ package gl
import (
"fmt"
"syscall/js"
"github.com/hajimehoshi/ebiten/v2/internal/jsutil"
)
type defaultContext struct {
@@ -61,15 +59,14 @@ type defaultContext struct {
fnFramebufferTexture2D js.Value
fnFlush js.Value
fnGetError js.Value
fnGetExtension 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
fnLinkProgram js.Value
fnPixelStorei js.Value
fnReadPixels js.Value
@@ -191,15 +188,14 @@ func NewDefaultContext(v js.Value) (Context, error) {
fnFramebufferTexture2D: v.Get("framebufferTexture2D").Call("bind", v),
fnFlush: v.Get("flush").Call("bind", v),
fnGetError: v.Get("getError").Call("bind", v),
fnGetExtension: v.Get("getExtension").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),
fnLinkProgram: v.Get("linkProgram").Call("bind", v),
fnPixelStorei: v.Get("pixelStorei").Call("bind", v),
fnReadPixels: v.Get("readPixels").Call("bind", v),
@@ -292,7 +288,7 @@ func (c *defaultContext) BufferInit(target uint32, size int, usage uint32) {
func (c *defaultContext) BufferSubData(target uint32, offset int, data []byte) {
l := len(data)
arr := jsutil.TemporaryUint8ArrayFromUint8Slice(l, data)
arr := tmpUint8ArrayFromUint8Slice(l, data)
c.fnBufferSubData.Invoke(target, offset, arr, 0, l)
}
@@ -373,7 +369,7 @@ func (c *defaultContext) DeleteTexture(texture uint32) {
func (c *defaultContext) DeleteVertexArray(array uint32) {
c.fnDeleteVertexArray.Invoke(c.vertexArrays.get(array))
c.textures.delete(array)
c.vertexArrays.delete(array)
}
func (c *defaultContext) Disable(cap uint32) {
@@ -412,6 +408,14 @@ func (c *defaultContext) GetError() uint32 {
return uint32(c.fnGetError.Invoke().Int())
}
func (c *defaultContext) GetExtension(name string) any {
ext := c.fnGetExtension.Invoke(name)
if ext.IsNull() || ext.IsUndefined() {
return nil
}
return ext
}
func (c *defaultContext) GetInteger(pname uint32) int {
ret := c.fnGetParameter.Invoke(pname)
switch pname {
@@ -481,18 +485,10 @@ func (c *defaultContext) GetUniformLocation(program uint32, name string) int32 {
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) LinkProgram(program uint32) {
c.fnLinkProgram.Invoke(c.programs.get(program))
}
@@ -506,7 +502,7 @@ func (c *defaultContext) ReadPixels(dst []byte, x int32, y int32, width int32, h
c.fnReadPixels.Invoke(x, y, width, height, format, xtype, 0)
return
}
p := jsutil.TemporaryUint8ArrayFromUint8Slice(len(dst), nil)
p := tmpUint8ArrayFromUint8Slice(len(dst), nil)
c.fnReadPixels.Invoke(x, y, width, height, format, xtype, p)
js.CopyBytesToGo(dst, p)
}
@@ -543,7 +539,7 @@ func (c *defaultContext) TexParameteri(target uint32, pname uint32, param int32)
}
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)
arr := tmpUint8ArrayFromUint8Slice(len(pixels), pixels)
// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
// GLsizei width, GLsizei height,
// GLenum format, GLenum type, ArrayBufferView pixels, srcOffset);
@@ -552,7 +548,7 @@ func (c *defaultContext) TexSubImage2D(target uint32, level int32, xoffset int32
func (c *defaultContext) Uniform1fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
arr := tmpFloat32ArrayFromFloat32Slice(len(value), value)
c.fnUniform1fv.Invoke(l, arr, 0, len(value))
}
@@ -563,61 +559,61 @@ func (c *defaultContext) Uniform1i(location int32, v0 int32) {
func (c *defaultContext) Uniform1iv(location int32, value []int32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryInt32Array(len(value), value)
arr := tmpInt32ArrayFromInt32Slice(len(value), value)
c.fnUniform1iv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) Uniform2fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
arr := tmpFloat32ArrayFromFloat32Slice(len(value), value)
c.fnUniform2fv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) Uniform2iv(location int32, value []int32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryInt32Array(len(value), value)
arr := tmpInt32ArrayFromInt32Slice(len(value), value)
c.fnUniform2iv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) Uniform3fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
arr := tmpFloat32ArrayFromFloat32Slice(len(value), value)
c.fnUniform3fv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) Uniform3iv(location int32, value []int32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryInt32Array(len(value), value)
arr := tmpInt32ArrayFromInt32Slice(len(value), value)
c.fnUniform3iv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) Uniform4fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
arr := tmpFloat32ArrayFromFloat32Slice(len(value), value)
c.fnUniform4fv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) Uniform4iv(location int32, value []int32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryInt32Array(len(value), value)
arr := tmpInt32ArrayFromInt32Slice(len(value), value)
c.fnUniform4iv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) UniformMatrix2fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
arr := tmpFloat32ArrayFromFloat32Slice(len(value), value)
c.fnUniformMatrix2fv.Invoke(l, false, arr, 0, len(value))
}
func (c *defaultContext) UniformMatrix3fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
arr := tmpFloat32ArrayFromFloat32Slice(len(value), value)
c.fnUniformMatrix3fv.Invoke(l, false, arr, 0, len(value))
}
func (c *defaultContext) UniformMatrix4fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
arr := tmpFloat32ArrayFromFloat32Slice(len(value), value)
c.fnUniformMatrix4fv.Invoke(l, false, arr, 0, len(value))
}
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build darwin || windows
//go:build (darwin || freebsd || linux || netbsd || openbsd || windows) && !nintendosdk && !playstation5
package gl
@@ -69,9 +69,7 @@ type defaultContext struct {
gpGetShaderInfoLog uintptr
gpGetShaderiv uintptr
gpGetUniformLocation uintptr
gpIsFramebuffer uintptr
gpIsProgram uintptr
gpIsRenderbuffer uintptr
gpLinkProgram uintptr
gpPixelStorei uintptr
gpReadPixels uintptr
@@ -294,6 +292,10 @@ func (c *defaultContext) GetError() uint32 {
return uint32(ret)
}
func (c *defaultContext) GetExtension(name string) any {
return nil
}
func (c *defaultContext) GetInteger(pname uint32) int {
var dst int32
purego.SyscallN(c.gpGetIntegerv, uintptr(pname), uintptr(unsafe.Pointer(&dst)))
@@ -302,6 +304,9 @@ func (c *defaultContext) GetInteger(pname uint32) int {
func (c *defaultContext) GetProgramInfoLog(program uint32) string {
bufSize := c.GetProgrami(program, INFO_LOG_LENGTH)
if bufSize == 0 {
return ""
}
infoLog := make([]byte, bufSize)
purego.SyscallN(c.gpGetProgramInfoLog, uintptr(program), uintptr(bufSize), 0, uintptr(unsafe.Pointer(&infoLog[0])))
return string(infoLog)
@@ -315,6 +320,9 @@ func (c *defaultContext) GetProgrami(program uint32, pname uint32) int {
func (c *defaultContext) GetShaderInfoLog(shader uint32) string {
bufSize := c.GetShaderi(shader, INFO_LOG_LENGTH)
if bufSize == 0 {
return ""
}
infoLog := make([]byte, bufSize)
purego.SyscallN(c.gpGetShaderInfoLog, uintptr(shader), uintptr(bufSize), 0, uintptr(unsafe.Pointer(&infoLog[0])))
return string(infoLog)
@@ -333,21 +341,11 @@ func (c *defaultContext) GetUniformLocation(program uint32, name string) int32 {
return int32(ret)
}
func (c *defaultContext) IsFramebuffer(framebuffer uint32) bool {
ret, _, _ := purego.SyscallN(c.gpIsFramebuffer, uintptr(framebuffer))
return byte(ret) != 0
}
func (c *defaultContext) IsProgram(program uint32) bool {
ret, _, _ := purego.SyscallN(c.gpIsProgram, uintptr(program))
return byte(ret) != 0
}
func (c *defaultContext) IsRenderbuffer(renderbuffer uint32) bool {
ret, _, _ := purego.SyscallN(c.gpIsRenderbuffer, uintptr(renderbuffer))
return byte(ret) != 0
}
func (c *defaultContext) LinkProgram(program uint32) {
purego.SyscallN(c.gpLinkProgram, uintptr(program))
}
@@ -519,9 +517,7 @@ func (c *defaultContext) LoadFunctions() error {
c.gpGetShaderInfoLog = g.get("glGetShaderInfoLog")
c.gpGetShaderiv = g.get("glGetShaderiv")
c.gpGetUniformLocation = g.get("glGetUniformLocation")
c.gpIsFramebuffer = g.get("glIsFramebuffer")
c.gpIsProgram = g.get("glIsProgram")
c.gpIsRenderbuffer = g.get("glIsRenderbuffer")
c.gpLinkProgram = g.get("glLinkProgram")
c.gpPixelStorei = g.get("glPixelStorei")
c.gpReadPixels = g.get("glReadPixels")
@@ -66,15 +66,14 @@ type Context interface {
FramebufferRenderbuffer(target uint32, attachment uint32, renderbuffertarget uint32, renderbuffer uint32)
FramebufferTexture2D(target uint32, attachment uint32, textarget uint32, texture uint32, level int32)
GetError() uint32
GetExtension(name string) any
GetInteger(pname uint32) int
GetProgramInfoLog(program uint32) string
GetProgrami(program uint32, pname uint32) int
GetShaderInfoLog(shader uint32) string
GetShaderi(shader uint32, pname uint32) int
GetUniformLocation(program uint32, name string) int32
IsFramebuffer(framebuffer uint32) bool
IsProgram(program uint32) bool
IsRenderbuffer(renderbuffer 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)
@@ -38,8 +38,7 @@ func (c *defaultContext) init() error {
return nil
}
// TODO: Use multiple %w-s as of Go 1.20
return fmt.Errorf("gl: failed to load: OpenGL.framework: %v, OpenGLES.framework: %v", errGL, errGLES)
return fmt.Errorf("gl: failed to load: OpenGL.framework: %w, OpenGLES.framework: %w", errGL, errGLES)
}
func (c *defaultContext) getProcAddress(name string) (uintptr, error) {
@@ -16,99 +16,84 @@
package gl
// #cgo LDFLAGS: -ldl
//
// #include <dlfcn.h>
// #include <stdlib.h>
//
// static void* getProcAddressGL(void* libGL, const char* name) {
// static void*(*glXGetProcAddress)(const char*);
// if (!glXGetProcAddress) {
// glXGetProcAddress = dlsym(libGL, "glXGetProcAddress");
// if (!glXGetProcAddress) {
// glXGetProcAddress = dlsym(libGL, "glXGetProcAddressARB");
// }
// }
// return glXGetProcAddress(name);
// }
//
// static void* getProcAddressGLES(void* libGLES, const char* name) {
// return dlsym(libGLES, name);
// }
import "C"
import (
"errors"
"fmt"
"os"
"runtime"
"strings"
"unsafe"
"github.com/ebitengine/purego"
)
var (
libGL unsafe.Pointer
libGLES unsafe.Pointer
libGL uintptr
libGLES uintptr
)
func (c *defaultContext) init() error {
var preferES bool
if runtime.GOOS == "android" {
preferES = true
}
if !preferES {
for _, t := range strings.Split(os.Getenv("EBITENGINE_OPENGL"), ",") {
switch strings.TrimSpace(t) {
case "es":
preferES = true
break
}
}
}
var errs []error
// Try OpenGL first. OpenGL is preferable as this doesn't cause context losses.
if !preferES {
// Usually libGL.so or libGL.so.1 is used. libGL.so.2 might exist only on NetBSD.
for _, name := range []string{"libGL.so", "libGL.so.2", "libGL.so.1", "libGL.so.0"} {
cname := C.CString(name)
lib := C.dlopen(cname, C.RTLD_LAZY|C.RTLD_GLOBAL)
C.free(unsafe.Pointer(cname))
if lib != nil {
libGL = lib
// Try OpenGL ES first. Some machines like Android and Raspberry Pi might work only with OpenGL ES.
//
// Do not use OpenGL ES for Steam, as overlays might not work properly (#3338).
// With Steam, OpenGL (not ES) should be available anyway.
if os.Getenv("SteamEnv") != "1" {
for _, name := range []string{"libGLESv2.so", "libGLESv2.so.2", "libGLESv2.so.1", "libGLESv2.so.0"} {
lib, err := purego.Dlopen(name, purego.RTLD_LAZY|purego.RTLD_GLOBAL)
if err == nil {
libGLES = lib
c.isES = true
return nil
}
errs = append(errs, fmt.Errorf("gl: Dlopen failed: name: %s: %w", name, err))
}
}
// Try OpenGL ES.
for _, name := range []string{"libGLESv2.so", "libGLESv2.so.2", "libGLESv2.so.1", "libGLESv2.so.0"} {
cname := C.CString(name)
lib := C.dlopen(cname, C.RTLD_LAZY|C.RTLD_GLOBAL)
C.free(unsafe.Pointer(cname))
if lib != nil {
libGLES = lib
c.isES = true
// Try OpenGL next.
// Usually libGL.so or libGL.so.1 is used. libGL.so.2 might exist only on NetBSD.
// TODO: Should "libOpenGL.so.0" [1] and "libGLX.so.0" [2] be added? These were added as of GLFW 3.3.9.
// [1] https://github.com/glfw/glfw/commit/55aad3c37b67f17279378db52da0a3ab81bbf26d
// [2] https://github.com/glfw/glfw/commit/c18851f52ec9704eb06464058a600845ec1eada1
for _, name := range []string{"libGL.so", "libGL.so.2", "libGL.so.1", "libGL.so.0"} {
lib, err := purego.Dlopen(name, purego.RTLD_LAZY|purego.RTLD_GLOBAL)
if err == nil {
libGL = lib
return nil
}
errs = append(errs, fmt.Errorf("gl: Dlopen failed: name: %s: %w", name, err))
}
return fmt.Errorf("gl: failed to load libGL.so and libGLESv2.so")
errs = append([]error{fmt.Errorf("gl: failed to load libGL.so and libGLESv2.so: ")}, errs...)
return errors.Join(errs...)
}
func (c *defaultContext) getProcAddress(name string) (uintptr, error) {
if c.isES {
return getProcAddressGLES(name), nil
return getProcAddressGLES(name)
}
return getProcAddressGL(name), nil
return getProcAddressGL(name)
}
func getProcAddressGL(name string) uintptr {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
return uintptr(C.getProcAddressGL(libGL, cname))
var glXGetProcAddress func(name string) uintptr
func getProcAddressGL(name string) (uintptr, error) {
if glXGetProcAddress == nil {
if _, err := purego.Dlsym(libGL, "glXGetProcAddress"); err == nil {
purego.RegisterLibFunc(&glXGetProcAddress, libGL, "glXGetProcAddress")
} else if _, err := purego.Dlsym(libGL, "glXGetProcAddressARB"); err == nil {
purego.RegisterLibFunc(&glXGetProcAddress, libGL, "glXGetProcAddressARB")
}
}
if glXGetProcAddress == nil {
return 0, fmt.Errorf("gl: failed to find glXGetProcAddress or glXGetProcAddressARB in libGL.so")
}
return glXGetProcAddress(name), nil
}
func getProcAddressGLES(name string) uintptr {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
return uintptr(C.getProcAddressGLES(libGLES, cname))
func getProcAddressGLES(name string) (uintptr, error) {
proc, err := purego.Dlsym(libGLES, name)
if err != nil {
return 0, err
}
return proc, nil
}
@@ -21,6 +21,8 @@ package gl
// #include <stdlib.h>
// #include <EGL/egl.h>
//
// #cgo noescape getProcAddress
// #cgo nocallback getProcAddress
// static void* getProcAddress(const char* name) {
// return eglGetProcAddress(name);
// }
@@ -0,0 +1,40 @@
// Copyright 2019 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gl
import (
"runtime"
"syscall/js"
"unsafe"
)
func copyUint8SliceToTemporaryArrayBuffer(src []uint8) {
if len(src) == 0 {
return
}
js.CopyBytesToJS(tmpUint8Array, src)
}
type numeric interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64
}
func copySliceToTemporaryArrayBuffer[T numeric](src []T) {
if len(src) == 0 {
return
}
js.CopyBytesToJS(tmpUint8Array, unsafe.Slice((*byte)(unsafe.Pointer(&src[0])), len(src)*int(unsafe.Sizeof(T(0)))))
runtime.KeepAlive(src)
}
@@ -198,7 +198,7 @@ func (g *Graphics) uniformVariableName(idx int) string {
return name
}
func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderSrcImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
if shaderID == graphicsdriver.InvalidShaderID {
return fmt.Errorf("opengl: shader ID is invalid")
}
@@ -224,7 +224,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
var idx int
for i, typ := range shader.ir.Uniforms {
n := typ.Uint32Count()
n := typ.DwordCount()
g.uniformVars[i].name = g.uniformVariableName(i)
g.uniformVars[i].value = uniforms[idx : idx+n]
g.uniformVars[i].typ = typ
@@ -241,7 +241,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
g.uniformVars[idx].value[13] ^= 1 << 31
}
var imgs [graphics.ShaderImageCount]textureVariable
var imgs [graphics.ShaderSrcImageCount]textureVariable
for i, srcID := range srcIDs {
if srcID == graphicsdriver.InvalidImageID {
continue
@@ -259,7 +259,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
}
g.uniformVars = g.uniformVars[:0]
if fillRule != graphicsdriver.FillAll {
if fillRule != graphicsdriver.FillRuleFillAll {
if err := destination.ensureStencilBuffer(); err != nil {
return err
}
@@ -274,14 +274,14 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
int32(dstRegion.Region.Dy()),
)
switch fillRule {
case graphicsdriver.NonZero:
case graphicsdriver.FillRuleNonZero:
g.context.ctx.Clear(gl.STENCIL_BUFFER_BIT)
g.context.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff)
g.context.ctx.StencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.INCR_WRAP)
g.context.ctx.StencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.DECR_WRAP)
g.context.ctx.ColorMask(false, false, false, false)
g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0))))
case graphicsdriver.EvenOdd:
case graphicsdriver.FillRuleEvenOdd:
g.context.ctx.Clear(gl.STENCIL_BUFFER_BIT)
g.context.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff)
g.context.ctx.StencilOpSeparate(gl.FRONT_AND_BACK, gl.KEEP, gl.KEEP, gl.INVERT)
@@ -289,7 +289,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0))))
}
if fillRule != graphicsdriver.FillAll {
if fillRule != graphicsdriver.FillRuleFillAll {
g.context.ctx.StencilFunc(gl.NOTEQUAL, 0x00, 0xff)
g.context.ctx.StencilOpSeparate(gl.FRONT_AND_BACK, gl.KEEP, gl.KEEP, gl.KEEP)
g.context.ctx.ColorMask(true, true, true, true)
@@ -298,7 +298,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
indexOffset += dstRegion.IndexCount
}
if fillRule != graphicsdriver.FillAll {
if fillRule != graphicsdriver.FillRuleFillAll {
g.context.ctx.Disable(gl.STENCIL_TEST)
}
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build ebitenginegldebug
//go:build !playstation5 && ebitenginegldebug
package opengl
@@ -27,7 +27,7 @@ type graphicsPlatform struct {
// NewGraphics creates an implementation of graphicsdriver.Graphics for OpenGL.
// The returned graphics value is nil iff the error is not nil.
func NewGraphics(canvas js.Value) (graphicsdriver.Graphics, error) {
func NewGraphics(canvas js.Value, colorSpace graphicsdriver.ColorSpace) (graphicsdriver.Graphics, error) {
var glContext js.Value
attr := js.Global().Get("Object").New()
@@ -41,6 +41,13 @@ func NewGraphics(canvas js.Value) (graphicsdriver.Graphics, error) {
return nil, fmt.Errorf("opengl: getContext for webgl2 failed")
}
switch colorSpace {
case graphicsdriver.ColorSpaceSRGB:
glContext.Set("drawingBufferColorSpace", "srgb")
case graphicsdriver.ColorSpaceDisplayP3:
glContext.Set("drawingBufferColorSpace", "display-p3")
}
ctx, err := gl.NewDefaultContext(glContext)
if err != nil {
return nil, err
@@ -12,20 +12,61 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !android && !ios && !js && !nintendosdk && !playstation5
//go:build (freebsd || linux || netbsd || openbsd) && !android && !nintendosdk && !playstation5
package opengl
import (
"fmt"
"runtime"
"bufio"
"bytes"
"os/exec"
"strings"
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
"github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk"
)
func isGLXExtensionForGL2Available() bool {
var buf bytes.Buffer
cmd := exec.Command("glxinfo")
cmd.Stdout = &buf
if err := cmd.Run(); err != nil {
return false
}
const (
indent = " "
ext = "GLX_EXT_create_context_es2_profile"
)
var listingExtensions bool
s := bufio.NewScanner(&buf)
for s.Scan() {
line := s.Text()
if !listingExtensions {
if line == "GLX extensions:" {
listingExtensions = true
}
continue
}
if !strings.HasPrefix(line, indent) {
listingExtensions = false
break
}
for len(line) > 0 {
head, tail, _ := strings.Cut(line, ",")
if strings.TrimSpace(head) == ext {
return true
}
line = tail
}
}
return false
}
type graphicsPlatform struct {
window *glfw.Window
}
@@ -33,10 +74,6 @@ type graphicsPlatform struct {
// NewGraphics creates an implementation of graphicsdriver.Graphics for OpenGL.
// The returned graphics value is nil iff the error is not nil.
func NewGraphics() (graphicsdriver.Graphics, error) {
if microsoftgdk.IsXbox() {
return nil, fmt.Errorf("opengl: OpenGL is not supported on Xbox")
}
ctx, err := gl.NewDefaultContext()
if err != nil {
return nil, err
@@ -60,8 +97,12 @@ func setGLFWClientAPI(isES bool) error {
if err := glfw.WindowHint(glfw.ContextVersionMinor, 0); err != nil {
return err
}
if err := glfw.WindowHint(glfw.ContextCreationAPI, glfw.EGLContextAPI); err != nil {
return err
// Use GLX if the extension allows, or use EGL otherwise.
// Prefer GLX since EGL might not work well on Wayland (#3152).
if !isGLXExtensionForGL2Available() {
if err := glfw.WindowHint(glfw.ContextCreationAPI, glfw.EGLContextAPI); err != nil {
return err
}
}
return nil
}
@@ -75,15 +116,6 @@ func setGLFWClientAPI(isES bool) error {
if err := glfw.WindowHint(glfw.ContextVersionMinor, 2); err != nil {
return err
}
// macOS requires forward-compatible and a core profile.
if runtime.GOOS == "darwin" {
if err := glfw.WindowHint(glfw.OpenGLForwardCompat, glfw.True); err != nil {
return err
}
if err := glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile); err != nil {
return err
}
}
return nil
}
@@ -103,11 +135,11 @@ func (g *Graphics) swapBuffers() error {
// This needs to be called at least after SetMonitor.
// Without SwapInterval after SetMonitor, vsynch doesn't work (#375).
if g.vsync {
if err := glfw.SwapInterval(1); err != nil {
if err := g.window.SwapInterval(1); err != nil {
return err
}
} else {
if err := glfw.SwapInterval(0); err != nil {
if err := g.window.SwapInterval(0); err != nil {
return err
}
}
@@ -0,0 +1,86 @@
// Copyright 2024 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.
//go:build darwin && !ios
package opengl
import (
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
)
type graphicsPlatform struct {
window *glfw.Window
}
// NewGraphics creates an implementation of graphicsdriver.Graphics for OpenGL.
// The returned graphics value is nil iff the error is not nil.
func NewGraphics() (graphicsdriver.Graphics, error) {
ctx, err := gl.NewDefaultContext()
if err != nil {
return nil, err
}
if err := glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLAPI); err != nil {
return nil, err
}
if err := glfw.WindowHint(glfw.ContextVersionMajor, 3); err != nil {
return nil, err
}
if err := glfw.WindowHint(glfw.ContextVersionMinor, 2); err != nil {
return nil, err
}
// macOS requires forward-compatible and a core profile.
if err := glfw.WindowHint(glfw.OpenGLForwardCompat, glfw.True); err != nil {
return nil, err
}
if err := glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile); err != nil {
return nil, err
}
return newGraphics(ctx), nil
}
func (g *Graphics) SetGLFWWindow(window *glfw.Window) {
g.window = window
}
func (g *Graphics) makeContextCurrent() error {
return g.window.MakeContextCurrent()
}
func (g *Graphics) swapBuffers() error {
// Call SwapIntervals even though vsync is not changed.
// When toggling to fullscreen, vsync state might be reset unexpectedly (#1787).
// SwapInterval is affected by the current monitor of the window.
// This needs to be called at least after SetMonitor.
// Without SwapInterval after SetMonitor, vsynch doesn't work (#375).
if g.vsync {
if err := g.window.SwapInterval(1); err != nil {
return err
}
} else {
if err := g.window.SwapInterval(0); err != nil {
return err
}
}
if err := g.window.SwapBuffers(); err != nil {
return err
}
return nil
}
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !ebitenginegldebug
//go:build !playstation5 && !ebitenginegldebug
package opengl
@@ -0,0 +1,84 @@
// Copyright 2024 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 opengl
import (
"fmt"
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
"github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk"
)
type graphicsPlatform struct {
window *glfw.Window
}
// NewGraphics creates an implementation of graphicsdriver.Graphics for OpenGL.
// The returned graphics value is nil iff the error is not nil.
func NewGraphics() (graphicsdriver.Graphics, error) {
if microsoftgdk.IsXbox() {
return nil, fmt.Errorf("opengl: OpenGL is not supported on Xbox")
}
ctx, err := gl.NewDefaultContext()
if err != nil {
return nil, err
}
if err := glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLAPI); err != nil {
return nil, err
}
if err := glfw.WindowHint(glfw.ContextVersionMajor, 3); err != nil {
return nil, err
}
if err := glfw.WindowHint(glfw.ContextVersionMinor, 2); err != nil {
return nil, err
}
return newGraphics(ctx), nil
}
func (g *Graphics) SetGLFWWindow(window *glfw.Window) {
g.window = window
}
func (g *Graphics) makeContextCurrent() error {
return g.window.MakeContextCurrent()
}
func (g *Graphics) swapBuffers() error {
// Call SwapIntervals even though vsync is not changed.
// When toggling to fullscreen, vsync state might be reset unexpectedly (#1787).
// SwapInterval is affected by the current monitor of the window.
// This needs to be called at least after SetMonitor.
// Without SwapInterval after SetMonitor, vsynch doesn't work (#375).
if g.vsync {
if err := g.window.SwapInterval(1); err != nil {
return err
}
} else {
if err := g.window.SwapInterval(0); err != nil {
return err
}
}
if err := g.window.SwapBuffers(); err != nil {
return err
}
return nil
}
@@ -37,10 +37,9 @@ type Image struct {
// framebuffer is a wrapper of OpenGL's framebuffer.
type framebuffer struct {
graphics *Graphics
native framebufferNative
width int
height int
native framebufferNative
viewportWidth int
viewportHeight int
}
func (i *Image) ID() graphicsdriver.ImageID {
@@ -81,7 +80,7 @@ func (i *Image) ReadPixels(args []graphicsdriver.PixelsArgs) error {
return nil
}
func (i *Image) framebufferSize() (int, int) {
func (i *Image) viewportSize() (int, int) {
if i.screen {
// The (default) framebuffer size can't be converted to a power of 2.
// On browsers, i.width and i.height are used as viewport size and
@@ -96,11 +95,12 @@ func (i *Image) ensureFramebuffer() error {
return nil
}
w, h := i.framebufferSize()
w, h := i.viewportSize()
if i.screen {
i.framebuffer = i.graphics.context.newScreenFramebuffer(w, h)
return nil
}
f, err := i.graphics.context.newFramebuffer(i.texture, w, h)
if err != nil {
return err
@@ -118,7 +118,7 @@ func (i *Image) ensureStencilBuffer() error {
return err
}
r, err := i.graphics.context.newRenderbuffer(i.framebufferSize())
r, err := i.graphics.context.newRenderbuffer(i.viewportSize())
if err != nil {
return err
}
@@ -53,28 +53,33 @@ func (a *arrayBufferLayout) names() []string {
return ns
}
// totalBytes returns the size in bytes for one element of the array buffer.
func (a *arrayBufferLayout) totalBytes() int {
// float32Count returns the total float32 count for one element of the array buffer.
func (a *arrayBufferLayout) float32Count() int {
if a.total != 0 {
return a.total
}
t := 0
for _, p := range a.parts {
t += floatSizeInBytes * p.num
t += p.num
}
a.total = t
return a.total
}
func (a *arrayBufferLayout) addPart(part arrayBufferLayoutPart) {
a.parts = append(a.parts, part)
a.total = 0
}
// enable starts using the array buffer.
func (a *arrayBufferLayout) enable(context *context) {
for i := range a.parts {
context.ctx.EnableVertexAttribArray(uint32(i))
}
total := a.totalBytes()
total := a.float32Count()
offset := 0
for i, p := range a.parts {
context.ctx.VertexAttribPointer(uint32(i), int32(p.num), gl.FLOAT, false, int32(total), offset)
context.ctx.VertexAttribPointer(uint32(i), int32(p.num), gl.FLOAT, false, int32(floatSizeInBytes*total), offset)
offset += floatSizeInBytes * p.num
}
}
@@ -88,28 +93,39 @@ func (a *arrayBufferLayout) disable(context *context) {
}
// theArrayBufferLayout is the array buffer layout for Ebitengine.
var theArrayBufferLayout = arrayBufferLayout{
// Note that GL_MAX_VERTEX_ATTRIBS is at least 16.
parts: []arrayBufferLayoutPart{
{
name: "A0",
num: 2,
},
{
name: "A1",
num: 2,
},
{
name: "A2",
num: 4,
},
},
}
var theArrayBufferLayout arrayBufferLayout
func init() {
vertexFloatCount := theArrayBufferLayout.totalBytes() / floatSizeInBytes
if graphics.VertexFloatCount != vertexFloatCount {
panic(fmt.Sprintf("vertex float num must be %d but %d", graphics.VertexFloatCount, vertexFloatCount))
theArrayBufferLayout = arrayBufferLayout{
// Note that GL_MAX_VERTEX_ATTRIBS is at least 16.
parts: []arrayBufferLayoutPart{
{
name: "A0",
num: 2,
},
{
name: "A1",
num: 2,
},
{
name: "A2",
num: 4,
},
},
}
n := theArrayBufferLayout.float32Count()
diff := graphics.VertexFloatCount - n
if diff == 0 {
return
}
if diff%4 != 0 {
panic("opengl: unexpected attribute layout")
}
for i := 0; i < diff/4; i++ {
theArrayBufferLayout.addPart(arrayBufferLayoutPart{
name: fmt.Sprintf("A%d", i+3),
num: 4,
})
}
}
@@ -259,7 +275,7 @@ 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 {
func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textures [graphics.ShaderSrcImageCount]textureVariable) error {
if g.state.lastProgram != program {
g.context.ctx.UseProgram(uint32(program))
@@ -276,7 +292,7 @@ func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textu
if u.value == nil {
continue
}
if got, expected := len(u.value), u.typ.Uint32Count(); got != expected {
if got, expected := len(u.value), u.typ.DwordCount(); got != expected {
// Copy a shaderir.Type value once. Do not pass u.typ directly to fmt.Errorf arguments, or
// the value u would be allocated on heap.
typ := u.typ
@@ -18,6 +18,7 @@ package opengl
import (
"fmt"
"runtime"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
@@ -59,13 +60,13 @@ func (s *Shader) compile() error {
vs, err := s.graphics.context.newShader(gl.VERTEX_SHADER, vssrc)
if err != nil {
return fmt.Errorf("opengl: vertex shader compile error: %v, source:\n%s", err, vssrc)
return err
}
defer s.graphics.context.ctx.DeleteShader(uint32(vs))
fs, err := s.graphics.context.newShader(gl.FRAGMENT_SHADER, fssrc)
if err != nil {
return fmt.Errorf("opengl: fragment shader compile error: %v, source:\n%s", err, fssrc)
return err
}
defer s.graphics.context.ctx.DeleteShader(uint32(fs))
@@ -74,6 +75,26 @@ func (s *Shader) compile() error {
return err
}
// Check the shader compile status asynchronously if possible.
// The function 'compile' itself is still blocking, but at least this gives a chance to other goroutines to run
// while waiting for the shader compilation.
if s.graphics.context.hasParallelShaderCompile() {
for s.graphics.context.ctx.GetShaderi(uint32(vs), gl.COMPLETION_STATUS_KHR) != gl.TRUE ||
s.graphics.context.ctx.GetShaderi(uint32(fs), gl.COMPLETION_STATUS_KHR) != gl.TRUE {
runtime.Gosched()
}
}
// Check errors only after linking fails.
// See https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#dont_check_shader_compile_status_unless_linking_fails
if s.graphics.context.ctx.GetProgrami(uint32(p), gl.LINK_STATUS) == gl.FALSE {
programInfo := s.graphics.context.ctx.GetProgramInfoLog(uint32(p))
vertexShaderInfo := s.graphics.context.ctx.GetShaderInfoLog(uint32(vs))
fragmentShaderInfo := s.graphics.context.ctx.GetShaderInfoLog(uint32(fs))
return fmt.Errorf("opengl: program error: %s\nvertex shader error: %s\nvertex shader source: %s\nfragment shader error: %s\nfragment shader source: %s",
programInfo, vertexShaderInfo, vssrc, fragmentShaderInfo, fssrc)
}
s.p = p
return nil
}