1 // SPDX-License-Identifier: Unlicense OR MIT
2
3 // GL types, constants, and Functions wrapper over jsbridge/webgl.
4 // WASM/WebGL2 only. GL handle types are prefixed with GL to avoid
5 // conflicts with the driver.Buffer/Texture interfaces.
6
7 package gio
8
9 import (
10 "bytes"
11 "errors"
12 "fmt"
13 "unsafe"
14
15 "jsbridge/webgl"
16 )
17
18 type (
19 Enum uint32
20 GLAttrib uint32
21 )
22
23 const (
24 ACTIVE_TEXTURE = 0x84E0
25 ALL_BARRIER_BITS = 0xffffffff
26 ARRAY_BUFFER = 0x8892
27 ARRAY_BUFFER_BINDING = 0x8894
28 BACK = 0x0405
29 BLEND = 0xbe2
30 BLEND_DST_RGB = 0x80C8
31 BLEND_SRC_RGB = 0x80C9
32 BLEND_DST_ALPHA = 0x80CA
33 BLEND_SRC_ALPHA = 0x80CB
34 CLAMP_TO_EDGE = 0x812f
35 COLOR_ATTACHMENT0 = 0x8ce0
36 COLOR_BUFFER_BIT = 0x4000
37 COLOR_CLEAR_VALUE = 0x0C22
38 COMPILE_STATUS = 0x8b81
39 CURRENT_PROGRAM = 0x8B8D
40 DEPTH_ATTACHMENT = 0x8d00
41 DEPTH_BUFFER_BIT = 0x100
42 DEPTH_CLEAR_VALUE = 0x0B73
43 DEPTH_COMPONENT16 = 0x81a5
44 DEPTH_COMPONENT24 = 0x81A6
45 DEPTH_COMPONENT32F = 0x8CAC
46 DEPTH_FUNC = 0x0B74
47 DEPTH_TEST = 0xb71
48 DEPTH_WRITEMASK = 0x0B72
49 DRAW_FRAMEBUFFER = 0x8CA9
50 DST_COLOR = 0x306
51 DYNAMIC_DRAW = 0x88E8
52 ELEMENT_ARRAY_BUFFER = 0x8893
53 ELEMENT_ARRAY_BUFFER_BINDING = 0x8895
54 EXTENSIONS = 0x1f03
55 FALSE = 0
56 FLOAT = 0x1406
57 FRAGMENT_SHADER = 0x8b30
58 FRAMEBUFFER = 0x8d40
59 FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING = 0x8210
60 FRAMEBUFFER_BINDING = 0x8ca6
61 FRAMEBUFFER_COMPLETE = 0x8cd5
62 FRAMEBUFFER_SRGB = 0x8db9
63 HALF_FLOAT = 0x140b
64 HALF_FLOAT_OES = 0x8d61
65 INFO_LOG_LENGTH = 0x8B84
66 INVALID_INDEX = ^uint32(0)
67 GREATER = 0x204
68 GEQUAL = 0x206
69 LINEAR = 0x2601
70 LINEAR_MIPMAP_LINEAR = 0x2703
71 LINK_STATUS = 0x8b82
72 LUMINANCE = 0x1909
73 MAX_TEXTURE_SIZE = 0xd33
74 NEAREST = 0x2600
75 NO_ERROR = 0x0
76 ONE = 0x1
77 ONE_MINUS_SRC_ALPHA = 0x303
78 PACK_ROW_LENGTH = 0x0D02
79 R16F = 0x822d
80 RGBA16F = 0x881A
81 RGBA32F = 0x8814
82 R8 = 0x8229
83 READ_FRAMEBUFFER = 0x8ca8
84 READ_FRAMEBUFFER_BINDING = 0x8CAA
85 RED = 0x1903
86 RENDERER = 0x1F01
87 RENDERBUFFER = 0x8d41
88 RENDERBUFFER_BINDING = 0x8ca7
89 RGB = 0x1907
90 GL_RGBA = 0x1908
91 RGBA8 = 0x8058
92 SHORT = 0x1402
93 SRGB = 0x8c40
94 SRGB_ALPHA_EXT = 0x8c42
95 SRGB8 = 0x8c41
96 SRGB8_ALPHA8 = 0x8c43
97 STATIC_DRAW = 0x88e4
98 STENCIL_BUFFER_BIT = 0x00000400
99 TEXTURE_2D = 0xde1
100 TEXTURE_BINDING_2D = 0x8069
101 TEXTURE_MAG_FILTER = 0x2800
102 TEXTURE_MIN_FILTER = 0x2801
103 TEXTURE_WRAP_S = 0x2802
104 TEXTURE_WRAP_T = 0x2803
105 TEXTURE0 = 0x84c0
106 TEXTURE1 = 0x84c1
107 TRIANGLE_STRIP = 0x5
108 TRIANGLES = 0x4
109 TRUE = 1
110 UNIFORM_BUFFER = 0x8A11
111 UNIFORM_BUFFER_BINDING = 0x8A28
112 UNPACK_ALIGNMENT = 0xcf5
113 UNPACK_ROW_LENGTH = 0x0CF2
114 UNSIGNED_BYTE = 0x1401
115 UNSIGNED_SHORT = 0x1403
116 VIEWPORT = 0x0BA2
117 VERSION = 0x1f02
118 VERTEX_ARRAY_BINDING = 0x85B5
119 VERTEX_SHADER = 0x8b31
120 WRITE_ONLY = 0x88B9
121 ZERO = 0x0
122 )
123
124 // GL object handle types. Zero value means unbound/invalid.
125 // Prefixed GL to avoid conflicts with driver.Buffer/Texture interfaces.
126 type (
127 GLObject struct{ V int32 }
128 GLBuffer GLObject
129 GLFramebuffer GLObject
130 GLProgram GLObject
131 GLRenderbuffer GLObject
132 GLShader GLObject
133 GLTexture GLObject
134 GLUniform struct{ V int32 }
135 )
136
137 func (o GLObject) valid() bool { return o.V != 0 }
138 func (b GLBuffer) Valid() bool { return GLObject(b).valid() }
139 func (f GLFramebuffer) Valid() bool { return GLObject(f).valid() }
140 func (p GLProgram) Valid() bool { return GLObject(p).valid() }
141 func (s GLShader) Valid() bool { return GLObject(s).valid() }
142 func (u GLUniform) Valid() bool { return u.V != -1 }
143
144 func (f GLFramebuffer) Equal(f2 GLFramebuffer) bool { return f == f2 }
145 func (p GLProgram) Equal(p2 GLProgram) bool { return p == p2 }
146 func (s GLShader) Equal(s2 GLShader) bool { return s == s2 }
147 func (u GLUniform) Equal(u2 GLUniform) bool { return u == u2 }
148 func (b GLBuffer) Equal(b2 GLBuffer) bool { return b == b2 }
149 func (t GLTexture) Equal(t2 GLTexture) bool { return t == t2 }
150 func (r GLRenderbuffer) Equal(r2 GLRenderbuffer) bool { return r == r2 }
151
152 // GLContext is a WebGL2 rendering context handle.
153 type GLContext = webgl.Context
154
155 // Functions wraps all WebGL2 operations via jsbridge/webgl.
156 type Functions struct {
157 ctx webgl.Context
158 }
159
160 func NewFunctions(ctx GLContext) (*Functions, error) {
161 if ctx == 0 {
162 return nil, errors.New("gl: null context")
163 }
164 return &Functions{ctx: ctx}, nil
165 }
166
167 func (f *Functions) ActiveTexture(t Enum) {
168 webgl.ActiveTexture(f.ctx, int32(t))
169 }
170
171 func (f *Functions) AttachShader(p GLProgram, s GLShader) {
172 webgl.AttachShader(f.ctx, webgl.Program(p.V), webgl.Shader(s.V))
173 }
174
175 func (f *Functions) BindAttribLocation(p GLProgram, a GLAttrib, name string) {
176 webgl.BindAttribLocation(f.ctx, webgl.Program(p.V), int32(a), unsafe.StringData(name), int32(len(name)))
177 }
178
179 func (f *Functions) BindBuffer(target Enum, b GLBuffer) {
180 webgl.BindBuffer(f.ctx, int32(target), webgl.Buffer(b.V))
181 }
182
183 func (f *Functions) BindFramebuffer(target Enum, fb GLFramebuffer) {
184 webgl.BindFramebuffer(f.ctx, int32(target), webgl.Framebuffer(fb.V))
185 }
186
187 func (f *Functions) BindRenderbuffer(target Enum, rb GLRenderbuffer) {
188 webgl.BindRenderbuffer(f.ctx, int32(target), webgl.Renderbuffer(rb.V))
189 }
190
191 func (f *Functions) BindTexture(target Enum, t GLTexture) {
192 webgl.BindTexture(f.ctx, int32(target), webgl.Texture(t.V))
193 }
194
195 func (f *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) {
196 webgl.BlendFuncSeparate(f.ctx, int32(srcRGB), int32(dstRGB), int32(srcA), int32(dstA))
197 }
198
199 func (f *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
200 if data == nil {
201 webgl.BufferData(f.ctx, int32(target), nil, int32(size), int32(usage))
202 } else {
203 webgl.BufferData(f.ctx, int32(target), &data[0], int32(len(data)), int32(usage))
204 }
205 }
206
207 func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
208 if len(src) > 0 {
209 webgl.BufferSubData(f.ctx, int32(target), int32(offset), &src[0], int32(len(src)))
210 }
211 }
212
213 func (f *Functions) CheckFramebufferStatus(target Enum) Enum {
214 return Enum(webgl.CheckFramebufferStatus(f.ctx, int32(target)))
215 }
216
217 func (f *Functions) Clear(mask Enum) {
218 webgl.Clear(f.ctx, int32(mask))
219 }
220
221 func (f *Functions) ClearColor(red, green, blue, alpha float32) {
222 webgl.ClearColor(f.ctx, red, green, blue, alpha)
223 }
224
225 func (f *Functions) ClearDepthf(d float32) {
226 webgl.ClearDepth(f.ctx, d)
227 }
228
229 func (f *Functions) CompileShader(s GLShader) {
230 webgl.CompileShader(f.ctx, webgl.Shader(s.V))
231 }
232
233 func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) {
234 webgl.CopyTexSubImage2D(f.ctx, int32(target), int32(level), int32(xoffset), int32(yoffset), int32(x), int32(y), int32(width), int32(height))
235 }
236
237 func (f *Functions) CreateBuffer() GLBuffer {
238 return GLBuffer{V: int32(webgl.CreateBuffer(f.ctx))}
239 }
240
241 func (f *Functions) CreateFramebuffer() GLFramebuffer {
242 return GLFramebuffer{V: int32(webgl.CreateFramebuffer(f.ctx))}
243 }
244
245 func (f *Functions) CreateProgram() GLProgram {
246 return GLProgram{V: int32(webgl.CreateProgram(f.ctx))}
247 }
248
249 func (f *Functions) CreateRenderbuffer() GLRenderbuffer {
250 return GLRenderbuffer{V: int32(webgl.CreateRenderbuffer(f.ctx))}
251 }
252
253 func (f *Functions) CreateShader(ty Enum) GLShader {
254 return GLShader{V: int32(webgl.CreateShader(f.ctx, int32(ty)))}
255 }
256
257 func (f *Functions) CreateTexture() GLTexture {
258 return GLTexture{V: int32(webgl.CreateTexture(f.ctx))}
259 }
260
261 func (f *Functions) DeleteBuffer(v GLBuffer) {
262 webgl.DeleteBuffer(f.ctx, webgl.Buffer(v.V))
263 }
264
265 func (f *Functions) DeleteFramebuffer(v GLFramebuffer) {
266 webgl.DeleteFramebuffer(f.ctx, webgl.Framebuffer(v.V))
267 }
268
269 func (f *Functions) DeleteProgram(p GLProgram) {
270 webgl.DeleteProgram(f.ctx, webgl.Program(p.V))
271 }
272
273 func (f *Functions) DeleteShader(s GLShader) {
274 webgl.DeleteShader(f.ctx, webgl.Shader(s.V))
275 }
276
277 func (f *Functions) DeleteRenderbuffer(v GLRenderbuffer) {
278 webgl.DeleteRenderbuffer(f.ctx, webgl.Renderbuffer(v.V))
279 }
280
281 func (f *Functions) DeleteTexture(v GLTexture) {
282 webgl.DeleteTexture(f.ctx, webgl.Texture(v.V))
283 }
284
285 func (f *Functions) DepthFunc(fn Enum) {
286 webgl.DepthFunc(f.ctx, int32(fn))
287 }
288
289 func (f *Functions) DepthMask(mask bool) {
290 m := int32(0)
291 if mask {
292 m = 1
293 }
294 webgl.DepthMask(f.ctx, m)
295 }
296
297 func (f *Functions) DisableVertexAttribArray(a GLAttrib) {
298 webgl.DisableVertexAttribArray(f.ctx, int32(a))
299 }
300
301 func (f *Functions) Disable(cap Enum) {
302 webgl.Disable(f.ctx, int32(cap))
303 }
304
305 func (f *Functions) DrawArrays(mode Enum, first, count int) {
306 webgl.DrawArrays(f.ctx, int32(mode), int32(first), int32(count))
307 }
308
309 func (f *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) {
310 webgl.DrawElements(f.ctx, int32(mode), int32(count), int32(ty), int32(offset))
311 }
312
313 func (f *Functions) Enable(cap Enum) {
314 webgl.Enable(f.ctx, int32(cap))
315 }
316
317 func (f *Functions) EnableVertexAttribArray(a GLAttrib) {
318 webgl.EnableVertexAttribArray(f.ctx, int32(a))
319 }
320
321 func (f *Functions) Finish() {
322 webgl.Finish(f.ctx)
323 }
324
325 func (f *Functions) Flush() {
326 webgl.Flush(f.ctx)
327 }
328
329 func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer GLRenderbuffer) {
330 webgl.FramebufferRenderbuffer(f.ctx, int32(target), int32(attachment), int32(renderbuffertarget), webgl.Renderbuffer(renderbuffer.V))
331 }
332
333 func (f *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t GLTexture, level int) {
334 webgl.FramebufferTexture2D(f.ctx, int32(target), int32(attachment), int32(texTarget), webgl.Texture(t.V), int32(level))
335 }
336
337 func (f *Functions) GenerateMipmap(target Enum) {
338 webgl.GenerateMipmap(f.ctx, int32(target))
339 }
340
341 func (f *Functions) GetError() Enum {
342 return 0 // avoid slow getError; WebGL2 errors are reported as exceptions
343 }
344
345 func (f *Functions) GetInteger(pname Enum) int {
346 return int(webgl.GetInteger(f.ctx, int32(pname)))
347 }
348
349 func (f *Functions) GetProgrami(p GLProgram, pname Enum) int {
350 return int(webgl.GetProgramParameter(f.ctx, webgl.Program(p.V), int32(pname)))
351 }
352
353 func (f *Functions) GetProgramInfoLog(p GLProgram) string {
354 buf := []byte{:512} // heap-allocated, not stack
355 n := webgl.GetProgramInfoLog(f.ctx, webgl.Program(p.V), &buf[0], 512)
356 if n > 512 {
357 n = 512
358 }
359 return string(buf[:n])
360 }
361
362 func (f *Functions) GetShaderi(s GLShader, pname Enum) int {
363 return int(webgl.GetShaderParameter(f.ctx, webgl.Shader(s.V), int32(pname)))
364 }
365
366 func (f *Functions) GetShaderInfoLog(s GLShader) string {
367 buf := []byte{:512} // heap-allocated, not stack
368 n := webgl.GetShaderInfoLog(f.ctx, webgl.Shader(s.V), &buf[0], 512)
369 if n > 512 {
370 n = 512
371 }
372 return string(buf[:n])
373 }
374
375 func (f *Functions) GetUniformLocation(p GLProgram, name string) GLUniform {
376 loc := webgl.GetUniformLocation(f.ctx, webgl.Program(p.V), unsafe.StringData(name), int32(len(name)))
377 if loc == 0 {
378 return GLUniform{V: -1}
379 }
380 return GLUniform{V: int32(loc)}
381 }
382
383 func (f *Functions) InvalidateFramebuffer(target, attachment Enum) {
384 webgl.InvalidateFramebuffer(f.ctx, int32(target), int32(attachment))
385 }
386
387 func (f *Functions) IsEnabled(cap Enum) bool {
388 return webgl.IsEnabled(f.ctx, int32(cap)) != 0
389 }
390
391 func (f *Functions) LinkProgram(p GLProgram) {
392 webgl.LinkProgram(f.ctx, webgl.Program(p.V))
393 }
394
395 func (f *Functions) PixelStorei(pname Enum, param int) {
396 webgl.PixelStorei(f.ctx, int32(pname), int32(param))
397 }
398
399 func (f *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) {
400 webgl.RenderbufferStorage(f.ctx, int32(target), int32(internalformat), int32(width), int32(height))
401 }
402
403 func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) {
404 if len(data) > 0 {
405 webgl.ReadPixels(f.ctx, int32(x), int32(y), int32(width), int32(height), int32(format), int32(ty), &data[0], int32(len(data)))
406 }
407 }
408
409 func (f *Functions) Scissor(x, y, width, height int32) {
410 webgl.Scissor(f.ctx, x, y, width, height)
411 }
412
413 func (f *Functions) ShaderSource(s GLShader, src string) {
414 webgl.ShaderSource(f.ctx, webgl.Shader(s.V), unsafe.StringData(src), int32(len(src)))
415 }
416
417 func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width, height int, format, ty Enum) {
418 webgl.TexImage2D(f.ctx, int32(target), int32(level), int32(internalFormat), int32(width), int32(height), 0, int32(format), int32(ty), nil, 0)
419 }
420
421 func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) {
422 webgl.TexStorage2D(f.ctx, int32(target), int32(levels), int32(internalFormat), int32(width), int32(height))
423 }
424
425 func (f *Functions) TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte) {
426 if len(data) > 0 {
427 webgl.TexSubImage2D(f.ctx, int32(target), int32(level), int32(x), int32(y), int32(width), int32(height), int32(format), int32(ty), &data[0], int32(len(data)))
428 }
429 }
430
431 func (f *Functions) TexParameteri(target, pname Enum, param int) {
432 webgl.TexParameteri(f.ctx, int32(target), int32(pname), int32(param))
433 }
434
435 func (f *Functions) Uniform1f(dst GLUniform, v float32) {
436 webgl.Uniform1f(f.ctx, webgl.UniformLocation(dst.V), v)
437 }
438
439 func (f *Functions) Uniform1i(dst GLUniform, v int) {
440 webgl.Uniform1i(f.ctx, webgl.UniformLocation(dst.V), int32(v))
441 }
442
443 func (f *Functions) Uniform2f(dst GLUniform, v0, v1 float32) {
444 webgl.Uniform2f(f.ctx, webgl.UniformLocation(dst.V), v0, v1)
445 }
446
447 func (f *Functions) Uniform3f(dst GLUniform, v0, v1, v2 float32) {
448 webgl.Uniform3f(f.ctx, webgl.UniformLocation(dst.V), v0, v1, v2)
449 }
450
451 func (f *Functions) Uniform4f(dst GLUniform, v0, v1, v2, v3 float32) {
452 webgl.Uniform4f(f.ctx, webgl.UniformLocation(dst.V), v0, v1, v2, v3)
453 }
454
455 func (f *Functions) UseProgram(p GLProgram) {
456 webgl.UseProgram(f.ctx, webgl.Program(p.V))
457 }
458
459 func (f *Functions) VertexAttribPointer(dst GLAttrib, size int, ty Enum, normalized bool, stride, offset int) {
460 norm := int32(0)
461 if normalized {
462 norm = 1
463 }
464 webgl.VertexAttribPointer(f.ctx, int32(dst), int32(size), int32(ty), norm, int32(stride), int32(offset))
465 }
466
467 func (f *Functions) Viewport(x, y, width, height int) {
468 webgl.Viewport(f.ctx, int32(x), int32(y), int32(width), int32(height))
469 }
470
471 // GLCreateProgram links a vertex and fragment shader into a program.
472 func GLCreateProgram(ctx *Functions, vsSrc, fsSrc string, attribs []string) (GLProgram, error) {
473 vs, err := GLCreateShader(ctx, VERTEX_SHADER, vsSrc)
474 if err != nil {
475 return GLProgram{}, err
476 }
477 defer ctx.DeleteShader(vs)
478 fs, err := GLCreateShader(ctx, FRAGMENT_SHADER, fsSrc)
479 if err != nil {
480 return GLProgram{}, err
481 }
482 defer ctx.DeleteShader(fs)
483 prog := ctx.CreateProgram()
484 if !prog.Valid() {
485 return GLProgram{}, errors.New("glCreateProgram failed")
486 }
487 ctx.AttachShader(prog, vs)
488 ctx.AttachShader(prog, fs)
489 for i, a := range attribs {
490 ctx.BindAttribLocation(prog, GLAttrib(i), a)
491 }
492 ctx.LinkProgram(prog)
493 if ctx.GetProgrami(prog, LINK_STATUS) == 0 {
494 log := ctx.GetProgramInfoLog(prog)
495 ctx.DeleteProgram(prog)
496 return GLProgram{}, fmt.Errorf("program link failed: %s", bytes.TrimSpace(log))
497 }
498 return prog, nil
499 }
500
501 // GLCreateShader compiles a shader from source.
502 func GLCreateShader(ctx *Functions, typ Enum, src string) (GLShader, error) {
503 sh := ctx.CreateShader(typ)
504 if !sh.Valid() {
505 return GLShader{}, errors.New("glCreateShader failed")
506 }
507 ctx.ShaderSource(sh, src)
508 ctx.CompileShader(sh)
509 if ctx.GetShaderi(sh, COMPILE_STATUS) == 0 {
510 log := ctx.GetShaderInfoLog(sh)
511 ctx.DeleteShader(sh)
512 return GLShader{}, fmt.Errorf("shader compilation failed: %s", bytes.TrimSpace(log))
513 }
514 return sh, nil
515 }
516