2
0
mirror of synced 2025-02-20 13:38:20 +00:00

gl: batch calls onto a dedicated context thread

All GL function calls fill out a C.struct_fnargs and drop it on the
work queue. The Start function drains the work queue and hands
over a batch of calls to C.process which runs them. This allows
multiple GL calls to be executed in a single cgo call.

A GL call is marked as blocking if it returns a value, or if it
takes a Go pointer. In this case the call will not return until
C.process sends a value on the retvalue channel.

Change-Id: I4c76b2a8ad55f57b0c98d200d0fb708d4634e042
Reviewed-on: https://go-review.googlesource.com/10452
Reviewed-by: Nigel Tao <nigeltao@golang.org>
This commit is contained in:
David Crawshaw 2015-05-28 06:08:52 -04:00
parent 0d468be861
commit 068a51c195
17 changed files with 2876 additions and 677 deletions

View File

@ -21,8 +21,7 @@ void glGenVertexArrays(GLsizei n, GLuint* array);
void glBindVertexArray(GLuint array);
void runApp(void);
void lockContext(GLintptr);
void unlockContext(GLintptr);
void makeCurrentContext(GLintptr);
double backingScaleFactor();
uint64 threadID();
@ -71,17 +70,6 @@ func setGeom(pixelsPerPt float32, width, height int) {
configSwap(callbacks)
}
func initGL() {
// Using attribute arrays in OpenGL 3.3 requires the use of a VBA.
// But VBAs don't exist in ES 2. So we bind a default one.
var id C.GLuint
C.glGenVertexArrays(1, &id)
C.glBindVertexArray(id)
stateStart(callbacks)
}
var initGLOnce sync.Once
var touchEvents struct {
sync.Mutex
pending []event.Touch
@ -109,16 +97,25 @@ func eventMouseDragged(x, y float32) { sendTouch(event.TouchMove, x, y) }
//export eventMouseEnd
func eventMouseEnd(x, y float32) { sendTouch(event.TouchEnd, x, y) }
var startedgl = false
//export drawgl
func drawgl(ctx C.GLintptr) {
// The call to lockContext loads the OpenGL context into
// thread-local storage for use by the underlying GL calls
// done in the user's Draw function. We need to stay on
// the same thread throughout Draw, so we LockOSThread.
runtime.LockOSThread()
C.lockContext(ctx)
if !startedgl {
startedgl = true
go gl.Start(func() {
C.makeCurrentContext(ctx)
initGLOnce.Do(initGL)
// Using attribute arrays in OpenGL 3.3 requires the use of a VBA.
// But VBAs don't exist in ES 2. So we bind a default one.
var id C.GLuint
C.glGenVertexArrays(1, &id)
C.glBindVertexArray(id)
})
stateStart(callbacks)
}
touchEvents.Lock()
pending := touchEvents.pending
@ -140,11 +137,7 @@ func drawgl(ctx C.GLintptr) {
cb.Draw()
}
}
C.unlockContext(ctx)
// This may unlock the original main thread, but that's OK,
// because by the time it does the thread has already entered
// C.runApp, which won't give the thread up.
runtime.UnlockOSThread()
gl.Do(func() {
C.CGLFlushDrawable(C.CGLGetCurrentContext())
})
}

View File

