diff --git a/internal/graphicsdriver/opengl/context_desktop.go b/internal/graphicsdriver/opengl/context_desktop.go index 41e0bcd24..0280a22a0 100644 --- a/internal/graphicsdriver/opengl/context_desktop.go +++ b/internal/graphicsdriver/opengl/context_desktop.go @@ -198,6 +198,13 @@ func (c *context) framebufferPixels(f *framebuffer, width, height int) ([]byte, return pixels, nil } +func (c *context) activeTexture(idx int) { + _ = c.t.Call(func() error { + gl.ActiveTexture(gl.TEXTURE0 + uint32(idx)) + return nil + }) +} + func (c *context) bindTextureImpl(t textureNative) { _ = c.t.Call(func() error { gl.BindTexture(gl.TEXTURE_2D, uint32(t)) diff --git a/internal/graphicsdriver/opengl/context_js.go b/internal/graphicsdriver/opengl/context_js.go index 0111b82d1..b07233aa0 100644 --- a/internal/graphicsdriver/opengl/context_js.go +++ b/internal/graphicsdriver/opengl/context_js.go @@ -112,6 +112,8 @@ var ( unsignedByte js.Value unsignedShort js.Value + texture0 int + isWebGL2Available bool ) @@ -154,6 +156,7 @@ func init() { nearest = contextPrototype.Get("NEAREST") noError = contextPrototype.Get("NO_ERROR") rgba = contextPrototype.Get("RGBA") + texture0 = contextPrototype.Get("TEXTURE0").Int() texture2d = contextPrototype.Get("TEXTURE_2D") textureMagFilter = contextPrototype.Get("TEXTURE_MAG_FILTER") textureMinFilter = contextPrototype.Get("TEXTURE_MIN_FILTER") @@ -279,6 +282,12 @@ func (c *context) framebufferPixels(f *framebuffer, width, height int) ([]byte, return jsutil.Uint8ArrayToSlice(p), nil } +func (c *context) activeTexture(idx int) { + c.ensureGL() + gl := c.gl + gl.Call("activeTexture", texture0+idx) +} + func (c *context) bindTextureImpl(t textureNative) { c.ensureGL() gl := c.gl diff --git a/internal/graphicsdriver/opengl/context_mobile.go b/internal/graphicsdriver/opengl/context_mobile.go index a9eb1fe58..cb15a1be1 100644 --- a/internal/graphicsdriver/opengl/context_mobile.go +++ b/internal/graphicsdriver/opengl/context_mobile.go @@ -156,6 +156,11 @@ func (c *context) framebufferPixels(f *framebuffer, width, height int) ([]byte, return pixels, nil } +func (c *context) activeTexture(idx int) { + gl := c.gl + gl.ActiveTexture(mgl.Enum(mgl.TEXTURE0 + idx)) +} + func (c *context) bindTextureImpl(t textureNative) { gl := c.gl gl.BindTexture(mgl.TEXTURE_2D, mgl.Texture(t)) diff --git a/internal/graphicsdriver/opengl/gl/package.go b/internal/graphicsdriver/opengl/gl/package.go index 42e067015..f513f9802 100644 --- a/internal/graphicsdriver/opengl/gl/package.go +++ b/internal/graphicsdriver/opengl/gl/package.go @@ -49,6 +49,7 @@ const ( NO_ERROR = 0 READ_WRITE = 0x88BA RGBA = 0x1908 + TEXTURE0 = 0x84C0 TEXTURE_2D = 0x0DE1 TEXTURE_MAG_FILTER = 0x2800 TEXTURE_MIN_FILTER = 0x2801 diff --git a/internal/graphicsdriver/opengl/gl/package_notwindows.go b/internal/graphicsdriver/opengl/gl/package_notwindows.go index 9738a76c1..a080cfb92 100644 --- a/internal/graphicsdriver/opengl/gl/package_notwindows.go +++ b/internal/graphicsdriver/opengl/gl/package_notwindows.go @@ -95,7 +95,8 @@ package gl // typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); // typedef unsigned short GLhalfNV; // typedef GLintptr GLvdpauSurfaceNV; -// typedef void (APIENTRY *GLVULKANPROCNV)(void); +// +// typedef void (APIENTRYP GPACTIVETEXTURE)(GLenum texture); // typedef void (APIENTRYP GPATTACHSHADER)(GLuint program, GLuint shader); // typedef void (APIENTRYP GPBINDATTRIBLOCATION)(GLuint program, GLuint index, const GLchar * name); // typedef void (APIENTRYP GPBINDBUFFER)(GLenum target, GLuint buffer); @@ -159,6 +160,9 @@ package gl // typedef void (APIENTRYP GPVERTEXATTRIBPOINTER)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const uintptr_t pointer); // typedef void (APIENTRYP GPVIEWPORT)(GLint x, GLint y, GLsizei width, GLsizei height); // +// static void glowActiveTexture(GPACTIVETEXTURE fnptr, GLenum texture) { +// (*fnptr)(texture); +// } // static void glowAttachShader(GPATTACHSHADER fnptr, GLuint program, GLuint shader) { // (*fnptr)(program, shader); // } @@ -353,6 +357,7 @@ import ( ) var ( + gpActiveTexture C.GPACTIVETEXTURE gpAttachShader C.GPATTACHSHADER gpBindAttribLocation C.GPBINDATTRIBLOCATION gpBindBuffer C.GPBINDBUFFER @@ -424,6 +429,10 @@ func boolToInt(b bool) int { return 0 } +func ActiveTexture(texture uint32) { + C.glowActiveTexture(gpActiveTexture, (C.GLenum)(texture)) +} + func AttachShader(program uint32, shader uint32) { C.glowAttachShader(gpAttachShader, (C.GLuint)(program), (C.GLuint)(shader)) } @@ -675,6 +684,10 @@ func Viewport(x int32, y int32, width int32, height int32) { // InitWithProcAddrFunc intializes the package using the specified OpenGL // function pointer loading function. For more cases Init should be used func InitWithProcAddrFunc(getProcAddr func(name string) unsafe.Pointer) error { + gpActiveTexture = (C.GPACTIVETEXTURE)(getProcAddr("glActiveTexture")) + if gpActiveTexture == nil { + return errors.New("glActiveTexture") + } gpAttachShader = (C.GPATTACHSHADER)(getProcAddr("glAttachShader")) if gpAttachShader == nil { return errors.New("glAttachShader") diff --git a/internal/graphicsdriver/opengl/gl/package_windows.go b/internal/graphicsdriver/opengl/gl/package_windows.go index e7a10af84..8b8afafc6 100644 --- a/internal/graphicsdriver/opengl/gl/package_windows.go +++ b/internal/graphicsdriver/opengl/gl/package_windows.go @@ -10,6 +10,7 @@ import ( ) var ( + gpActiveTexture uintptr gpAttachShader uintptr gpBindAttribLocation uintptr gpBindBuffer uintptr @@ -81,6 +82,10 @@ func boolToUintptr(b bool) uintptr { return 0 } +func ActiveTexture(texture uint32) { + syscall.Syscall(gpActiveTexture, 1, uintptr(texture), 0, 0) +} + func AttachShader(program uint32, shader uint32) { syscall.Syscall(gpAttachShader, 2, uintptr(program), uintptr(shader), 0) } @@ -332,6 +337,10 @@ func Viewport(x int32, y int32, width int32, height int32) { // InitWithProcAddrFunc intializes the package using the specified OpenGL // function pointer loading function. For more cases Init should be used func InitWithProcAddrFunc(getProcAddr func(name string) uintptr) error { + gpActiveTexture = getProcAddr("glActiveTexture") + if gpActiveTexture == 0 { + return errors.New("glActiveTexture") + } gpAttachShader = getProcAddr("glAttachShader") if gpAttachShader == 0 { return errors.New("glAttachShader") diff --git a/internal/graphicsdriver/opengl/graphics.go b/internal/graphicsdriver/opengl/graphics.go index d0be9a258..88bacae72 100644 --- a/internal/graphicsdriver/opengl/graphics.go +++ b/internal/graphicsdriver/opengl/graphics.go @@ -160,7 +160,7 @@ func (g *Graphics) Draw(indexLen int, indexOffset int, mode driver.CompositeMode uniforms["scale"] = scale } - uniforms["texture"] = source.textureNative + uniforms["texture/0"] = source.textureNative if err := g.useProgram(program, uniforms); err != nil { return err diff --git a/internal/graphicsdriver/opengl/program.go b/internal/graphicsdriver/opengl/program.go index 38df00acd..3e0b66707 100644 --- a/internal/graphicsdriver/opengl/program.go +++ b/internal/graphicsdriver/opengl/program.go @@ -16,6 +16,8 @@ package opengl import ( "fmt" + "strconv" + "strings" "github.com/hajimehoshi/ebiten/internal/driver" "github.com/hajimehoshi/ebiten/internal/graphics" @@ -132,8 +134,9 @@ type openGLState struct { // programs is OpenGL's program for rendering a texture. programs map[programKey]program - lastProgram program - lastUniforms map[string]interface{} + lastProgram program + lastUniforms map[string]interface{} + lastActiveTexture int source *Image destination *Image @@ -251,6 +254,8 @@ func (g *Graphics) useProgram(program program, uniforms map[string]interface{}) g.state.lastProgram = program g.state.lastUniforms = map[string]interface{}{} + g.state.lastActiveTexture = 0 + g.context.activeTexture(0) } for key, u := range uniforms { @@ -271,10 +276,19 @@ func (g *Graphics) useProgram(program program, uniforms map[string]interface{}) g.state.lastUniforms[key] = u case textureNative: // Apparently, a texture must be bound every time. The cache is not used here. - // TODO: Use another value than 0 when binding multiple textures. - g.context.uniformInt(program, key, 0) - // We don't have to call gl.ActiveTexture here: GL_TEXTURE0 is the default active texture - // See also: https://www.opengl.org/sdk/docs/man2/xhtml/glActiveTexture.xml + tokens := strings.SplitN(key, "/", 2) + if len(tokens) != 2 { + return fmt.Errorf("opengl: a uniform variable name for textures must be '[name]/[index]' but %s", key) + } + idx, err := strconv.Atoi(tokens[1]) + if err != nil { + return fmt.Errorf("opengl: a uniform variable name for textures must be '[name]/[index]' but %s", key) + } + g.context.uniformInt(program, tokens[0], idx) + if g.state.lastActiveTexture != idx { + g.context.activeTexture(idx) + g.state.lastActiveTexture = idx + } g.context.bindTexture(u) } }