opengl.go raw
1 // SPDX-License-Identifier: Unlicense OR MIT
2
3 package opengl
4
5 import (
6 "errors"
7 "fmt"
8 "image"
9 "strings"
10 "time"
11 "unsafe"
12
13 "github.com/p9c/p9/pkg/gel/gio/gpu/internal/driver"
14 "github.com/p9c/p9/pkg/gel/gio/internal/gl"
15 )
16
17 // Backend implements driver.Device.
18 type Backend struct {
19 funcs *gl.Functions
20
21 state glstate
22
23 glver [2]int
24 gles bool
25 ubo bool
26 feats driver.Caps
27 // floatTriple holds the settings for floating point
28 // textures.
29 floatTriple textureTriple
30 // Single channel alpha textures.
31 alphaTriple textureTriple
32 srgbaTriple textureTriple
33 }
34
35 // State tracking.
36 type glstate struct {
37 // nattr is the current number of enabled vertex arrays.
38 nattr int
39 prog *gpuProgram
40 texUnits [4]*gpuTexture
41 layout *gpuInputLayout
42 buffer bufferBinding
43 }
44
45 type bufferBinding struct {
46 buf *gpuBuffer
47 offset int
48 stride int
49 }
50
51 type gpuTimer struct {
52 funcs *gl.Functions
53 obj gl.Query
54 }
55
56 type gpuTexture struct {
57 backend *Backend
58 obj gl.Texture
59 triple textureTriple
60 width int
61 height int
62 }
63
64 type gpuFramebuffer struct {
65 backend *Backend
66 obj gl.Framebuffer
67 hasDepth bool
68 depthBuf gl.Renderbuffer
69 foreign bool
70 }
71
72 type gpuBuffer struct {
73 backend *Backend
74 hasBuffer bool
75 obj gl.Buffer
76 typ driver.BufferBinding
77 size int
78 immutable bool
79 version int
80 // For emulation of uniform buffers.
81 data []byte
82 }
83
84 type gpuProgram struct {
85 backend *Backend
86 obj gl.Program
87 nattr int
88 vertUniforms uniformsTracker
89 fragUniforms uniformsTracker
90 storage [storageBindings]*gpuBuffer
91 }
92
93 type uniformsTracker struct {
94 locs []uniformLocation
95 size int
96 buf *gpuBuffer
97 version int
98 }
99
100 type uniformLocation struct {
101 uniform gl.Uniform
102 offset int
103 typ driver.DataType
104 size int
105 }
106
107 type gpuInputLayout struct {
108 inputs []driver.InputLocation
109 layout []driver.InputDesc
110 }
111
112 // textureTriple holds the type settings for
113 // a TexImage2D call.
114 type textureTriple struct {
115 internalFormat gl.Enum
116 format gl.Enum
117 typ gl.Enum
118 }
119
120 type Context = gl.Context
121
122 const (
123 storageBindings = 32
124 )
125
126 func init() {
127 driver.NewOpenGLDevice = newOpenGLDevice
128 }
129
130 func newOpenGLDevice(api driver.OpenGL) (driver.Device, error) {
131 f, err := gl.NewFunctions(api.Context)
132 if err != nil {
133 return nil, err
134 }
135 exts := strings.Split(f.GetString(gl.EXTENSIONS), " ")
136 glVer := f.GetString(gl.VERSION)
137 ver, gles, err := gl.ParseGLVersion(glVer)
138 if err != nil {
139 return nil, err
140 }
141 floatTriple, ffboErr := floatTripleFor(f, ver, exts)
142 srgbaTriple, err := srgbaTripleFor(ver, exts)
143 if err != nil {
144 return nil, err
145 }
146 gles30 := gles && ver[0] >= 3
147 gles31 := gles && (ver[0] > 3 || (ver[0] == 3 && ver[1] >= 1))
148 gl40 := !gles && ver[0] >= 4
149 b := &Backend{
150 glver: ver,
151 gles: gles,
152 ubo: gles30 || gl40,
153 funcs: f,
154 floatTriple: floatTriple,
155 alphaTriple: alphaTripleFor(ver),
156 srgbaTriple: srgbaTriple,
157 }
158 b.feats.BottomLeftOrigin = true
159 if ffboErr == nil {
160 b.feats.Features |= driver.FeatureFloatRenderTargets
161 }
162 if gles31 {
163 b.feats.Features |= driver.FeatureCompute
164 }
165 if hasExtension(exts, "GL_EXT_disjoint_timer_query_webgl2") || hasExtension(exts, "GL_EXT_disjoint_timer_query") {
166 b.feats.Features |= driver.FeatureTimers
167 }
168 b.feats.MaxTextureSize = f.GetInteger(gl.MAX_TEXTURE_SIZE)
169 return b, nil
170 }
171
172 func (b *Backend) BeginFrame() driver.Framebuffer {
173 // Assume GL state is reset between frames.
174 b.state = glstate{}
175 fboID := gl.Framebuffer(b.funcs.GetBinding(gl.FRAMEBUFFER_BINDING))
176 return &gpuFramebuffer{backend: b, obj: fboID, foreign: true}
177 }
178
179 func (b *Backend) EndFrame() {
180 b.funcs.ActiveTexture(gl.TEXTURE0)
181 }
182
183 func (b *Backend) Caps() driver.Caps {
184 return b.feats
185 }
186
187 func (b *Backend) NewTimer() driver.Timer {
188 return &gpuTimer{
189 funcs: b.funcs,
190 obj: b.funcs.CreateQuery(),
191 }
192 }
193
194 func (b *Backend) IsTimeContinuous() bool {
195 return b.funcs.GetInteger(gl.GPU_DISJOINT_EXT) == gl.FALSE
196 }
197
198 func (b *Backend) NewFramebuffer(tex driver.Texture, depthBits int) (driver.Framebuffer, error) {
199 glErr(b.funcs)
200 gltex := tex.(*gpuTexture)
201 fb := b.funcs.CreateFramebuffer()
202 fbo := &gpuFramebuffer{backend: b, obj: fb}
203 b.BindFramebuffer(fbo)
204 if err := glErr(b.funcs); err != nil {
205 fbo.Release()
206 return nil, err
207 }
208 b.funcs.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, gltex.obj, 0)
209 if depthBits > 0 {
210 size := gl.Enum(gl.DEPTH_COMPONENT16)
211 switch {
212 case depthBits > 24:
213 size = gl.DEPTH_COMPONENT32F
214 case depthBits > 16:
215 size = gl.DEPTH_COMPONENT24
216 }
217 depthBuf := b.funcs.CreateRenderbuffer()
218 b.funcs.BindRenderbuffer(gl.RENDERBUFFER, depthBuf)
219 b.funcs.RenderbufferStorage(gl.RENDERBUFFER, size, gltex.width, gltex.height)
220 b.funcs.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuf)
221 fbo.depthBuf = depthBuf
222 fbo.hasDepth = true
223 if err := glErr(b.funcs); err != nil {
224 fbo.Release()
225 return nil, err
226 }
227 }
228 if st := b.funcs.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
229 fbo.Release()
230 return nil, fmt.Errorf("incomplete framebuffer, status = 0x%x, err = %d", st, b.funcs.GetError())
231 }
232 return fbo, nil
233 }
234
235 func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, binding driver.BufferBinding) (driver.Texture, error) {
236 glErr(b.funcs)
237 tex := &gpuTexture{backend: b, obj: b.funcs.CreateTexture(), width: width, height: height}
238 switch format {
239 case driver.TextureFormatFloat:
240 tex.triple = b.floatTriple
241 case driver.TextureFormatSRGB:
242 tex.triple = b.srgbaTriple
243 case driver.TextureFormatRGBA8:
244 tex.triple = textureTriple{gl.RGBA8, gl.RGBA, gl.UNSIGNED_BYTE}
245 default:
246 return nil, errors.New("unsupported texture format")
247 }
248 b.BindTexture(0, tex)
249 b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, toTexFilter(magFilter))
250 b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, toTexFilter(minFilter))
251 b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
252 b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
253 if b.gles && b.glver[0] >= 3 {
254 // Immutable textures are required for BindImageTexture, and can't hurt otherwise.
255 b.funcs.TexStorage2D(gl.TEXTURE_2D, 1, tex.triple.internalFormat, width, height)
256 } else {
257 b.funcs.TexImage2D(gl.TEXTURE_2D, 0, tex.triple.internalFormat, width, height, tex.triple.format, tex.triple.typ)
258 }
259 if err := glErr(b.funcs); err != nil {
260 tex.Release()
261 return nil, err
262 }
263 return tex, nil
264 }
265
266 func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) {
267 glErr(b.funcs)
268 buf := &gpuBuffer{backend: b, typ: typ, size: size}
269 if typ&driver.BufferBindingUniforms != 0 {
270 if typ != driver.BufferBindingUniforms {
271 return nil, errors.New("uniforms buffers cannot be bound as anything else")
272 }
273 if !b.ubo {
274 // GLES 2 doesn't support uniform buffers.
275 buf.data = make([]byte, size)
276 }
277 }
278 if typ&^driver.BufferBindingUniforms != 0 || b.ubo {
279 buf.hasBuffer = true
280 buf.obj = b.funcs.CreateBuffer()
281 if err := glErr(b.funcs); err != nil {
282 buf.Release()
283 return nil, err
284 }
285 firstBinding := firstBufferType(typ)
286 b.funcs.BindBuffer(firstBinding, buf.obj)
287 b.funcs.BufferData(firstBinding, size, gl.DYNAMIC_DRAW)
288 }
289 return buf, nil
290 }
291
292 func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) {
293 glErr(b.funcs)
294 obj := b.funcs.CreateBuffer()
295 buf := &gpuBuffer{backend: b, obj: obj, typ: typ, size: len(data), hasBuffer: true}
296 firstBinding := firstBufferType(typ)
297 b.funcs.BindBuffer(firstBinding, buf.obj)
298 b.funcs.BufferData(firstBinding, len(data), gl.STATIC_DRAW)
299 buf.Upload(data)
300 buf.immutable = true
301 if err := glErr(b.funcs); err != nil {
302 buf.Release()
303 return nil, err
304 }
305 return buf, nil
306 }
307
308 func glErr(f *gl.Functions) error {
309 if st := f.GetError(); st != gl.NO_ERROR {
310 return fmt.Errorf("glGetError: %#x", st)
311 }
312 return nil
313 }
314
315 func (b *Backend) Release() {
316 }
317
318 func (b *Backend) MemoryBarrier() {
319 b.funcs.MemoryBarrier(gl.ALL_BARRIER_BITS)
320 }
321
322 func (b *Backend) DispatchCompute(x, y, z int) {
323 if p := b.state.prog; p != nil {
324 for binding, buf := range p.storage {
325 if buf != nil {
326 b.funcs.BindBufferBase(gl.SHADER_STORAGE_BUFFER, binding, buf.obj)
327 }
328 }
329 }
330 b.funcs.DispatchCompute(x, y, z)
331 }
332
333 func (b *Backend) BindImageTexture(unit int, tex driver.Texture, access driver.AccessBits, f driver.TextureFormat) {
334 t := tex.(*gpuTexture)
335 var acc gl.Enum
336 switch access {
337 case driver.AccessWrite:
338 acc = gl.WRITE_ONLY
339 case driver.AccessRead:
340 acc = gl.READ_ONLY
341 default:
342 panic("unsupported access bits")
343 }
344 var format gl.Enum
345 switch f {
346 case driver.TextureFormatRGBA8:
347 format = gl.RGBA8
348 default:
349 panic("unsupported format")
350 }
351 b.funcs.BindImageTexture(unit, t.obj, 0, false, 0, acc, format)
352 }
353
354 func (b *Backend) bindTexture(unit int, t *gpuTexture) {
355 if b.state.texUnits[unit] != t {
356 b.funcs.ActiveTexture(gl.TEXTURE0 + gl.Enum(unit))
357 b.funcs.BindTexture(gl.TEXTURE_2D, t.obj)
358 b.state.texUnits[unit] = t
359 }
360 }
361
362 func (b *Backend) useProgram(p *gpuProgram) {
363 if b.state.prog != p {
364 p.backend.funcs.UseProgram(p.obj)
365 b.state.prog = p
366 }
367 }
368
369 func (b *Backend) enableVertexArrays(n int) {
370 // Enable needed arrays.
371 for i := b.state.nattr; i < n; i++ {
372 b.funcs.EnableVertexAttribArray(gl.Attrib(i))
373 }
374 // Disable extra arrays.
375 for i := n; i < b.state.nattr; i++ {
376 b.funcs.DisableVertexAttribArray(gl.Attrib(i))
377 }
378 b.state.nattr = n
379 }
380
381 func (b *Backend) SetDepthTest(enable bool) {
382 if enable {
383 b.funcs.Enable(gl.DEPTH_TEST)
384 } else {
385 b.funcs.Disable(gl.DEPTH_TEST)
386 }
387 }
388
389 func (b *Backend) BlendFunc(sfactor, dfactor driver.BlendFactor) {
390 b.funcs.BlendFunc(toGLBlendFactor(sfactor), toGLBlendFactor(dfactor))
391 }
392
393 func toGLBlendFactor(f driver.BlendFactor) gl.Enum {
394 switch f {
395 case driver.BlendFactorOne:
396 return gl.ONE
397 case driver.BlendFactorOneMinusSrcAlpha:
398 return gl.ONE_MINUS_SRC_ALPHA
399 case driver.BlendFactorZero:
400 return gl.ZERO
401 case driver.BlendFactorDstColor:
402 return gl.DST_COLOR
403 default:
404 panic("unsupported blend factor")
405 }
406 }
407
408 func (b *Backend) DepthMask(mask bool) {
409 b.funcs.DepthMask(mask)
410 }
411
412 func (b *Backend) SetBlend(enable bool) {
413 if enable {
414 b.funcs.Enable(gl.BLEND)
415 } else {
416 b.funcs.Disable(gl.BLEND)
417 }
418 }
419
420 func (b *Backend) DrawElements(mode driver.DrawMode, off, count int) {
421 b.prepareDraw()
422 // off is in 16-bit indices, but DrawElements take a byte offset.
423 byteOff := off * 2
424 b.funcs.DrawElements(toGLDrawMode(mode), count, gl.UNSIGNED_SHORT, byteOff)
425 }
426
427 func (b *Backend) DrawArrays(mode driver.DrawMode, off, count int) {
428 b.prepareDraw()
429 b.funcs.DrawArrays(toGLDrawMode(mode), off, count)
430 }
431
432 func (b *Backend) prepareDraw() {
433 nattr := b.state.prog.nattr
434 b.enableVertexArrays(nattr)
435 if nattr > 0 {
436 b.setupVertexArrays()
437 }
438 if p := b.state.prog; p != nil {
439 p.updateUniforms()
440 }
441 }
442
443 func toGLDrawMode(mode driver.DrawMode) gl.Enum {
444 switch mode {
445 case driver.DrawModeTriangleStrip:
446 return gl.TRIANGLE_STRIP
447 case driver.DrawModeTriangles:
448 return gl.TRIANGLES
449 default:
450 panic("unsupported draw mode")
451 }
452 }
453
454 func (b *Backend) Viewport(x, y, width, height int) {
455 b.funcs.Viewport(x, y, width, height)
456 }
457
458 func (b *Backend) Clear(colR, colG, colB, colA float32) {
459 b.funcs.ClearColor(colR, colG, colB, colA)
460 b.funcs.Clear(gl.COLOR_BUFFER_BIT)
461 }
462
463 func (b *Backend) ClearDepth(d float32) {
464 b.funcs.ClearDepthf(d)
465 b.funcs.Clear(gl.DEPTH_BUFFER_BIT)
466 }
467
468 func (b *Backend) DepthFunc(f driver.DepthFunc) {
469 var glfunc gl.Enum
470 switch f {
471 case driver.DepthFuncGreater:
472 glfunc = gl.GREATER
473 case driver.DepthFuncGreaterEqual:
474 glfunc = gl.GEQUAL
475 default:
476 panic("unsupported depth func")
477 }
478 b.funcs.DepthFunc(glfunc)
479 }
480
481 func (b *Backend) NewInputLayout(vs driver.ShaderSources, layout []driver.InputDesc) (driver.InputLayout, error) {
482 if len(vs.Inputs) != len(layout) {
483 return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vs.Inputs))
484 }
485 for i, inp := range vs.Inputs {
486 if exp, got := inp.Size, layout[i].Size; exp != got {
487 return nil, fmt.Errorf("NewInputLayout: data size mismatch for %q: got %d expected %d", inp.Name, got, exp)
488 }
489 }
490 return &gpuInputLayout{
491 inputs: vs.Inputs,
492 layout: layout,
493 }, nil
494 }
495
496 func (b *Backend) NewComputeProgram(src driver.ShaderSources) (driver.Program, error) {
497 p, err := gl.CreateComputeProgram(b.funcs, src.GLSL310ES)
498 if err != nil {
499 return nil, fmt.Errorf("%s: %v", src.Name, err)
500 }
501 gpuProg := &gpuProgram{
502 backend: b,
503 obj: p,
504 }
505 return gpuProg, nil
506 }
507
508 func (b *Backend) NewProgram(vertShader, fragShader driver.ShaderSources) (driver.Program, error) {
509 attr := make([]string, len(vertShader.Inputs))
510 for _, inp := range vertShader.Inputs {
511 attr[inp.Location] = inp.Name
512 }
513 vsrc, fsrc := vertShader.GLSL100ES, fragShader.GLSL100ES
514 if b.glver[0] >= 3 {
515 // OpenGL (ES) 3.0.
516 switch {
517 case b.gles:
518 vsrc, fsrc = vertShader.GLSL300ES, fragShader.GLSL300ES
519 case b.glver[0] >= 4 || b.glver[1] >= 2:
520 // OpenGL 3.2 Core only accepts glsl 1.50 or newer.
521 vsrc, fsrc = vertShader.GLSL150, fragShader.GLSL150
522 default:
523 vsrc, fsrc = vertShader.GLSL130, fragShader.GLSL130
524 }
525 }
526 p, err := gl.CreateProgram(b.funcs, vsrc, fsrc, attr)
527 if err != nil {
528 return nil, err
529 }
530 gpuProg := &gpuProgram{
531 backend: b,
532 obj: p,
533 nattr: len(attr),
534 }
535 b.BindProgram(gpuProg)
536 // Bind texture uniforms.
537 for _, tex := range vertShader.Textures {
538 u := b.funcs.GetUniformLocation(p, tex.Name)
539 if u.Valid() {
540 b.funcs.Uniform1i(u, tex.Binding)
541 }
542 }
543 for _, tex := range fragShader.Textures {
544 u := b.funcs.GetUniformLocation(p, tex.Name)
545 if u.Valid() {
546 b.funcs.Uniform1i(u, tex.Binding)
547 }
548 }
549 if b.ubo {
550 for _, block := range vertShader.Uniforms.Blocks {
551 blockIdx := b.funcs.GetUniformBlockIndex(p, block.Name)
552 if blockIdx != gl.INVALID_INDEX {
553 b.funcs.UniformBlockBinding(p, blockIdx, uint(block.Binding))
554 }
555 }
556 // To match Direct3D 11 with separate vertex and fragment
557 // shader uniform buffers, offset all fragment blocks to be
558 // located after the vertex blocks.
559 off := len(vertShader.Uniforms.Blocks)
560 for _, block := range fragShader.Uniforms.Blocks {
561 blockIdx := b.funcs.GetUniformBlockIndex(p, block.Name)
562 if blockIdx != gl.INVALID_INDEX {
563 b.funcs.UniformBlockBinding(p, blockIdx, uint(block.Binding+off))
564 }
565 }
566 } else {
567 gpuProg.vertUniforms.setup(b.funcs, p, vertShader.Uniforms.Size, vertShader.Uniforms.Locations)
568 gpuProg.fragUniforms.setup(b.funcs, p, fragShader.Uniforms.Size, fragShader.Uniforms.Locations)
569 }
570 return gpuProg, nil
571 }
572
573 func lookupUniform(funcs *gl.Functions, p gl.Program, loc driver.UniformLocation) uniformLocation {
574 u := funcs.GetUniformLocation(p, loc.Name)
575 if !u.Valid() {
576 panic(fmt.Errorf("uniform %q not found", loc.Name))
577 }
578 return uniformLocation{uniform: u, offset: loc.Offset, typ: loc.Type, size: loc.Size}
579 }
580
581 func (p *gpuProgram) SetStorageBuffer(binding int, buffer driver.Buffer) {
582 buf := buffer.(*gpuBuffer)
583 if buf.typ&driver.BufferBindingShaderStorage == 0 {
584 panic("not a shader storage buffer")
585 }
586 p.storage[binding] = buf
587 }
588
589 func (p *gpuProgram) SetVertexUniforms(buffer driver.Buffer) {
590 p.vertUniforms.setBuffer(buffer)
591 }
592
593 func (p *gpuProgram) SetFragmentUniforms(buffer driver.Buffer) {
594 p.fragUniforms.setBuffer(buffer)
595 }
596
597 func (p *gpuProgram) updateUniforms() {
598 f := p.backend.funcs
599 if p.backend.ubo {
600 if b := p.vertUniforms.buf; b != nil {
601 f.BindBufferBase(gl.UNIFORM_BUFFER, 0, b.obj)
602 }
603 if b := p.fragUniforms.buf; b != nil {
604 f.BindBufferBase(gl.UNIFORM_BUFFER, 1, b.obj)
605 }
606 } else {
607 p.vertUniforms.update(f)
608 p.fragUniforms.update(f)
609 }
610 }
611
612 func (b *Backend) BindProgram(prog driver.Program) {
613 p := prog.(*gpuProgram)
614 b.useProgram(p)
615 }
616
617 func (p *gpuProgram) Release() {
618 p.backend.funcs.DeleteProgram(p.obj)
619 }
620
621 func (u *uniformsTracker) setup(funcs *gl.Functions, p gl.Program, uniformSize int, uniforms []driver.UniformLocation) {
622 u.locs = make([]uniformLocation, len(uniforms))
623 for i, uniform := range uniforms {
624 u.locs[i] = lookupUniform(funcs, p, uniform)
625 }
626 u.size = uniformSize
627 }
628
629 func (u *uniformsTracker) setBuffer(buffer driver.Buffer) {
630 buf := buffer.(*gpuBuffer)
631 if buf.typ&driver.BufferBindingUniforms == 0 {
632 panic("not a uniform buffer")
633 }
634 if buf.size < u.size {
635 panic(fmt.Errorf("uniform buffer too small, got %d need %d", buf.size, u.size))
636 }
637 u.buf = buf
638 // Force update.
639 u.version = buf.version - 1
640 }
641
642 func (p *uniformsTracker) update(funcs *gl.Functions) {
643 b := p.buf
644 if b == nil || b.version == p.version {
645 return
646 }
647 p.version = b.version
648 data := b.data
649 for _, u := range p.locs {
650 data := data[u.offset:]
651 switch {
652 case u.typ == driver.DataTypeFloat && u.size == 1:
653 data := data[:4]
654 v := *(*[1]float32)(unsafe.Pointer(&data[0]))
655 funcs.Uniform1f(u.uniform, v[0])
656 case u.typ == driver.DataTypeFloat && u.size == 2:
657 data := data[:8]
658 v := *(*[2]float32)(unsafe.Pointer(&data[0]))
659 funcs.Uniform2f(u.uniform, v[0], v[1])
660 case u.typ == driver.DataTypeFloat && u.size == 3:
661 data := data[:12]
662 v := *(*[3]float32)(unsafe.Pointer(&data[0]))
663 funcs.Uniform3f(u.uniform, v[0], v[1], v[2])
664 case u.typ == driver.DataTypeFloat && u.size == 4:
665 data := data[:16]
666 v := *(*[4]float32)(unsafe.Pointer(&data[0]))
667 funcs.Uniform4f(u.uniform, v[0], v[1], v[2], v[3])
668 default:
669 panic("unsupported uniform data type or size")
670 }
671 }
672 }
673
674 func (b *gpuBuffer) Upload(data []byte) {
675 if b.immutable {
676 panic("immutable buffer")
677 }
678 if len(data) > b.size {
679 panic("buffer size overflow")
680 }
681 b.version++
682 copy(b.data, data)
683 if b.hasBuffer {
684 firstBinding := firstBufferType(b.typ)
685 b.backend.funcs.BindBuffer(firstBinding, b.obj)
686 if len(data) == b.size {
687 // the iOS GL implementation doesn't recognize when BufferSubData
688 // clears the entire buffer. Tell it and avoid GPU stalls.
689 // See also https://github.com/godotengine/godot/issues/23956.
690 b.backend.funcs.BufferData(firstBinding, b.size, gl.DYNAMIC_DRAW)
691 }
692 b.backend.funcs.BufferSubData(firstBinding, 0, data)
693 }
694 }
695
696 func (b *gpuBuffer) Download(data []byte) error {
697 if len(data) > b.size {
698 panic("buffer size overflow")
699 }
700 if !b.hasBuffer {
701 copy(data, b.data)
702 return nil
703 }
704 firstBinding := firstBufferType(b.typ)
705 b.backend.funcs.BindBuffer(firstBinding, b.obj)
706 bufferMap := b.backend.funcs.MapBufferRange(firstBinding, 0, len(data), gl.MAP_READ_BIT)
707 if bufferMap == nil {
708 return fmt.Errorf("MapBufferRange: error %#x", b.backend.funcs.GetError())
709 }
710 copy(data, bufferMap)
711 if !b.backend.funcs.UnmapBuffer(firstBinding) {
712 return driver.ErrContentLost
713 }
714 return nil
715 }
716
717 func (b *gpuBuffer) Release() {
718 if b.hasBuffer {
719 b.backend.funcs.DeleteBuffer(b.obj)
720 b.hasBuffer = false
721 }
722 }
723
724 func (b *Backend) BindVertexBuffer(buf driver.Buffer, stride, offset int) {
725 gbuf := buf.(*gpuBuffer)
726 if gbuf.typ&driver.BufferBindingVertices == 0 {
727 panic("not a vertex buffer")
728 }
729 b.state.buffer = bufferBinding{buf: gbuf, stride: stride, offset: offset}
730 }
731
732 func (b *Backend) setupVertexArrays() {
733 layout := b.state.layout
734 if layout == nil {
735 return
736 }
737 buf := b.state.buffer
738 b.funcs.BindBuffer(gl.ARRAY_BUFFER, buf.buf.obj)
739 for i, inp := range layout.inputs {
740 l := layout.layout[i]
741 var gltyp gl.Enum
742 switch l.Type {
743 case driver.DataTypeFloat:
744 gltyp = gl.FLOAT
745 case driver.DataTypeShort:
746 gltyp = gl.SHORT
747 default:
748 panic("unsupported data type")
749 }
750 b.funcs.VertexAttribPointer(gl.Attrib(inp.Location), l.Size, gltyp, false, buf.stride, buf.offset+l.Offset)
751 }
752 }
753
754 func (b *Backend) BindIndexBuffer(buf driver.Buffer) {
755 gbuf := buf.(*gpuBuffer)
756 if gbuf.typ&driver.BufferBindingIndices == 0 {
757 panic("not an index buffer")
758 }
759 b.funcs.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, gbuf.obj)
760 }
761
762 func (b *Backend) BlitFramebuffer(dst, src driver.Framebuffer, srect, drect image.Rectangle) {
763 b.funcs.BindFramebuffer(gl.DRAW_FRAMEBUFFER, dst.(*gpuFramebuffer).obj)
764 b.funcs.BindFramebuffer(gl.READ_FRAMEBUFFER, src.(*gpuFramebuffer).obj)
765 b.funcs.BlitFramebuffer(
766 srect.Min.X, srect.Min.Y, srect.Max.X, srect.Max.Y,
767 drect.Min.X, drect.Min.Y, drect.Max.X, drect.Max.Y,
768 gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT|gl.STENCIL_BUFFER_BIT,
769 gl.NEAREST)
770 }
771
772 func (f *gpuFramebuffer) ReadPixels(src image.Rectangle, pixels []byte) error {
773 glErr(f.backend.funcs)
774 f.backend.BindFramebuffer(f)
775 if len(pixels) < src.Dx()*src.Dy()*4 {
776 return errors.New("unexpected RGBA size")
777 }
778 f.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, src.Dx(), src.Dy(), gl.RGBA, gl.UNSIGNED_BYTE, pixels)
779 return glErr(f.backend.funcs)
780 }
781
782 func (b *Backend) BindFramebuffer(fbo driver.Framebuffer) {
783 b.funcs.BindFramebuffer(gl.FRAMEBUFFER, fbo.(*gpuFramebuffer).obj)
784 }
785
786 func (f *gpuFramebuffer) Invalidate() {
787 f.backend.BindFramebuffer(f)
788 f.backend.funcs.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0)
789 }
790
791 func (f *gpuFramebuffer) Release() {
792 if f.foreign {
793 panic("framebuffer not created by NewFramebuffer")
794 }
795 f.backend.funcs.DeleteFramebuffer(f.obj)
796 if f.hasDepth {
797 f.backend.funcs.DeleteRenderbuffer(f.depthBuf)
798 }
799 }
800
801 func toTexFilter(f driver.TextureFilter) int {
802 switch f {
803 case driver.FilterNearest:
804 return gl.NEAREST
805 case driver.FilterLinear:
806 return gl.LINEAR
807 default:
808 panic("unsupported texture filter")
809 }
810 }
811
812 func (b *Backend) BindTexture(unit int, t driver.Texture) {
813 b.bindTexture(unit, t.(*gpuTexture))
814 }
815
816 func (t *gpuTexture) Release() {
817 t.backend.funcs.DeleteTexture(t.obj)
818 }
819
820 func (t *gpuTexture) Upload(offset, size image.Point, pixels []byte) {
821 if min := size.X * size.Y * 4; min > len(pixels) {
822 panic(fmt.Errorf("size %d larger than data %d", min, len(pixels)))
823 }
824 t.backend.BindTexture(0, t)
825 t.backend.funcs.TexSubImage2D(gl.TEXTURE_2D, 0, offset.X, offset.Y, size.X, size.Y, t.triple.format, t.triple.typ, pixels)
826 }
827
828 func (t *gpuTimer) Begin() {
829 t.funcs.BeginQuery(gl.TIME_ELAPSED_EXT, t.obj)
830 }
831
832 func (t *gpuTimer) End() {
833 t.funcs.EndQuery(gl.TIME_ELAPSED_EXT)
834 }
835
836 func (t *gpuTimer) ready() bool {
837 return t.funcs.GetQueryObjectuiv(t.obj, gl.QUERY_RESULT_AVAILABLE) == gl.TRUE
838 }
839
840 func (t *gpuTimer) Release() {
841 t.funcs.DeleteQuery(t.obj)
842 }
843
844 func (t *gpuTimer) Duration() (time.Duration, bool) {
845 if !t.ready() {
846 return 0, false
847 }
848 nanos := t.funcs.GetQueryObjectuiv(t.obj, gl.QUERY_RESULT)
849 return time.Duration(nanos), true
850 }
851
852 func (b *Backend) BindInputLayout(l driver.InputLayout) {
853 b.state.layout = l.(*gpuInputLayout)
854 }
855
856 func (l *gpuInputLayout) Release() {}
857
858 // floatTripleFor determines the best texture triple for floating point FBOs.
859 func floatTripleFor(f *gl.Functions, ver [2]int, exts []string) (textureTriple, error) {
860 var triples []textureTriple
861 if ver[0] >= 3 {
862 triples = append(triples, textureTriple{gl.R16F, gl.Enum(gl.RED), gl.Enum(gl.HALF_FLOAT)})
863 }
864 // According to the OES_texture_half_float specification, EXT_color_buffer_half_float is needed to
865 // render to FBOs. However, the Safari WebGL1 implementation does support half-float FBOs but does not
866 // report EXT_color_buffer_half_float support. The triples are verified below, so it doesn't matter if we're
867 // wrong.
868 if hasExtension(exts, "GL_OES_texture_half_float") || hasExtension(exts, "GL_EXT_color_buffer_half_float") {
869 // Try single channel.
870 triples = append(triples, textureTriple{gl.LUMINANCE, gl.Enum(gl.LUMINANCE), gl.Enum(gl.HALF_FLOAT_OES)})
871 // Fallback to 4 channels.
872 triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.HALF_FLOAT_OES)})
873 }
874 if hasExtension(exts, "GL_OES_texture_float") || hasExtension(exts, "GL_EXT_color_buffer_float") {
875 triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.FLOAT)})
876 }
877 tex := f.CreateTexture()
878 defer f.DeleteTexture(tex)
879 f.BindTexture(gl.TEXTURE_2D, tex)
880 f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
881 f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
882 f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
883 f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
884 fbo := f.CreateFramebuffer()
885 defer f.DeleteFramebuffer(fbo)
886 defFBO := gl.Framebuffer(f.GetBinding(gl.FRAMEBUFFER_BINDING))
887 f.BindFramebuffer(gl.FRAMEBUFFER, fbo)
888 defer f.BindFramebuffer(gl.FRAMEBUFFER, defFBO)
889 var attempts []string
890 for _, tt := range triples {
891 const size = 256
892 f.TexImage2D(gl.TEXTURE_2D, 0, tt.internalFormat, size, size, tt.format, tt.typ)
893 f.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0)
894 st := f.CheckFramebufferStatus(gl.FRAMEBUFFER)
895 if st == gl.FRAMEBUFFER_COMPLETE {
896 return tt, nil
897 }
898 attempts = append(attempts, fmt.Sprintf("(0x%x, 0x%x, 0x%x): 0x%x", tt.internalFormat, tt.format, tt.typ, st))
899 }
900 return textureTriple{}, fmt.Errorf("floating point fbos not supported (attempted %s)", attempts)
901 }
902
903 func srgbaTripleFor(ver [2]int, exts []string) (textureTriple, error) {
904 switch {
905 case ver[0] >= 3:
906 return textureTriple{gl.SRGB8_ALPHA8, gl.Enum(gl.RGBA), gl.Enum(gl.UNSIGNED_BYTE)}, nil
907 case hasExtension(exts, "GL_EXT_sRGB"):
908 return textureTriple{gl.SRGB_ALPHA_EXT, gl.Enum(gl.SRGB_ALPHA_EXT), gl.Enum(gl.UNSIGNED_BYTE)}, nil
909 default:
910 return textureTriple{}, errors.New("no sRGB texture formats found")
911 }
912 }
913
914 func alphaTripleFor(ver [2]int) textureTriple {
915 intf, f := gl.Enum(gl.R8), gl.Enum(gl.RED)
916 if ver[0] < 3 {
917 // R8, RED not supported on OpenGL ES 2.0.
918 intf, f = gl.LUMINANCE, gl.Enum(gl.LUMINANCE)
919 }
920 return textureTriple{intf, f, gl.UNSIGNED_BYTE}
921 }
922
923 func hasExtension(exts []string, ext string) bool {
924 for _, e := range exts {
925 if ext == e {
926 return true
927 }
928 }
929 return false
930 }
931
932 func firstBufferType(typ driver.BufferBinding) gl.Enum {
933 switch {
934 case typ&driver.BufferBindingIndices != 0:
935 return gl.ELEMENT_ARRAY_BUFFER
936 case typ&driver.BufferBindingVertices != 0:
937 return gl.ARRAY_BUFFER
938 case typ&driver.BufferBindingUniforms != 0:
939 return gl.UNIFORM_BUFFER
940 case typ&driver.BufferBindingShaderStorage != 0:
941 return gl.SHADER_STORAGE_BUFFER
942 default:
943 panic("unsupported buffer type")
944 }
945 }
946