@ -22,17 +22,9 @@ static CVReturn displayLinkDraw(CVDisplayLinkRef displayLink, const CVTimeStamp*
return kCVReturnSuccess;
}
void lockContext(GLintptr context) {
void makeCurrentContext(GLintptr context) {
NSOpenGLContext* ctx = (NSOpenGLContext*)context;
[ctx makeCurrentContext];
CGLLockContext([ctx CGLContextObj]);
}
void unlockContext(GLintptr context) {
NSOpenGLContext* ctx = (NSOpenGLContext*)context;
[ctx flushBuffer];
CGLUnlockContext([ctx CGLContextObj]);
}
uint64 threadID() {

View File

@ -24,7 +24,6 @@ import "C"
import (
"log"
"runtime"
"sync"
"unsafe"
"golang.org/x/mobile/event"
@ -121,12 +120,8 @@ func setGeom(width, height int) {
configSwap(cb)
}
func initGL() {
stateStart(cb)
}
var cb []Callbacks
var initGLOnce sync.Once
var startedgl = false
// touchIDs is the current active touches. The position in the array
// is the ID, the value is the UITouch* pointer value.
@ -181,14 +176,13 @@ func sendTouch(touch uintptr, touchType int, x, y float32) {
//export drawgl
func drawgl(ctx uintptr) {
// The call to lockContext loads the OpenGL context into
// thread-local storage for use by the underlying GL calls
// done in the user's Draw function. We need to stay on
// the same thread throughout Draw, so we LockOSThread.
runtime.LockOSThread()
C.setContext(unsafe.Pointer(ctx))
initGLOnce.Do(initGL)
if !startedgl {
startedgl = true
go gl.Start(func() {
C.setContext(unsafe.Pointer(ctx))
})
stateStart(cb)
}
touchEvents.Lock()
pending := touchEvents.pending
@ -210,12 +204,4 @@ func drawgl(ctx uintptr) {
c.Draw()
}
}
// TODO
//C.unlockContext(ctx)
// This may unlock the original main thread, but that's OK,
// because by the time it does the thread has already entered
// C.runApp, which won't give the thread up.
runtime.UnlockOSThread()
}

View File

@ -91,12 +91,14 @@ import (
)
func windowDraw(callbacks []Callbacks, w *C.ANativeWindow, queue *C.AInputQueue) {
C.createEGLWindow(w)
go gl.Start(func() {
C.createEGLWindow(w)
})
// TODO: is the library or the app responsible for clearing the buffers?
gl.ClearColor(0, 0, 0, 1)
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
C.eglSwapBuffers(C.display, C.surface)
gl.Do(func() { C.eglSwapBuffers(C.display, C.surface) })
if errv := gl.GetError(); errv != gl.NO_ERROR {
log.Printf("GL initialization error: %s", errv)
@ -121,6 +123,7 @@ func windowDraw(callbacks []Callbacks, w *C.ANativeWindow, queue *C.AInputQueue)
configSwap(callbacks)
case <-windowDestroyed:
stateStop(callbacks)
gl.Stop()
return
default:
for _, cb := range callbacks {
@ -128,7 +131,7 @@ func windowDraw(callbacks []Callbacks, w *C.ANativeWindow, queue *C.AInputQueue)
cb.Draw()
}
}
C.eglSwapBuffers(C.display, C.surface)
gl.Do(func() { C.eglSwapBuffers(C.display, C.surface) })
}
}
}

View File

@ -14,6 +14,10 @@ https://www.khronos.org/opengles/sdk/docs/man/
One notable departure from the C API is the introduction of types
to represent common uses of GLint: Texture, Surface, Buffer, etc.
Calls are not safe for concurrent use. However calls can be made from
any goroutine, the gl package removes the notion of thread-local
context.
A tracing version of the OpenGL bindings is behind the `gldebug` build
tag. It acts as a simplified version of apitrace. Build your Go binary
with
@ -34,4 +38,23 @@ the build tag before deploying any binaries.
*/
package gl // import "golang.org/x/mobile/gl"
/*
Implementation details.
All GL function calls fill out a C.struct_fnargs and drop it on the work
queue. The Start function drains the work queue and hands over a batch
of calls to C.process which runs them. This allows multiple GL calls to
be executed in a single cgo call.
A GL call is marked as blocking if it returns a value, or if it takes a
Go pointer. In this case the call will not return until C.process sends a
value on the retvalue channel.
This implementation ensures any goroutine can make GL calls, but it does
not make the GL interface safe for simultaneous use by multiple goroutines.
For the purpose of analyzing this code for race conditions, picture two
separate goroutines: one blocked on gl.Start, and another making calls to
the gl package exported functions.
*/
//go:generate go run gendebug.go -o gldebug.go

View File

@ -154,42 +154,49 @@ func main() {
}
fmt.Fprintf(buf, ") {\n")
// Insert a defer block for tracing.
fmt.Fprintf(buf, "defer func() {\n")
fmt.Fprintf(buf, "\terrstr := errDrain()\n")
switch fn.Name.Name {
case "GetUniformLocation", "GetAttribLocation":
fmt.Fprintf(buf, "\tr0.name = name\n")
}
fmt.Fprintf(buf, "\tlog.Printf(\"gl.%s(", fn.Name.Name)
for i, p := range paramTypes {
if i > 0 {
fmt.Fprint(buf, ", ")
// gl.GetError is used by errDrain, which will be made part of
// all functions. So do not apply it to gl.GetError to avoid
// infinite recursion.
skip := fn.Name.Name == "GetError"
if !skip {
// Insert a defer block for tracing.
fmt.Fprintf(buf, "defer func() {\n")
fmt.Fprintf(buf, "\terrstr := errDrain()\n")
switch fn.Name.Name {
case "GetUniformLocation", "GetAttribLocation":
fmt.Fprintf(buf, "\tr0.name = name\n")
}
fmt.Fprint(buf, typePrinter(p))
}
fmt.Fprintf(buf, ") ")
if len(resultTypes) > 1 {
fmt.Fprint(buf, "(")
}
for i, r := range resultTypes {
if i > 0 {
fmt.Fprint(buf, ", ")
fmt.Fprintf(buf, "\tlog.Printf(\"gl.%s(", fn.Name.Name)
for i, p := range paramTypes {
if i > 0 {
fmt.Fprint(buf, ", ")
}
fmt.Fprint(buf, typePrinter(p))
}
fmt.Fprint(buf, typePrinter(r))
fmt.Fprintf(buf, ") ")
if len(resultTypes) > 1 {
fmt.Fprint(buf, "(")
}
for i, r := range resultTypes {
if i > 0 {
fmt.Fprint(buf, ", ")
}
fmt.Fprint(buf, typePrinter(r))
}
if len(resultTypes) > 1 {
fmt.Fprint(buf, ") ")
}
fmt.Fprintf(buf, "%%v\"")
for i, p := range paramTypes {
fmt.Fprintf(buf, ", %s", typePrinterArg(p, params[i]))
}
for i, r := range resultTypes {
fmt.Fprintf(buf, ", %s", typePrinterArg(r, results[i]))
}
fmt.Fprintf(buf, ", errstr)\n")
fmt.Fprintf(buf, "}()\n")
}
if len(resultTypes) > 1 {
fmt.Fprint(buf, ") ")
}
fmt.Fprintf(buf, "%%v\"")
for i, p := range paramTypes {
fmt.Fprintf(buf, ", %s", typePrinterArg(p, params[i]))
}
for i, r := range resultTypes {
fmt.Fprintf(buf, ", %s", typePrinterArg(r, results[i]))
}
fmt.Fprintf(buf, ", errstr)\n")
fmt.Fprintf(buf, "}()\n")
// Print original body of function.
for _, s := range fn.Body.List {
@ -226,31 +233,20 @@ const preamble = `// Copyright 2014 The Go Authors. All rights reserved.
package gl
/*
#include <stdlib.h>
#ifdef os_linux
#include <GLES2/gl2.h>
#endif
#ifdef os_ios
#include <OpenGLES/ES2/gl.h>
#endif
#ifdef os_darwin_amd64
#include <OpenGL/gl3.h>
#endif
*/
// #include "work.h"
import "C"
import (
"fmt"
"log"
"math"
"unsafe"
)
func errDrain() string {
var errs []Enum
for {
e := Enum(C.glGetError())
e := GetError()
if e == 0 {
break
}

1187
gl/gl.go

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -280,7 +280,7 @@ func (img *Image) Draw(topLeft, topRight, bottomLeft geom.Point, srcBounds image
// which gives:
// a00 = (2*qx2 - 2*px2) / 2 = qx2 - px2
// and similarly for the other elements of a.
glimage.mvp.WriteAffine(&f32.Affine{{
writeAffine(glimage.mvp, &f32.Affine{{
qx2 - px2,
px2 - sx2,
qx2 + sx2,
@ -315,7 +315,7 @@ func (img *Image) Draw(topLeft, topRight, bottomLeft geom.Point, srcBounds image
// a10 + 0 + a12 = qy = py
// 0 + a01 + a02 = sx = px
// 0 + a11 + a12 = sy
glimage.uvp.WriteAffine(&f32.Affine{{
writeAffine(glimage.uvp, &f32.Affine{{
qx - px,
0,
px,

View File

@ -39,11 +39,16 @@ func TestImage(t *testing.T) {
t.Fatal(err)
}
ctxGL := createContext()
defer ctxGL.destroy()
var ctxGL *contextGL
go gl.Start(func() {
ctxGL = createContext()
})
start()
defer stop()
defer func() {
stop()
gl.Stop()
ctxGL.destroy()
}()
const (
pixW = 100
@ -69,7 +74,12 @@ func TestImage(t *testing.T) {
t.Fatalf("framebuffer create failed: %v", status)
}
gl.ClearColor(0, 0, 1, 1) // blue
allocs := testing.AllocsPerRun(100, func() {
gl.ClearColor(0, 0, 1, 1) // blue
})
if allocs != 0 {
t.Errorf("unexpected allocations from calling gl.ClearColor: %f", allocs)
}
gl.Clear(gl.COLOR_BUFFER_BIT)
gl.Viewport(0, 0, pixW, pixH)

View File

@ -8,6 +8,7 @@ package glutil // import "golang.org/x/mobile/gl/glutil"
import (
"fmt"
"golang.org/x/mobile/f32"
"golang.org/x/mobile/gl"
)
@ -56,3 +57,18 @@ func loadShader(shaderType gl.Enum, src string) (gl.Shader, error) {
}
return shader, nil
}
// writeAffine writes the contents of an Affine to a 3x3 matrix GL uniform.
func writeAffine(u gl.Uniform, a *f32.Affine) {
var m [9]float32
m[0*3+0] = a[0][0]
m[0*3+1] = a[1][0]
m[0*3+2] = 0
m[1*3+0] = a[0][1]
m[1*3+1] = a[1][1]
m[1*3+2] = 0
m[2*3+0] = a[0][2]
m[2*3+1] = a[1][2]
m[2*3+2] = 1
gl.UniformMatrix3fv(u, m[:])
}

View File

@ -1,106 +0,0 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux darwin
package gl
// This file contains GL Types and their methods that are independent of the
// "gldebug" build tag.
/*
#cgo darwin,amd64 LDFLAGS: -framework OpenGL
#cgo darwin,arm LDFLAGS: -framework OpenGLES
#cgo darwin,arm64 LDFLAGS: -framework OpenGLES
#cgo linux LDFLAGS: -lGLESv2
#cgo darwin,amd64 CFLAGS: -Dos_darwin_amd64
#cgo darwin,arm CFLAGS: -Dos_ios
#cgo darwin,arm64 CFLAGS: -Dos_ios
#cgo linux CFLAGS: -Dos_linux
#ifdef os_linux
#include <GLES2/gl2.h>
#endif
#ifdef os_ios
#include <OpenGLES/ES2/gl.h>
#endif
#ifdef os_darwin_amd64
#include <OpenGL/gl3.h>
#endif
void blendColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { glBlendColor(r, g, b, a); }
void clearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { glClearColor(r, g, b, a); }
void clearDepthf(GLfloat d) { glClearDepthf(d); }
void depthRangef(GLfloat n, GLfloat f) { glDepthRangef(n, f); }
void sampleCoverage(GLfloat v, GLboolean invert) { glSampleCoverage(v, invert); }
*/
import "C"
import "golang.org/x/mobile/f32"
// WriteAffine writes the contents of an Affine to a 3x3 matrix GL uniform.
func (u Uniform) WriteAffine(a *f32.Affine) {
var m [9]float32
m[0*3+0] = a[0][0]
m[0*3+1] = a[1][0]
m[0*3+2] = 0
m[1*3+0] = a[0][1]
m[1*3+1] = a[1][1]
m[1*3+2] = 0
m[2*3+0] = a[0][2]
m[2*3+1] = a[1][2]
m[2*3+2] = 1
UniformMatrix3fv(u, m[:])
}
// WriteMat4 writes the contents of a 4x4 matrix to a GL uniform.
func (u Uniform) WriteMat4(p *f32.Mat4) {
var m [16]float32
m[0*4+0] = p[0][0]
m[0*4+1] = p[1][0]
m[0*4+2] = p[2][0]
m[0*4+3] = p[3][0]
m[1*4+0] = p[0][1]
m[1*4+1] = p[1][1]
m[1*4+2] = p[2][1]
m[1*4+3] = p[3][1]
m[2*4+0] = p[0][2]
m[2*4+1] = p[1][2]
m[2*4+2] = p[2][2]
m[2*4+3] = p[3][2]
m[3*4+0] = p[0][3]
m[3*4+1] = p[1][3]
m[3*4+2] = p[2][3]
m[3*4+3] = p[3][3]
UniformMatrix4fv(u, m[:])
}
// WriteVec4 writes the contents of a 4-element vector to a GL uniform.
func (u Uniform) WriteVec4(v *f32.Vec4) {
Uniform4f(u, v[0], v[1], v[2], v[3])
}
func glBoolean(b bool) C.GLboolean {
if b {
return TRUE
}
return FALSE
}
// Desktop OpenGL and the ES 2/3 APIs have a very slight difference
// that is imperceptible to C programmers: some function parameters
// use the type Glclampf and some use GLfloat. These two types are
// equivalent in size and bit layout (both are single-precision
// floats), but it plays havoc with cgo. We adjust the types by using
// C wrappers for the problematic functions.
func blendColor(r, g, b, a float32) {
C.blendColor(C.GLfloat(r), C.GLfloat(g), C.GLfloat(b), C.GLfloat(a))
}
func clearColor(r, g, b, a float32) {
C.clearColor(C.GLfloat(r), C.GLfloat(g), C.GLfloat(b), C.GLfloat(a))
}
func clearDepthf(d float32) { C.clearDepthf(C.GLfloat(d)) }
func depthRangef(n, f float32) { C.depthRangef(C.GLfloat(n), C.GLfloat(f)) }
func sampleCoverage(v float32, i bool) { C.sampleCoverage(C.GLfloat(v), glBoolean(i)) }

View File

@ -10,20 +10,7 @@ package gl
// Alternate versions of the types defined in types.go with extra
// debugging information attached. For documentation, see types.go.
/*
#cgo darwin LDFLAGS: -framework OpenGL
#cgo linux LDFLAGS: -lGLESv2
#cgo darwin CFLAGS: -DGOOS_darwin
#cgo linux CFLAGS: -DGOOS_linux
#ifdef GOOS_linux
#include <GLES2/gl2.h>
#endif
#ifdef GOOS_darwin
#include <OpenGL/gl3.h>
#endif
*/
// #include "work.h"
import "C"
import "fmt"
@ -63,15 +50,15 @@ type Uniform struct {
name string
}
func (v Attrib) c() C.GLuint { return C.GLuint(v.Value) }
func (v Enum) c() C.GLenum { return C.GLenum(v) }
func (v Program) c() C.GLuint { return C.GLuint(v.Value) }
func (v Shader) c() C.GLuint { return C.GLuint(v.Value) }
func (v Buffer) c() C.GLuint { return C.GLuint(v.Value) }
func (v Framebuffer) c() C.GLuint { return C.GLuint(v.Value) }
func (v Renderbuffer) c() C.GLuint { return C.GLuint(v.Value) }
func (v Texture) c() C.GLuint { return C.GLuint(v.Value) }
func (v Uniform) c() C.GLint { return C.GLint(v.Value) }
func (v Attrib) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Enum) c() C.uintptr_t { return C.uintptr_t(v) }
func (v Program) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Shader) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Buffer) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Framebuffer) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Renderbuffer) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Texture) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Uniform) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Attrib) String() string { return fmt.Sprintf("Attrib(%d:%s)", v.Value, v.name) }
func (v Program) String() string { return fmt.Sprintf("Program(%d)", v.Value) }

View File

@ -7,17 +7,7 @@
package gl
/*
#ifdef os_linux
#include <GLES2/gl2.h>
#endif
#ifdef os_ios
#include <OpenGLES/ES2/gl.h>
#endif
#ifdef os_darwin_amd64
#include <OpenGL/gl3.h>
#endif
*/
// #include "work.h"
import "C"
import "fmt"
@ -68,15 +58,15 @@ type Uniform struct {
Value int32
}
func (v Attrib) c() C.GLuint { return C.GLuint(v.Value) }
func (v Enum) c() C.GLenum { return C.GLenum(v) }
func (v Program) c() C.GLuint { return C.GLuint(v.Value) }
func (v Shader) c() C.GLuint { return C.GLuint(v.Value) }
func (v Buffer) c() C.GLuint { return C.GLuint(v.Value) }
func (v Framebuffer) c() C.GLuint { return C.GLuint(v.Value) }
func (v Renderbuffer) c() C.GLuint { return C.GLuint(v.Value) }
func (v Texture) c() C.GLuint { return C.GLuint(v.Value) }
func (v Uniform) c() C.GLint { return C.GLint(v.Value) }
func (v Attrib) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Enum) c() C.uintptr_t { return C.uintptr_t(v) }
func (v Program) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Shader) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Buffer) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Framebuffer) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Renderbuffer) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Texture) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Uniform) c() C.uintptr_t { return C.uintptr_t(v.Value) }
func (v Attrib) String() string { return fmt.Sprintf("Attrib(%d)", v.Value) }
func (v Program) String() string { return fmt.Sprintf("Program(%d)", v.Value) }

479
gl/work.c Normal file
View File

@ -0,0 +1,479 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include <stdlib.h>
#include "_cgo_export.h"
#include "work.h"
void processFn(struct fnargs* args) {
switch (args->fn) {
case glfnUNDEFINED:
abort(); // bad glfn
break;
case glfnStop:
abort(); // should never make it into C
break;
case glfnActiveTexture:
glActiveTexture((GLenum)args->a0);
break;
case glfnAttachShader:
glAttachShader((GLint)args->a0, (GLint)args->a1);
break;
case glfnBindAttribLocation:
glBindAttribLocation((GLint)args->a0, (GLint)args->a1, (GLchar*)args->a2);
free((void*)args->a2);
break;
case glfnBindBuffer:
glBindBuffer((GLenum)args->a0, (GLuint)args->a1);
break;
case glfnBindFramebuffer:
glBindFramebuffer((GLenum)args->a0, (GLint)args->a1);
break;
case glfnBindRenderbuffer:
glBindRenderbuffer((GLenum)args->a0, (GLint)args->a1);
break;
case glfnBindTexture:
glBindTexture((GLenum)args->a0, (GLint)args->a1);
break;
case glfnBlendColor:
glBlendColor(*(GLfloat*)&args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2, *(GLfloat*)&args->a3);
break;
case glfnBlendEquation:
glBlendEquation((GLenum)args->a0);
break;
case glfnBlendEquationSeparate:
glBlendEquationSeparate((GLenum)args->a0, (GLenum)args->a1);
break;
case glfnBlendFunc:
glBlendFunc((GLenum)args->a0, (GLenum)args->a1);
break;
case glfnBlendFuncSeparate:
glBlendFuncSeparate((GLenum)args->a0, (GLenum)args->a1, (GLenum)args->a2, (GLenum)args->a3);
break;
case glfnBufferData:
glBufferData((GLenum)args->a0, (GLsizeiptr)args->a1, (GLvoid*)args->a2, (GLenum)args->a3);
break;
case glfnBufferSubData:
glBufferSubData((GLenum)args->a0, (GLint)args->a1, (GLsizeiptr)args->a2, (GLvoid*)args->a3);
break;
case glfnCheckFramebufferStatus:
ret = glCheckFramebufferStatus((GLenum)args->a0);
break;
case glfnClear:
glClear((GLenum)args->a0);
break;
case glfnClearColor:
glClearColor(*(GLfloat*)&args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2, *(GLfloat*)&args->a3);
break;
case glfnClearDepthf:
glClearDepthf(*(GLfloat*)&args->a0);
break;
case glfnClearStencil:
glClearStencil((GLint)args->a0);
break;
case glfnColorMask:
glColorMask((GLboolean)args->a0, (GLboolean)args->a1, (GLboolean)args->a2, (GLboolean)args->a3);
break;
case glfnCompileShader:
glCompileShader((GLint)args->a0);
break;
case glfnCompressedTexImage2D:
glCompressedTexImage2D((GLenum)args->a0, (GLint)args->a1, (GLenum)args->a2, (GLint)args->a3, (GLint)args->a4, (GLint)args->a5, (GLsizeiptr)args->a6, (GLvoid*)args->a7);
break;
case glfnCompressedTexSubImage2D:
glCompressedTexSubImage2D((GLenum)args->a0, (GLint)args->a1, (GLint)args->a2, (GLint)args->a3, (GLint)args->a4, (GLint)args->a5, (GLenum)args->a6, (GLsizeiptr)args->a7, (GLvoid*)args->a8);
break;
case glfnCopyTexImage2D:
glCopyTexImage2D((GLenum)args->a0, (GLint)args->a1, (GLenum)args->a2, (GLint)args->a3, (GLint)args->a4, (GLint)args->a5, (GLint)args->a6, (GLint)args->a7);
break;
case glfnCopyTexSubImage2D:
glCopyTexSubImage2D((GLenum)args->a0, (GLint)args->a1, (GLint)args->a2, (GLint)args->a3, (GLint)args->a4, (GLint)args->a5, (GLint)args->a6, (GLint)args->a7);
break;
case glfnCreateProgram:
ret = glCreateProgram();
break;
case glfnCreateShader:
ret = glCreateShader((GLenum)args->a0);
break;
case glfnCullFace:
glCullFace((GLenum)args->a0);
break;
case glfnDeleteBuffer:
glDeleteBuffers(1, (const GLuint*)(&args->a0));
break;
case glfnDeleteFramebuffer:
glDeleteFramebuffers(1, (const GLuint*)(&args->a0));
break;
case glfnDeleteProgram:
glDeleteProgram((GLint)args->a0);
break;
case glfnDeleteRenderbuffer:
glDeleteRenderbuffers(1, (const GLuint*)(&args->a0));
break;
case glfnDeleteShader:
glDeleteShader((GLint)args->a0);
break;
case glfnDeleteTexture:
glDeleteTextures(1, (const GLuint*)(&args->a0));
break;
case glfnDepthFunc:
glDepthFunc((GLenum)args->a0);
break;
case glfnDepthMask:
glDepthMask((GLboolean)args->a0);
break;
case glfnDepthRangef:
glDepthRangef(*(GLfloat*)&args->a0, *(GLfloat*)&args->a1);
break;
case glfnDetachShader:
glDetachShader((GLint)args->a0, (GLint)args->a1);
break;
case glfnDisable:
glDisable((GLenum)args->a0);
break;
case glfnDisableVertexAttribArray:
glDisableVertexAttribArray((GLint)args->a0);
break;
case glfnDrawArrays:
glDrawArrays((GLenum)args->a0, (GLint)args->a1, (GLint)args->a2);
break;
case glfnDrawElements:
glDrawElements((GLenum)args->a0, (GLint)args->a1, (GLenum)args->a2, (void*)args->a3);
break;
case glfnEnable:
glEnable((GLenum)args->a0);
break;
case glfnEnableVertexAttribArray:
glEnableVertexAttribArray((GLint)args->a0);
break;
case glfnFinish:
glFinish();
break;
case glfnFlush:
glFlush();
break;
case glfnFramebufferRenderbuffer:
glFramebufferRenderbuffer((GLenum)args->a0, (GLenum)args->a1, (GLenum)args->a2, (GLint)args->a3);
break;
case glfnFramebufferTexture2D:
glFramebufferTexture2D((GLenum)args->a0, (GLenum)args->a1, (GLenum)args->a2, (GLint)args->a3, (GLint)args->a4);
break;
case glfnFrontFace:
glFrontFace((GLenum)args->a0);
break;
case glfnGenBuffer:
glGenBuffers(1, (GLuint*)&ret);
break;
case glfnGenFramebuffer:
glGenFramebuffers(1, (GLuint*)&ret);
break;
case glfnGenRenderbuffer:
glGenRenderbuffers(1, (GLuint*)&ret);
break;
case glfnGenTexture:
glGenTextures(1, (GLuint*)&ret);
break;
case glfnGenerateMipmap:
glGenerateMipmap((GLenum)args->a0);
break;
case glfnGetActiveAttrib:
glGetActiveAttrib(
(GLuint)args->a0,
(GLuint)args->a1,
(GLsizei)args->a2,
NULL,
(GLint*)args->a4,
(GLenum*)args->a5,
(GLchar*)args->a6);
break;
case glfnGetActiveUniform:
glGetActiveUniform(
(GLuint)args->a0,
(GLuint)args->a1,
(GLsizei)args->a2,
NULL,
(GLint*)args->a4,
(GLenum*)args->a5,
(GLchar*)args->a6);
break;
case glfnGetAttachedShaders:
glGetAttachedShaders((GLuint)args->a0, (GLsizei)args->a1, (GLsizei*)args->a2, (GLuint*)args->a3);
break;
case glfnGetAttribLocation:
ret = glGetAttribLocation((GLint)args->a0, (GLchar*)args->a1);
free((void*)args->a1);
break;
case glfnGetBooleanv:
glGetBooleanv((GLenum)args->a0, (GLboolean*)args->a1);
break;
case glfnGetBufferParameteri:
glGetBufferParameteriv((GLenum)args->a0, (GLenum)args->a1, (GLint*)&ret);
break;
case glfnGetFloatv:
glGetFloatv((GLenum)args->a0, (GLfloat*)args->a1);
break;
case glfnGetIntegerv:
glGetIntegerv((GLenum)args->a0, (GLint*)args->a1);
break;
case glfnGetError:
ret = glGetError();
break;
case glfnGetFramebufferAttachmentParameteriv:
glGetFramebufferAttachmentParameteriv((GLenum)args->a0, (GLenum)args->a1, (GLenum)args->a2, (GLint*)&ret);
break;
case glfnGetProgramiv:
glGetProgramiv((GLint)args->a0, (GLenum)args->a1, (GLint*)&ret);
break;
case glfnGetProgramInfoLog:
glGetProgramInfoLog((GLuint)args->a0, (GLsizei)args->a1, (GLsizei*)args->a2, (GLchar*)args->a3);
break;
case glfnGetRenderbufferParameteriv:
glGetRenderbufferParameteriv((GLenum)args->a0, (GLenum)args->a1, (GLint*)&ret);
break;
case glfnGetShaderiv:
glGetShaderiv((GLint)args->a0, (GLenum)args->a1, (GLint*)&ret);
break;
case glfnGetShaderInfoLog:
glGetShaderInfoLog((GLuint)args->a0, (GLsizei)args->a1, (GLsizei*)args->a2, (GLchar*)args->a3);
break;
case glfnGetShaderPrecisionFormat:
glGetShaderPrecisionFormat((GLenum)args->a0, (GLenum)args->a1, (GLint*)args->a2, (GLint*)args->a3);
break;
case glfnGetShaderSource:
glGetShaderSource((GLuint)args->a0, (GLsizei)args->a1, (GLsizei*)args->a2, (GLchar*)args->a3);
break;
case glfnGetString:
ret = (uintptr_t)glGetString((GLenum)args->a0);
break;
case glfnGetTexParameterfv:
glGetTexParameterfv((GLenum)args->a0, (GLenum)args->a1, (GLfloat*)args->a2);
break;
case glfnGetTexParameteriv:
glGetTexParameteriv((GLenum)args->a0, (GLenum)args->a1, (GLint*)args->a2);
break;
case glfnGetUniformfv:
glGetUniformfv((GLuint)args->a0, (GLint)args->a1, (GLfloat*)args->a2);
break;
case glfnGetUniformiv:
glGetUniformiv((GLuint)args->a0, (GLint)args->a1, (GLint*)args->a2);
break;
case glfnGetUniformLocation:
ret = glGetUniformLocation((GLint)args->a0, (GLchar*)args->a1);
free((void*)args->a1);
break;
case glfnGetVertexAttribfv:
glGetVertexAttribfv((GLuint)args->a0, (GLenum)args->a1, (GLfloat*)args->a2);
break;
case glfnGetVertexAttribiv:
glGetVertexAttribiv((GLuint)args->a0, (GLenum)args->a1, (GLint*)args->a2);
break;
case glfnHint:
glHint((GLenum)args->a0, (GLenum)args->a1);
break;
case glfnIsBuffer:
ret = glIsBuffer((GLint)args->a0);
break;
case glfnIsEnabled:
ret = glIsEnabled((GLenum)args->a0);
break;
case glfnIsFramebuffer:
ret = glIsFramebuffer((GLint)args->a0);
break;
case glfnIsProgram:
ret = glIsProgram((GLint)args->a0);
break;
case glfnIsRenderbuffer:
ret = glIsRenderbuffer((GLint)args->a0);
break;
case glfnIsShader:
ret = glIsShader((GLint)args->a0);
break;
case glfnIsTexture:
ret = glIsTexture((GLint)args->a0);
break;
case glfnLineWidth:
glLineWidth(*(GLfloat*)&args->a0);
break;
case glfnLinkProgram:
glLinkProgram((GLint)args->a0);
break;
case glfnPixelStorei:
glPixelStorei((GLenum)args->a0, (GLint)args->a1);
break;
case glfnPolygonOffset:
glPolygonOffset(*(GLfloat*)&args->a0, *(GLfloat*)&args->a1);
break;
case glfnReadPixels:
glReadPixels((GLint)args->a0, (GLint)args->a1, (GLsizei)args->a2, (GLsizei)args->a3, (GLenum)args->a4, (GLenum)args->a5, (void*)args->a6);
break;
case glfnReleaseShaderCompiler:
glReleaseShaderCompiler();
break;
case glfnRenderbufferStorage:
glRenderbufferStorage((GLenum)args->a0, (GLenum)args->a1, (GLint)args->a2, (GLint)args->a3);
break;
case glfnSampleCoverage:
glSampleCoverage(*(GLfloat*)&args->a0, (GLboolean)args->a1);
break;
case glfnScissor:
glScissor((GLint)args->a0, (GLint)args->a1, (GLint)args->a2, (GLint)args->a3);
break;
case glfnShaderSource:
#if defined(os_ios) || defined(os_osx)
glShaderSource((GLuint)args->a0, (GLsizei)args->a1, (const GLchar *const *)args->a2, NULL);
#else
glShaderSource((GLuint)args->a0, (GLsizei)args->a1, (const GLchar **)args->a2, NULL);
#endif
free(*(void**)args->a2);
free((void*)args->a2);
break;
case glfnStencilFunc:
glStencilFunc((GLenum)args->a0, (GLint)args->a1, (GLuint)args->a2);
break;
case glfnStencilFuncSeparate:
glStencilFuncSeparate((GLenum)args->a0, (GLenum)args->a1, (GLint)args->a2, (GLuint)args->a3);
break;
case glfnStencilMask:
glStencilMask((GLuint)args->a0);
break;
case glfnStencilMaskSeparate:
glStencilMaskSeparate((GLenum)args->a0, (GLuint)args->a1);
break;
case glfnStencilOp:
glStencilOp((GLenum)args->a0, (GLenum)args->a1, (GLenum)args->a2);
break;
case glfnStencilOpSeparate:
glStencilOpSeparate((GLenum)args->a0, (GLenum)args->a1, (GLenum)args->a2, (GLenum)args->a3);
break;
case glfnTexImage2D:
glTexImage2D(
(GLenum)args->a0,
(GLint)args->a1,
(GLint)args->a2,
(GLsizei)args->a3,
(GLsizei)args->a4,
0, // border
(GLenum)args->a5,
(GLenum)args->a6,
(const GLvoid*)args->a7);
break;
case glfnTexSubImage2D:
glTexSubImage2D(
(GLenum)args->a0,
(GLint)args->a1,
(GLint)args->a2,
(GLint)args->a3,
(GLsizei)args->a4,
(GLsizei)args->a5,
(GLenum)args->a6,
(GLenum)args->a7,
(const GLvoid*)args->a8);
break;
case glfnTexParameterf:
glTexParameterf((GLenum)args->a0, (GLenum)args->a1, *(GLfloat*)&args->a2);
break;
case glfnTexParameterfv:
glTexParameterfv((GLenum)args->a0, (GLenum)args->a1, (GLfloat*)args->a2);
break;
case glfnTexParameteri:
glTexParameteri((GLenum)args->a0, (GLenum)args->a1, (GLint)args->a2);
break;
case glfnTexParameteriv:
glTexParameteriv((GLenum)args->a0, (GLenum)args->a1, (GLint*)args->a2);
break;
case glfnUniform1f:
glUniform1f((GLint)args->a0, *(GLfloat*)&args->a1);
break;
case glfnUniform1fv:
glUniform1fv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)args->a2);
break;
case glfnUniform1i:
glUniform1i((GLint)args->a0, (GLint)args->a1);
break;
case glfnUniform1iv:
glUniform1iv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)args->a2);
break;
case glfnUniform2f:
glUniform2f((GLint)args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2);
break;
case glfnUniform2fv:
glUniform2fv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)args->a2);
break;
case glfnUniform2i:
glUniform2i((GLint)args->a0, (GLint)args->a1, (GLint)args->a2);
break;
case glfnUniform2iv:
glUniform2iv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)args->a2);
break;
case glfnUniform3f:
glUniform3f((GLint)args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2, *(GLfloat*)&args->a3);
break;
case glfnUniform3fv:
glUniform3fv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)args->a2);
break;
case glfnUniform3i:
glUniform3i((GLint)args->a0, (GLint)args->a1, (GLint)args->a2, (GLint)args->a3);
break;
case glfnUniform3iv:
glUniform3iv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)args->a2);
break;
case glfnUniform4f:
glUniform4f((GLint)args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2, *(GLfloat*)&args->a3, *(GLfloat*)&args->a4);
break;
case glfnUniform4fv:
glUniform4fv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)args->a2);
break;
case glfnUniform4i:
glUniform4i((GLint)args->a0, (GLint)args->a1, (GLint)args->a2, (GLint)args->a3, (GLint)args->a4);
break;
case glfnUniform4iv:
glUniform4iv((GLint)args->a0, (GLsizeiptr)args->a1, (GLvoid*)args->a2);
break;
case glfnUniformMatrix2fv:
glUniformMatrix2fv((GLint)args->a0, (GLsizeiptr)args->a1, 0, (GLvoid*)args->a2);
break;
case glfnUniformMatrix3fv:
glUniformMatrix3fv((GLint)args->a0, (GLsizeiptr)args->a1, 0, (GLvoid*)args->a2);
break;
case glfnUniformMatrix4fv:
glUniformMatrix4fv((GLint)args->a0, (GLsizeiptr)args->a1, 0, (GLvoid*)args->a2);
break;
case glfnUseProgram:
glUseProgram((GLint)args->a0);
break;
case glfnValidateProgram:
glValidateProgram((GLint)args->a0);
break;
case glfnVertexAttrib1f:
glVertexAttrib1f((GLint)args->a0, *(GLfloat*)&args->a1);
break;
case glfnVertexAttrib1fv:
glVertexAttrib1fv((GLint)args->a0, (GLfloat*)args->a1);
break;
case glfnVertexAttrib2f:
glVertexAttrib2f((GLint)args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2);
break;
case glfnVertexAttrib2fv:
glVertexAttrib2fv((GLint)args->a0, (GLfloat*)args->a1);
break;
case glfnVertexAttrib3f:
glVertexAttrib3f((GLint)args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2, *(GLfloat*)&args->a3);
break;
case glfnVertexAttrib3fv:
glVertexAttrib3fv((GLint)args->a0, (GLfloat*)args->a1);
break;
case glfnVertexAttrib4f:
glVertexAttrib4f((GLint)args->a0, *(GLfloat*)&args->a1, *(GLfloat*)&args->a2, *(GLfloat*)&args->a3, *(GLfloat*)&args->a4);
break;
case glfnVertexAttrib4fv:
glVertexAttrib4fv((GLint)args->a0, (GLfloat*)args->a1);
break;
case glfnVertexAttribPointer:
glVertexAttribPointer((GLuint)args->a0, (GLint)args->a1, (GLenum)args->a2, (GLboolean)args->a3, (GLsizei)args->a4, (const GLvoid*)args->a5);
break;
case glfnViewport:
glViewport((GLint)args->a0, (GLint)args->a1, (GLint)args->a2, (GLint)args->a3);
break;
}
}

130
gl/work.go Normal file
View File

@ -0,0 +1,130 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gl
/*
#cgo darwin,amd64 LDFLAGS: -framework OpenGL
#cgo darwin,arm LDFLAGS: -framework OpenGLES
#cgo darwin,arm64 LDFLAGS: -framework OpenGLES
#cgo linux LDFLAGS: -lGLESv2
#cgo darwin,amd64 CFLAGS: -Dos_osx
#cgo darwin,arm CFLAGS: -Dos_ios
#cgo darwin,arm64 CFLAGS: -Dos_ios
#cgo linux CFLAGS: -Dos_linux
#include <stdint.h>
#include "work.h"
struct fnargs cargs[10];
uintptr_t ret;
void process(int count) {
int i;
for (i = 0; i < count; i++) {
processFn(&cargs[i]);
}
}
*/
import "C"
import "runtime"
// work is a queue of calls to execute.
var work = make(chan call, 10)
// retvalue is sent a return value when blocking calls complete.
// It is safe to use a global unbuffered channel here as calls
// cannot currently be made concurrently.
var retvalue = make(chan C.uintptr_t)
type call struct {
blocking bool
fn func()
args C.struct_fnargs
}
// Do calls fn on the OS thread with the GL context.
func Do(fn func()) {
work <- call{
fn: fn,
blocking: true,
}
<-retvalue
}
// Stop stops the current GL processing.
func Stop() {
var call call
call.blocking = true
call.args.fn = C.glfnStop
work <- call
<-retvalue
}
// Start executes GL functions on a fixed OS thread, starting with initCtx.
// It blocks until Stop is called. Typical use:
//
// go gl.Start(func() {
// // establish a GL context, using for example, EGL.
// })
//
// // long running GL calls from any goroutine
//
// gl.Stop()
func Start(initCtx func()) {
runtime.LockOSThread()
initCtx()
queue := make([]call, 0, len(work))
for {
// Wait until at least one piece of work is ready.
// Accumulate work until a piece is marked as blocking.
select {
case w := <-work:
queue = append(queue, w)
}
blocking := queue[len(queue)-1].blocking
enqueue:
for len(queue) < cap(queue) && !blocking {
select {
case w := <-work:
queue = append(queue, w)
blocking = queue[len(queue)-1].blocking
default:
break enqueue
}
}
// Process the queued GL functions.
fn := queue[len(queue)-1].fn
stop := queue[len(queue)-1].args.fn == C.glfnStop
if fn != nil || stop {
queue = queue[:len(queue)-1]
}
for i := range queue {
C.cargs[i] = queue[i].args
}
C.process(C.int(len(queue)))
if fn != nil {
fn()
}
// Cleanup and signal.
queue = queue[:0]
if blocking {
retvalue <- C.ret
}
if stop {
return
}
}
}
func glBoolean(b bool) C.uintptr_t {
if b {
return TRUE
}
return FALSE
}

180
gl/work.h Normal file
View File

@ -0,0 +1,180 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifdef os_linux
#include <GLES2/gl2.h>
#endif
#ifdef os_ios
#include <OpenGLES/ES2/glext.h>
#endif
#ifdef os_osx
#include <OpenGL/gl3.h>
#endif
#include <stdint.h>
#include <stdlib.h>
typedef enum {
glfnUNDEFINED,
glfnStop,
glfnActiveTexture,
glfnAttachShader,
glfnBindAttribLocation,
glfnBindBuffer,
glfnBindFramebuffer,
glfnBindRenderbuffer,
glfnBindTexture,
glfnBlendColor,
glfnBlendEquation,
glfnBlendEquationSeparate,
glfnBlendFunc,
glfnBlendFuncSeparate,
glfnBufferData,
glfnBufferSubData,
glfnCheckFramebufferStatus,
glfnClear,
glfnClearColor,
glfnClearDepthf,
glfnClearStencil,
glfnColorMask,
glfnCompileShader,
glfnCompressedTexImage2D,
glfnCompressedTexSubImage2D,
glfnCopyTexImage2D,
glfnCopyTexSubImage2D,
glfnCreateProgram,
glfnCreateShader,
glfnCullFace,
glfnDeleteBuffer,
glfnDeleteFramebuffer,
glfnDeleteProgram,
glfnDeleteRenderbuffer,
glfnDeleteShader,
glfnDeleteTexture,
glfnDepthFunc,
glfnDepthRangef,
glfnDepthMask,
glfnDetachShader,
glfnDisable,
glfnDisableVertexAttribArray,
glfnDrawArrays,
glfnDrawElements,
glfnEnable,
glfnEnableVertexAttribArray,
glfnFinish,
glfnFlush,
glfnFramebufferRenderbuffer,
glfnFramebufferTexture2D,
glfnFrontFace,
glfnGenBuffer,
glfnGenFramebuffer,
glfnGenRenderbuffer,
glfnGenTexture,
glfnGenerateMipmap,
glfnGetActiveAttrib,
glfnGetActiveUniform,
glfnGetAttachedShaders,
glfnGetAttribLocation,
glfnGetBooleanv,
glfnGetBufferParameteri,
glfnGetError,
glfnGetFloatv,
glfnGetFramebufferAttachmentParameteriv,
glfnGetIntegerv,
glfnGetProgramInfoLog,
glfnGetProgramiv,
glfnGetRenderbufferParameteriv,
glfnGetShaderInfoLog,
glfnGetShaderPrecisionFormat,
glfnGetShaderSource,
glfnGetShaderiv,
glfnGetString,
glfnGetTexParameterfv,
glfnGetTexParameteriv,
glfnGetUniformLocation,
glfnGetUniformfv,
glfnGetUniformiv,
glfnGetVertexAttribfv,
glfnGetVertexAttribiv,
glfnHint,
glfnIsBuffer,
glfnIsEnabled,
glfnIsFramebuffer,
glfnIsProgram,
glfnIsRenderbuffer,
glfnIsShader,
glfnIsTexture,
glfnLineWidth,
glfnLinkProgram,
glfnPixelStorei,
glfnPolygonOffset,
glfnReadPixels,
glfnReleaseShaderCompiler,
glfnRenderbufferStorage,
glfnSampleCoverage,
glfnScissor,
glfnShaderSource,
glfnStencilFunc,
glfnStencilFuncSeparate,
glfnStencilMask,
glfnStencilMaskSeparate,
glfnStencilOp,
glfnStencilOpSeparate,
glfnTexImage2D,
glfnTexParameterf,
glfnTexParameterfv,
glfnTexParameteri,
glfnTexParameteriv,
glfnTexSubImage2D,
glfnUniform1f,
glfnUniform1fv,
glfnUniform1i,
glfnUniform1iv,
glfnUniform2f,
glfnUniform2fv,
glfnUniform2i,
glfnUniform2iv,
glfnUniform3f,
glfnUniform3fv,
glfnUniform3i,
glfnUniform3iv,
glfnUniform4f,
glfnUniform4fv,
glfnUniform4i,
glfnUniform4iv,
glfnUniformMatrix2fv,
glfnUniformMatrix3fv,
glfnUniformMatrix4fv,
glfnUseProgram,
glfnValidateProgram,
glfnVertexAttrib1f,
glfnVertexAttrib1fv,
glfnVertexAttrib2f,
glfnVertexAttrib2fv,
glfnVertexAttrib3f,
glfnVertexAttrib3fv,
glfnVertexAttrib4f,
glfnVertexAttrib4fv,
glfnVertexAttribPointer,
glfnViewport,
} glfn;
struct fnargs {
glfn fn;
uintptr_t a0;
uintptr_t a1;
uintptr_t a2;
uintptr_t a3;
uintptr_t a4;
uintptr_t a5;
uintptr_t a6;
uintptr_t a7;
uintptr_t a8;
};
extern uintptr_t ret;
extern void processFn(struct fnargs* args);