diff --git a/gl/doc.go b/gl/doc.go index 1ea6efb..1eb1d77 100644 --- a/gl/doc.go +++ b/gl/doc.go @@ -3,7 +3,15 @@ // license that can be found in the LICENSE file. /* -Package gl implements Go bindings for OpenGL ES 2. +Package gl implements Go bindings for OpenGL ES 2.0 and ES 3.0. + +The GL functions are defined on a Context object that is responsible for +tracking a GL context. Typically a windowing system package (such as +golang.org/x/exp/shiny/screen) will call NewContext and provide +a gl.Context for a user application. + +If the gl package is compiled on a platform capable of supporting ES 3.0, +the gl.Context object also implements gl.Context3. The bindings are deliberately minimal, staying as close the C API as possible. The semantics of each function maps onto functions diff --git a/gl/fn.go b/gl/fn.go index b4de7d5..a3fe0a6 100644 --- a/gl/fn.go +++ b/gl/fn.go @@ -23,6 +23,8 @@ type fnargs struct { a5 uintptr a6 uintptr a7 uintptr + a8 uintptr + a9 uintptr } type glfn int @@ -41,6 +43,7 @@ const ( glfnBlendEquationSeparate glfnBlendFunc glfnBlendFuncSeparate + glfnBlitFramebuffer glfnBufferData glfnBufferSubData glfnCheckFramebufferStatus diff --git a/gl/gendebug.go b/gl/gendebug.go index 861ca19..57b966e 100644 --- a/gl/gendebug.go +++ b/gl/gendebug.go @@ -103,6 +103,13 @@ func main() { if fn.Recv == nil || fn.Recv.List[0].Names[0].Name != "ctx" { continue } + tname := "" + t := fn.Recv.List[0].Type + if star, ok := t.(*ast.StarExpr); ok { + tname = "*" + star.X.(*ast.Ident).Name + } else if t, ok := t.(*ast.Ident); ok { + tname = t.Name + } var ( params []string @@ -112,7 +119,7 @@ func main() { ) // Print function signature. - fmt.Fprintf(buf, "func (ctx *context) %s(", fn.Name.Name) + fmt.Fprintf(buf, "func (ctx %s) %s(", tname, fn.Name.Name) for i, p := range fn.Type.Params.List { if i > 0 { fmt.Fprint(buf, ", ") diff --git a/gl/gl.go b/gl/gl.go index 71b0ee3..d9d0184 100644 --- a/gl/gl.go +++ b/gl/gl.go @@ -1679,3 +1679,21 @@ func (ctx *context) Viewport(x, y, width, height int) { }, }) } + +func (ctx context3) BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1 int, mask uint, filter Enum) { + ctx.enqueue(call{ + args: fnargs{ + fn: glfnBlitFramebuffer, + a0: uintptr(srcX0), + a1: uintptr(srcY0), + a2: uintptr(srcX1), + a3: uintptr(srcY1), + a4: uintptr(dstX0), + a5: uintptr(dstY0), + a6: uintptr(dstX1), + a7: uintptr(dstY1), + a8: uintptr(mask), + a9: filter.c(), + }, + }) +} diff --git a/gl/gldebug.go b/gl/gldebug.go index 2c718ff..8dde6df 100644 --- a/gl/gldebug.go +++ b/gl/gldebug.go @@ -2858,3 +2858,25 @@ func (ctx *context) Viewport(x, y, width, height int) { }, blocking: true}) } + +func (ctx context3) BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1 int, mask uint, filter Enum) { + defer func() { + errstr := ctx.errDrain() + log.Printf("gl.BlitFramebuffer(%v, %v, %v, %v, %v, %v, %v, %v, %v, %v) %v", srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter, errstr) + }() + ctx.enqueueDebug(call{ + args: fnargs{ + fn: glfnBlitFramebuffer, + a0: uintptr(srcX0), + a1: uintptr(srcY0), + a2: uintptr(srcX1), + a3: uintptr(srcY1), + a4: uintptr(dstX0), + a5: uintptr(dstY0), + a6: uintptr(dstX1), + a7: uintptr(dstY1), + a8: uintptr(mask), + a9: filter.c(), + }, + blocking: true}) +} diff --git a/gl/interface.go b/gl/interface.go index cf02587..8c93954 100644 --- a/gl/interface.go +++ b/gl/interface.go @@ -4,7 +4,15 @@ package gl -// Context is an OpenGL context. +// Context is an OpenGL ES context. +// +// A Context has a method for every GL function supported by ES 2 or later. +// In a program compiled with ES 3 support, a Context is also a Context3. +// For example, a program can: +// +// func f(glctx gl.Context) { +// glctx.(gl.Context3).BlitFramebuffer(...) +// } // // Calls are not safe for concurrent use. However calls can be made from // any goroutine, the gl package removes the notion of thread-local @@ -816,6 +824,19 @@ type Context interface { Viewport(x, y, width, height int) } +// Context3 is an OpenGL ES 3 context. +// +// When the gl package is compiled with GL ES 3 support, the produced +// Context object also implements the Context3 interface. +type Context3 interface { + Context + + // BlitFramebuffer copies a block of pixels between framebuffers. + // + // https://www.khronos.org/opengles/sdk/docs/man3/html/glBlitFramebuffer.xhtml + BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1 int, mask uint, filter Enum) +} + // Worker is used by display driver code to execute OpenGL calls. // // Typically display driver code creates a gl.Context for an application, diff --git a/gl/work.c b/gl/work.c index 6e7f76d..8f259f7 100644 --- a/gl/work.c +++ b/gl/work.c @@ -8,6 +8,15 @@ #include "_cgo_export.h" #include "work.h" +#if defined(GL_ES_VERSION_3_0) && GL_ES_VERSION_3_0 +#else +#include +void glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { + printf("GLES3 function is missing\n"); + exit(2); +} +#endif + uintptr_t processFn(struct fnargs* args, char* parg) { uintptr_t ret = 0; switch (args->fn) { @@ -50,6 +59,9 @@ uintptr_t processFn(struct fnargs* args, char* parg) { case glfnBlendFuncSeparate: glBlendFuncSeparate((GLenum)args->a0, (GLenum)args->a1, (GLenum)args->a2, (GLenum)args->a3); break; + case glfnBlitFramebuffer: + glBlitFramebuffer((GLint)args->a0, (GLint)args->a1, (GLint)args->a2, (GLint)args->a3, (GLint)args->a4, (GLint)args->a5, (GLint)args->a6, (GLint)args->a7, (GLbitfield)args->a8, (GLenum)args->a9); + break; case glfnBufferData: glBufferData((GLenum)args->a0, (GLsizeiptr)args->a1, (GLvoid*)parg, (GLenum)args->a2); break; diff --git a/gl/work.go b/gl/work.go index 65fdc42..b6b8c0d 100644 --- a/gl/work.go +++ b/gl/work.go @@ -12,6 +12,7 @@ package gl #cgo darwin,arm64 LDFLAGS: -framework OpenGLES #cgo linux LDFLAGS: -lGLESv2 +#cgo android CFLAGS: -Dos_android #cgo darwin,amd64 CFLAGS: -Dos_osx #cgo darwin,arm CFLAGS: -Dos_ios #cgo darwin,arm64 CFLAGS: -Dos_ios @@ -66,6 +67,10 @@ type context struct { func (ctx *context) WorkAvailable() <-chan struct{} { return ctx.workAvailable } +type context3 struct { + *context +} + // NewContext creates a cgo OpenGL context. // // See the Worker interface for more details on how it is used. @@ -75,7 +80,16 @@ func NewContext() (Context, Worker) { work: make(chan call, workbufLen), retvalue: make(chan C.uintptr_t), } - return glctx, glctx + if C.GLES_VERSION == "GL_ES_2_0" { + return glctx, glctx + } + return context3{glctx}, glctx +} + +// Version returns a GL ES version string, either "GL_ES_2_0" or "GL_ES_3_0". +// Future versions of the gl package may return "GL_ES_3_1". +func Version() string { + return C.GLES_VERSION } func (ctx *context) enqueue(c call) uintptr { diff --git a/gl/work.h b/gl/work.h index 1a090f3..3d9a81b 100644 --- a/gl/work.h +++ b/gl/work.h @@ -2,16 +2,30 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#ifdef os_linux -#include // install on Ubuntu with: sudo apt-get install libegl1-mesa-dev libgles2-mesa-dev libx11-dev +#ifdef os_android +// TODO(crawshaw): We could include and +// condition on __ANDROID_API__ to get GLES3 headers. However +// we also need to add -lGLESv3 to LDFLAGS, which we cannot do +// from inside an ifdef. +#include +#elif os_linux +#include // install on Ubuntu with: sudo apt-get install libegl1-mesa-dev libgles2-mesa-dev libx11-dev #endif + #ifdef os_ios #include #endif + #ifdef os_osx #include #endif +#if defined(GL_ES_VERSION_3_0) && GL_ES_VERSION_3_0 +#define GLES_VERSION "GL_ES_3_0" +#else +#define GLES_VERSION "GL_ES_2_0" +#endif + #include #include @@ -31,6 +45,7 @@ typedef enum { glfnBlendEquationSeparate, glfnBlendFunc, glfnBlendFuncSeparate, + glfnBlitFramebuffer, glfnBufferData, glfnBufferSubData, glfnCheckFramebufferStatus, @@ -173,6 +188,8 @@ struct fnargs { uintptr_t a5; uintptr_t a6; uintptr_t a7; + uintptr_t a8; + uintptr_t a9; }; extern uintptr_t processFn(struct fnargs* args, char* parg);