aboutsummaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/mobile/gl/work.go
blob: 9f340c10992752c3ae76e46238f1bdb214eb92e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// 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.

// +build darwin linux openbsd

package gl

/*
#cgo ios                LDFLAGS: -framework OpenGLES
#cgo darwin,amd64,!ios  LDFLAGS: -framework OpenGL
#cgo darwin,arm         LDFLAGS: -framework OpenGLES
#cgo darwin,arm64       LDFLAGS: -framework OpenGLES
#cgo linux              LDFLAGS: -lGLESv2
#cgo openbsd            LDFLAGS: -L/usr/X11R6/lib/ -lGLESv2

#cgo android            CFLAGS: -Dos_android
#cgo ios                CFLAGS: -Dos_ios
#cgo darwin,amd64,!ios  CFLAGS: -Dos_osx
#cgo darwin,arm         CFLAGS: -Dos_ios
#cgo darwin,arm64       CFLAGS: -Dos_ios
#cgo darwin             CFLAGS: -DGL_SILENCE_DEPRECATION
#cgo linux              CFLAGS: -Dos_linux
#cgo openbsd            CFLAGS: -Dos_openbsd

#cgo openbsd            CFLAGS: -I/usr/X11R6/include/

#include <stdint.h>
#include "work.h"

uintptr_t process(struct fnargs* cargs, char* parg0, char* parg1, char* parg2, int count) {
	uintptr_t ret;

	ret = processFn(&cargs[0], parg0);
	if (count > 1) {
		ret = processFn(&cargs[1], parg1);
	}
	if (count > 2) {
		ret = processFn(&cargs[2], parg2);
	}

	return ret;
}
*/
import "C"

import "unsafe"

const workbufLen = 3

type context struct {
	cptr  uintptr
	debug int32

	workAvailable chan struct{}

	// work is a queue of calls to execute.
	work chan call

	// 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.
	//
	// TODO: the comment above about concurrent calls isn't actually true: package
	// app calls package gl, but it has to do so in a separate goroutine, which
	// means that its gl calls (which may be blocking) can race with other gl calls
	// in the main program. We should make it safe to issue blocking gl calls
	// concurrently, or get the gl calls out of package app, or both.
	retvalue chan C.uintptr_t

	cargs [workbufLen]C.struct_fnargs
	parg  [workbufLen]*C.char
}

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.
func NewContext() (Context, Worker) {
	glctx := &context{
		workAvailable: make(chan struct{}, 1),
		work:          make(chan call, workbufLen),
		retvalue:      make(chan C.uintptr_t),
	}
	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 {
	ctx.work <- c

	select {
	case ctx.workAvailable <- struct{}{}:
	default:
	}

	if c.blocking {
		return uintptr(<-ctx.retvalue)
	}
	return 0
}

func (ctx *context) DoWork() {
	queue := make([]call, 0, workbufLen)
	for {
		// Wait until at least one piece of work is ready.
		// Accumulate work until a piece is marked as blocking.
		select {
		case w := <-ctx.work:
			queue = append(queue, w)
		default:
			return
		}
		blocking := queue[len(queue)-1].blocking
	enqueue:
		for len(queue) < cap(queue) && !blocking {
			select {
			case w := <-ctx.work:
				queue = append(queue, w)
				blocking = queue[len(queue)-1].blocking
			default:
				break enqueue
			}
		}

		// Process the queued GL functions.
		for i, q := range queue {
			ctx.cargs[i] = *(*C.struct_fnargs)(unsafe.Pointer(&q.args))
			ctx.parg[i] = (*C.char)(q.parg)
		}
		ret := C.process(&ctx.cargs[0], ctx.parg[0], ctx.parg[1], ctx.parg[2], C.int(len(queue)))

		// Cleanup and signal.
		queue = queue[:0]
		if blocking {
			ctx.retvalue <- ret
		}
	}
}

func init() {
	if unsafe.Sizeof(C.GLint(0)) != unsafe.Sizeof(int32(0)) {
		panic("GLint is not an int32")
	}
}

// cString creates C string off the Go heap.
// ret is a *char.
func (ctx *context) cString(str string) (uintptr, func()) {
	ptr := unsafe.Pointer(C.CString(str))
	return uintptr(ptr), func() { C.free(ptr) }
}

// cString creates a pointer to a C string off the Go heap.
// ret is a **char.
func (ctx *context) cStringPtr(str string) (uintptr, func()) {
	s, free := ctx.cString(str)
	ptr := C.malloc(C.size_t(unsafe.Sizeof((*int)(nil))))
	*(*uintptr)(ptr) = s
	return uintptr(ptr), func() {
		free()
		C.free(ptr)
	}
}