gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/gpu/internal/opengl/opengl.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package opengl 4 5 import ( 6 "errors" 7 "fmt" 8 "image" 9 "math/bits" 10 "runtime" 11 "strings" 12 "time" 13 "unsafe" 14 15 "gioui.org/gpu/internal/driver" 16 "gioui.org/internal/gl" 17 "gioui.org/shader" 18 ) 19 20 // Backend implements driver.Device. 21 type Backend struct { 22 funcs *gl.Functions 23 24 clear bool 25 glstate glState 26 state state 27 savedState glState 28 sharedCtx bool 29 30 glver [2]int 31 gles bool 32 feats driver.Caps 33 // floatTriple holds the settings for floating point 34 // textures. 35 floatTriple textureTriple 36 // Single channel alpha textures. 37 alphaTriple textureTriple 38 srgbaTriple textureTriple 39 storage [storageBindings]*buffer 40 41 outputFBO gl.Framebuffer 42 sRGBFBO *SRGBFBO 43 44 // vertArray is bound during a frame. We don't need it, but 45 // core desktop OpenGL profile 3.3 requires some array bound. 46 vertArray gl.VertexArray 47 } 48 49 // State tracking. 50 type glState struct { 51 drawFBO gl.Framebuffer 52 readFBO gl.Framebuffer 53 vertAttribs [5]struct { 54 obj gl.Buffer 55 enabled bool 56 size int 57 typ gl.Enum 58 normalized bool 59 stride int 60 offset uintptr 61 } 62 prog gl.Program 63 texUnits struct { 64 active gl.Enum 65 binds [2]gl.Texture 66 } 67 arrayBuf gl.Buffer 68 elemBuf gl.Buffer 69 uniBuf gl.Buffer 70 uniBufs [2]gl.Buffer 71 storeBuf gl.Buffer 72 storeBufs [4]gl.Buffer 73 vertArray gl.VertexArray 74 srgb bool 75 blend struct { 76 enable bool 77 srcRGB, dstRGB gl.Enum 78 srcA, dstA gl.Enum 79 } 80 clearColor [4]float32 81 viewport [4]int 82 unpack_row_length int 83 pack_row_length int 84 } 85 86 type state struct { 87 pipeline *pipeline 88 buffer bufferBinding 89 } 90 91 type bufferBinding struct { 92 obj gl.Buffer 93 offset int 94 } 95 96 type timer struct { 97 funcs *gl.Functions 98 obj gl.Query 99 } 100 101 type texture struct { 102 backend *Backend 103 obj gl.Texture 104 fbo gl.Framebuffer 105 hasFBO bool 106 triple textureTriple 107 width int 108 height int 109 mipmap bool 110 bindings driver.BufferBinding 111 foreign bool 112 } 113 114 type pipeline struct { 115 prog *program 116 inputs []shader.InputLocation 117 layout driver.VertexLayout 118 blend driver.BlendDesc 119 topology driver.Topology 120 } 121 122 type buffer struct { 123 backend *Backend 124 hasBuffer bool 125 obj gl.Buffer 126 typ driver.BufferBinding 127 size int 128 immutable bool 129 // For emulation of uniform buffers. 130 data []byte 131 } 132 133 type glshader struct { 134 backend *Backend 135 obj gl.Shader 136 src shader.Sources 137 } 138 139 type program struct { 140 backend *Backend 141 obj gl.Program 142 vertUniforms uniforms 143 fragUniforms uniforms 144 } 145 146 type uniforms struct { 147 locs []uniformLocation 148 size int 149 } 150 151 type uniformLocation struct { 152 uniform gl.Uniform 153 offset int 154 typ shader.DataType 155 size int 156 } 157 158 // textureTriple holds the type settings for 159 // a TexImage2D call. 160 type textureTriple struct { 161 internalFormat gl.Enum 162 format gl.Enum 163 typ gl.Enum 164 } 165 166 const ( 167 storageBindings = 32 168 ) 169 170 func init() { 171 driver.NewOpenGLDevice = newOpenGLDevice 172 } 173 174 // Supporting compute programs is theoretically possible with OpenGL ES 3.1. In 175 // practice, there are too many driver issues, especially on Android (e.g. 176 // Google Pixel, Samsung J2 are both broken i different ways). Disable support 177 // and rely on Vulkan for devices that support it, and the CPU fallback for 178 // devices that don't. 179 const brokenGLES31 = true 180 181 func newOpenGLDevice(api driver.OpenGL) (driver.Device, error) { 182 f, err := gl.NewFunctions(api.Context, api.ES) 183 if err != nil { 184 return nil, err 185 } 186 exts := strings.Split(f.GetString(gl.EXTENSIONS), " ") 187 glVer := f.GetString(gl.VERSION) 188 ver, gles, err := gl.ParseGLVersion(glVer) 189 if err != nil { 190 return nil, err 191 } 192 floatTriple, ffboErr := floatTripleFor(f, ver, exts) 193 srgbaTriple, srgbErr := srgbaTripleFor(ver, exts) 194 gles31 := gles && (ver[0] > 3 || (ver[0] == 3 && ver[1] >= 1)) 195 b := &Backend{ 196 glver: ver, 197 gles: gles, 198 funcs: f, 199 floatTriple: floatTriple, 200 alphaTriple: alphaTripleFor(ver), 201 srgbaTriple: srgbaTriple, 202 sharedCtx: api.Shared, 203 } 204 b.feats.BottomLeftOrigin = true 205 if srgbErr == nil { 206 b.feats.Features |= driver.FeatureSRGB 207 } 208 if ffboErr == nil { 209 b.feats.Features |= driver.FeatureFloatRenderTargets 210 } 211 if gles31 && !brokenGLES31 { 212 b.feats.Features |= driver.FeatureCompute 213 } 214 if hasExtension(exts, "GL_EXT_disjoint_timer_query_webgl2") || hasExtension(exts, "GL_EXT_disjoint_timer_query") { 215 b.feats.Features |= driver.FeatureTimers 216 } 217 b.feats.MaxTextureSize = f.GetInteger(gl.MAX_TEXTURE_SIZE) 218 if !b.sharedCtx { 219 // We have exclusive access to the context, so query the GL state once 220 // instead of at each frame. 221 b.glstate = b.queryState() 222 } 223 return b, nil 224 } 225 226 func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture { 227 b.clear = clear 228 if b.sharedCtx { 229 b.glstate = b.queryState() 230 b.savedState = b.glstate 231 } 232 b.state = state{} 233 var renderFBO gl.Framebuffer 234 if target != nil { 235 switch t := target.(type) { 236 case driver.OpenGLRenderTarget: 237 renderFBO = gl.Framebuffer(t) 238 case *texture: 239 renderFBO = t.ensureFBO() 240 default: 241 panic(fmt.Errorf("opengl: invalid render target type: %T", target)) 242 } 243 } 244 b.outputFBO = renderFBO 245 b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, renderFBO) 246 if b.gles { 247 // If the output framebuffer is not in the sRGB colorspace already, emulate it. 248 fbSRGB := false 249 if !b.gles || b.glver[0] > 2 { 250 var fbEncoding int 251 if !renderFBO.Valid() { 252 fbEncoding = b.funcs.GetFramebufferAttachmentParameteri(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) 253 } else { 254 fbEncoding = b.funcs.GetFramebufferAttachmentParameteri(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) 255 } 256 fbSRGB = fbEncoding != gl.LINEAR 257 } 258 if !fbSRGB && viewport != (image.Point{}) { 259 if b.sRGBFBO == nil { 260 sfbo, err := NewSRGBFBO(b.funcs, &b.glstate) 261 if err != nil { 262 panic(err) 263 } 264 b.sRGBFBO = sfbo 265 } 266 if err := b.sRGBFBO.Refresh(viewport); err != nil { 267 panic(err) 268 } 269 renderFBO = b.sRGBFBO.Framebuffer() 270 } else if b.sRGBFBO != nil { 271 b.sRGBFBO.Release() 272 b.sRGBFBO = nil 273 } 274 } else { 275 b.glstate.set(b.funcs, gl.FRAMEBUFFER_SRGB, true) 276 if !b.vertArray.Valid() { 277 b.vertArray = b.funcs.CreateVertexArray() 278 } 279 b.glstate.bindVertexArray(b.funcs, b.vertArray) 280 } 281 b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, renderFBO) 282 if b.sRGBFBO != nil && !clear { 283 b.clearOutput(0, 0, 0, 0) 284 } 285 return &texture{backend: b, fbo: renderFBO, hasFBO: true, foreign: true} 286 } 287 288 func (b *Backend) EndFrame() { 289 if b.sRGBFBO != nil { 290 b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, b.outputFBO) 291 if b.clear { 292 b.SetBlend(false) 293 } else { 294 b.BlendFunc(driver.BlendFactorOne, driver.BlendFactorOneMinusSrcAlpha) 295 b.SetBlend(true) 296 } 297 b.sRGBFBO.Blit() 298 } 299 if b.sharedCtx { 300 b.restoreState(b.savedState) 301 } else if runtime.GOOS == "android" { 302 // The Android emulator needs the output framebuffer to be current when 303 // eglSwapBuffers is called. 304 b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, b.outputFBO) 305 } 306 } 307 308 func (b *Backend) queryState() glState { 309 s := glState{ 310 prog: gl.Program(b.funcs.GetBinding(gl.CURRENT_PROGRAM)), 311 arrayBuf: gl.Buffer(b.funcs.GetBinding(gl.ARRAY_BUFFER_BINDING)), 312 elemBuf: gl.Buffer(b.funcs.GetBinding(gl.ELEMENT_ARRAY_BUFFER_BINDING)), 313 drawFBO: gl.Framebuffer(b.funcs.GetBinding(gl.FRAMEBUFFER_BINDING)), 314 clearColor: b.funcs.GetFloat4(gl.COLOR_CLEAR_VALUE), 315 viewport: b.funcs.GetInteger4(gl.VIEWPORT), 316 } 317 if !b.gles || b.glver[0] > 2 { 318 s.unpack_row_length = b.funcs.GetInteger(gl.UNPACK_ROW_LENGTH) 319 s.pack_row_length = b.funcs.GetInteger(gl.PACK_ROW_LENGTH) 320 } 321 s.blend.enable = b.funcs.IsEnabled(gl.BLEND) 322 s.blend.srcRGB = gl.Enum(b.funcs.GetInteger(gl.BLEND_SRC_RGB)) 323 s.blend.dstRGB = gl.Enum(b.funcs.GetInteger(gl.BLEND_DST_RGB)) 324 s.blend.srcA = gl.Enum(b.funcs.GetInteger(gl.BLEND_SRC_ALPHA)) 325 s.blend.dstA = gl.Enum(b.funcs.GetInteger(gl.BLEND_DST_ALPHA)) 326 s.texUnits.active = gl.Enum(b.funcs.GetInteger(gl.ACTIVE_TEXTURE)) 327 if !b.gles { 328 s.srgb = b.funcs.IsEnabled(gl.FRAMEBUFFER_SRGB) 329 } 330 if !b.gles || b.glver[0] >= 3 { 331 s.vertArray = gl.VertexArray(b.funcs.GetBinding(gl.VERTEX_ARRAY_BINDING)) 332 s.readFBO = gl.Framebuffer(b.funcs.GetBinding(gl.READ_FRAMEBUFFER_BINDING)) 333 s.uniBuf = gl.Buffer(b.funcs.GetBinding(gl.UNIFORM_BUFFER_BINDING)) 334 for i := range s.uniBufs { 335 s.uniBufs[i] = gl.Buffer(b.funcs.GetBindingi(gl.UNIFORM_BUFFER_BINDING, i)) 336 } 337 } 338 if b.gles && (b.glver[0] > 3 || (b.glver[0] == 3 && b.glver[1] >= 1)) { 339 s.storeBuf = gl.Buffer(b.funcs.GetBinding(gl.SHADER_STORAGE_BUFFER_BINDING)) 340 for i := range s.storeBufs { 341 s.storeBufs[i] = gl.Buffer(b.funcs.GetBindingi(gl.SHADER_STORAGE_BUFFER_BINDING, i)) 342 } 343 } 344 active := s.texUnits.active 345 for i := range s.texUnits.binds { 346 s.activeTexture(b.funcs, gl.TEXTURE0+gl.Enum(i)) 347 s.texUnits.binds[i] = gl.Texture(b.funcs.GetBinding(gl.TEXTURE_BINDING_2D)) 348 } 349 s.activeTexture(b.funcs, active) 350 for i := range s.vertAttribs { 351 a := &s.vertAttribs[i] 352 a.enabled = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_ENABLED) != gl.FALSE 353 a.obj = gl.Buffer(b.funcs.GetVertexAttribBinding(i, gl.VERTEX_ATTRIB_ARRAY_ENABLED)) 354 a.size = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_SIZE) 355 a.typ = gl.Enum(b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_TYPE)) 356 a.normalized = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_NORMALIZED) != gl.FALSE 357 a.stride = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_STRIDE) 358 a.offset = b.funcs.GetVertexAttribPointer(i, gl.VERTEX_ATTRIB_ARRAY_POINTER) 359 } 360 return s 361 } 362 363 func (b *Backend) restoreState(dst glState) { 364 src := &b.glstate 365 f := b.funcs 366 for i, unit := range dst.texUnits.binds { 367 src.bindTexture(f, i, unit) 368 } 369 src.activeTexture(f, dst.texUnits.active) 370 src.bindFramebuffer(f, gl.FRAMEBUFFER, dst.drawFBO) 371 src.bindFramebuffer(f, gl.READ_FRAMEBUFFER, dst.readFBO) 372 src.set(f, gl.BLEND, dst.blend.enable) 373 bf := dst.blend 374 src.setBlendFuncSeparate(f, bf.srcRGB, bf.dstRGB, bf.srcA, bf.dstA) 375 src.set(f, gl.FRAMEBUFFER_SRGB, dst.srgb) 376 src.bindVertexArray(f, dst.vertArray) 377 src.useProgram(f, dst.prog) 378 src.bindBuffer(f, gl.ELEMENT_ARRAY_BUFFER, dst.elemBuf) 379 for i, b := range dst.uniBufs { 380 src.bindBufferBase(f, gl.UNIFORM_BUFFER, i, b) 381 } 382 src.bindBuffer(f, gl.UNIFORM_BUFFER, dst.uniBuf) 383 for i, b := range dst.storeBufs { 384 src.bindBufferBase(f, gl.SHADER_STORAGE_BUFFER, i, b) 385 } 386 src.bindBuffer(f, gl.SHADER_STORAGE_BUFFER, dst.storeBuf) 387 col := dst.clearColor 388 src.setClearColor(f, col[0], col[1], col[2], col[3]) 389 for i, attr := range dst.vertAttribs { 390 src.setVertexAttribArray(f, i, attr.enabled) 391 src.vertexAttribPointer(f, attr.obj, i, attr.size, attr.typ, attr.normalized, attr.stride, int(attr.offset)) 392 } 393 src.bindBuffer(f, gl.ARRAY_BUFFER, dst.arrayBuf) 394 v := dst.viewport 395 src.setViewport(f, v[0], v[1], v[2], v[3]) 396 src.pixelStorei(f, gl.UNPACK_ROW_LENGTH, dst.unpack_row_length) 397 src.pixelStorei(f, gl.PACK_ROW_LENGTH, dst.pack_row_length) 398 } 399 400 func (s *glState) setVertexAttribArray(f *gl.Functions, idx int, enabled bool) { 401 a := &s.vertAttribs[idx] 402 if enabled != a.enabled { 403 if enabled { 404 f.EnableVertexAttribArray(gl.Attrib(idx)) 405 } else { 406 f.DisableVertexAttribArray(gl.Attrib(idx)) 407 } 408 a.enabled = enabled 409 } 410 } 411 412 func (s *glState) vertexAttribPointer(f *gl.Functions, buf gl.Buffer, idx, size int, typ gl.Enum, normalized bool, stride, offset int) { 413 s.bindBuffer(f, gl.ARRAY_BUFFER, buf) 414 a := &s.vertAttribs[idx] 415 a.obj = buf 416 a.size = size 417 a.typ = typ 418 a.normalized = normalized 419 a.stride = stride 420 a.offset = uintptr(offset) 421 f.VertexAttribPointer(gl.Attrib(idx), a.size, a.typ, a.normalized, a.stride, int(a.offset)) 422 } 423 424 func (s *glState) activeTexture(f *gl.Functions, unit gl.Enum) { 425 if unit != s.texUnits.active { 426 f.ActiveTexture(unit) 427 s.texUnits.active = unit 428 } 429 } 430 431 func (s *glState) bindTexture(f *gl.Functions, unit int, t gl.Texture) { 432 s.activeTexture(f, gl.TEXTURE0+gl.Enum(unit)) 433 if !t.Equal(s.texUnits.binds[unit]) { 434 f.BindTexture(gl.TEXTURE_2D, t) 435 s.texUnits.binds[unit] = t 436 } 437 } 438 439 func (s *glState) bindVertexArray(f *gl.Functions, a gl.VertexArray) { 440 if !a.Equal(s.vertArray) { 441 f.BindVertexArray(a) 442 s.vertArray = a 443 } 444 } 445 446 func (s *glState) deleteFramebuffer(f *gl.Functions, fbo gl.Framebuffer) { 447 f.DeleteFramebuffer(fbo) 448 if fbo.Equal(s.drawFBO) { 449 s.drawFBO = gl.Framebuffer{} 450 } 451 if fbo.Equal(s.readFBO) { 452 s.readFBO = gl.Framebuffer{} 453 } 454 } 455 456 func (s *glState) deleteBuffer(f *gl.Functions, b gl.Buffer) { 457 f.DeleteBuffer(b) 458 if b.Equal(s.arrayBuf) { 459 s.arrayBuf = gl.Buffer{} 460 } 461 if b.Equal(s.elemBuf) { 462 s.elemBuf = gl.Buffer{} 463 } 464 if b.Equal(s.uniBuf) { 465 s.uniBuf = gl.Buffer{} 466 } 467 if b.Equal(s.storeBuf) { 468 s.uniBuf = gl.Buffer{} 469 } 470 for i, b2 := range s.storeBufs { 471 if b.Equal(b2) { 472 s.storeBufs[i] = gl.Buffer{} 473 } 474 } 475 for i, b2 := range s.uniBufs { 476 if b.Equal(b2) { 477 s.uniBufs[i] = gl.Buffer{} 478 } 479 } 480 } 481 482 func (s *glState) deleteProgram(f *gl.Functions, p gl.Program) { 483 f.DeleteProgram(p) 484 if p.Equal(s.prog) { 485 s.prog = gl.Program{} 486 } 487 } 488 489 func (s *glState) deleteVertexArray(f *gl.Functions, a gl.VertexArray) { 490 f.DeleteVertexArray(a) 491 if a.Equal(s.vertArray) { 492 s.vertArray = gl.VertexArray{} 493 } 494 } 495 496 func (s *glState) deleteTexture(f *gl.Functions, t gl.Texture) { 497 f.DeleteTexture(t) 498 binds := &s.texUnits.binds 499 for i, obj := range binds { 500 if t.Equal(obj) { 501 binds[i] = gl.Texture{} 502 } 503 } 504 } 505 506 func (s *glState) useProgram(f *gl.Functions, p gl.Program) { 507 if !p.Equal(s.prog) { 508 f.UseProgram(p) 509 s.prog = p 510 } 511 } 512 513 func (s *glState) bindFramebuffer(f *gl.Functions, target gl.Enum, fbo gl.Framebuffer) { 514 switch target { 515 case gl.FRAMEBUFFER: 516 if fbo.Equal(s.drawFBO) && fbo.Equal(s.readFBO) { 517 return 518 } 519 s.drawFBO = fbo 520 s.readFBO = fbo 521 case gl.READ_FRAMEBUFFER: 522 if fbo.Equal(s.readFBO) { 523 return 524 } 525 s.readFBO = fbo 526 case gl.DRAW_FRAMEBUFFER: 527 if fbo.Equal(s.drawFBO) { 528 return 529 } 530 s.drawFBO = fbo 531 default: 532 panic("unknown target") 533 } 534 f.BindFramebuffer(target, fbo) 535 } 536 537 func (s *glState) bindBufferBase(f *gl.Functions, target gl.Enum, idx int, buf gl.Buffer) { 538 switch target { 539 case gl.UNIFORM_BUFFER: 540 if buf.Equal(s.uniBuf) && buf.Equal(s.uniBufs[idx]) { 541 return 542 } 543 s.uniBuf = buf 544 s.uniBufs[idx] = buf 545 case gl.SHADER_STORAGE_BUFFER: 546 if buf.Equal(s.storeBuf) && buf.Equal(s.storeBufs[idx]) { 547 return 548 } 549 s.storeBuf = buf 550 s.storeBufs[idx] = buf 551 default: 552 panic("unknown buffer target") 553 } 554 f.BindBufferBase(target, idx, buf) 555 } 556 557 func (s *glState) bindBuffer(f *gl.Functions, target gl.Enum, buf gl.Buffer) { 558 switch target { 559 case gl.ARRAY_BUFFER: 560 if buf.Equal(s.arrayBuf) { 561 return 562 } 563 s.arrayBuf = buf 564 case gl.ELEMENT_ARRAY_BUFFER: 565 if buf.Equal(s.elemBuf) { 566 return 567 } 568 s.elemBuf = buf 569 case gl.UNIFORM_BUFFER: 570 if buf.Equal(s.uniBuf) { 571 return 572 } 573 s.uniBuf = buf 574 case gl.SHADER_STORAGE_BUFFER: 575 if buf.Equal(s.storeBuf) { 576 return 577 } 578 s.storeBuf = buf 579 default: 580 panic("unknown buffer target") 581 } 582 f.BindBuffer(target, buf) 583 } 584 585 func (s *glState) pixelStorei(f *gl.Functions, pname gl.Enum, val int) { 586 switch pname { 587 case gl.UNPACK_ROW_LENGTH: 588 if val == s.unpack_row_length { 589 return 590 } 591 s.unpack_row_length = val 592 case gl.PACK_ROW_LENGTH: 593 if val == s.pack_row_length { 594 return 595 } 596 s.pack_row_length = val 597 default: 598 panic("unsupported PixelStorei pname") 599 } 600 f.PixelStorei(pname, val) 601 } 602 603 func (s *glState) setClearColor(f *gl.Functions, r, g, b, a float32) { 604 col := [4]float32{r, g, b, a} 605 if col != s.clearColor { 606 f.ClearColor(r, g, b, a) 607 s.clearColor = col 608 } 609 } 610 611 func (s *glState) setViewport(f *gl.Functions, x, y, width, height int) { 612 view := [4]int{x, y, width, height} 613 if view != s.viewport { 614 f.Viewport(x, y, width, height) 615 s.viewport = view 616 } 617 } 618 619 func (s *glState) setBlendFuncSeparate(f *gl.Functions, srcRGB, dstRGB, srcA, dstA gl.Enum) { 620 if srcRGB != s.blend.srcRGB || dstRGB != s.blend.dstRGB || srcA != s.blend.srcA || dstA != s.blend.dstA { 621 s.blend.srcRGB = srcRGB 622 s.blend.dstRGB = dstRGB 623 s.blend.srcA = srcA 624 s.blend.dstA = dstA 625 f.BlendFuncSeparate(srcA, dstA, srcA, dstA) 626 } 627 } 628 629 func (s *glState) set(f *gl.Functions, target gl.Enum, enable bool) { 630 switch target { 631 case gl.FRAMEBUFFER_SRGB: 632 if s.srgb == enable { 633 return 634 } 635 s.srgb = enable 636 case gl.BLEND: 637 if enable == s.blend.enable { 638 return 639 } 640 s.blend.enable = enable 641 default: 642 panic("unknown enable") 643 } 644 if enable { 645 f.Enable(target) 646 } else { 647 f.Disable(target) 648 } 649 } 650 651 func (b *Backend) Caps() driver.Caps { 652 return b.feats 653 } 654 655 func (b *Backend) NewTimer() driver.Timer { 656 return &timer{ 657 funcs: b.funcs, 658 obj: b.funcs.CreateQuery(), 659 } 660 } 661 662 func (b *Backend) IsTimeContinuous() bool { 663 return b.funcs.GetInteger(gl.GPU_DISJOINT_EXT) == gl.FALSE 664 } 665 666 func (t *texture) ensureFBO() gl.Framebuffer { 667 if t.hasFBO { 668 return t.fbo 669 } 670 b := t.backend 671 oldFBO := b.glstate.drawFBO 672 defer func() { 673 b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, oldFBO) 674 }() 675 glErr(b.funcs) 676 fb := b.funcs.CreateFramebuffer() 677 b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fb) 678 if err := glErr(b.funcs); err != nil { 679 b.funcs.DeleteFramebuffer(fb) 680 panic(err) 681 } 682 b.funcs.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, t.obj, 0) 683 if st := b.funcs.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE { 684 b.funcs.DeleteFramebuffer(fb) 685 panic(fmt.Errorf("incomplete framebuffer, status = 0x%x, err = %d", st, b.funcs.GetError())) 686 } 687 t.fbo = fb 688 t.hasFBO = true 689 return fb 690 } 691 692 func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, binding driver.BufferBinding) (driver.Texture, error) { 693 glErr(b.funcs) 694 tex := &texture{backend: b, obj: b.funcs.CreateTexture(), width: width, height: height, bindings: binding} 695 switch format { 696 case driver.TextureFormatFloat: 697 tex.triple = b.floatTriple 698 case driver.TextureFormatSRGBA: 699 tex.triple = b.srgbaTriple 700 case driver.TextureFormatRGBA8: 701 tex.triple = textureTriple{gl.RGBA8, gl.RGBA, gl.UNSIGNED_BYTE} 702 default: 703 return nil, errors.New("unsupported texture format") 704 } 705 b.BindTexture(0, tex) 706 min, mipmap := toTexFilter(minFilter) 707 mag, _ := toTexFilter(magFilter) 708 if b.gles && b.glver[0] < 3 { 709 // OpenGL ES 2 only supports mipmaps for power-of-two textures. 710 mipmap = false 711 } 712 tex.mipmap = mipmap 713 b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, mag) 714 b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, min) 715 b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) 716 b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) 717 if mipmap { 718 nmipmaps := 1 719 if mipmap { 720 dim := width 721 if height > dim { 722 dim = height 723 } 724 log2 := 32 - bits.LeadingZeros32(uint32(dim)) - 1 725 nmipmaps = log2 + 1 726 } 727 // Immutable textures are required for BindImageTexture, and can't hurt otherwise. 728 b.funcs.TexStorage2D(gl.TEXTURE_2D, nmipmaps, tex.triple.internalFormat, width, height) 729 } else { 730 b.funcs.TexImage2D(gl.TEXTURE_2D, 0, tex.triple.internalFormat, width, height, tex.triple.format, tex.triple.typ) 731 } 732 if err := glErr(b.funcs); err != nil { 733 tex.Release() 734 return nil, err 735 } 736 return tex, nil 737 } 738 739 func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) { 740 glErr(b.funcs) 741 buf := &buffer{backend: b, typ: typ, size: size} 742 if typ&driver.BufferBindingUniforms != 0 { 743 if typ != driver.BufferBindingUniforms { 744 return nil, errors.New("uniforms buffers cannot be bound as anything else") 745 } 746 buf.data = make([]byte, size) 747 } 748 if typ&^driver.BufferBindingUniforms != 0 { 749 buf.hasBuffer = true 750 buf.obj = b.funcs.CreateBuffer() 751 if err := glErr(b.funcs); err != nil { 752 buf.Release() 753 return nil, err 754 } 755 firstBinding := firstBufferType(typ) 756 b.glstate.bindBuffer(b.funcs, firstBinding, buf.obj) 757 b.funcs.BufferData(firstBinding, size, gl.DYNAMIC_DRAW, nil) 758 } 759 return buf, nil 760 } 761 762 func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) { 763 glErr(b.funcs) 764 obj := b.funcs.CreateBuffer() 765 buf := &buffer{backend: b, obj: obj, typ: typ, size: len(data), hasBuffer: true} 766 firstBinding := firstBufferType(typ) 767 b.glstate.bindBuffer(b.funcs, firstBinding, buf.obj) 768 b.funcs.BufferData(firstBinding, len(data), gl.STATIC_DRAW, data) 769 buf.immutable = true 770 if err := glErr(b.funcs); err != nil { 771 buf.Release() 772 return nil, err 773 } 774 return buf, nil 775 } 776 777 func glErr(f *gl.Functions) error { 778 if st := f.GetError(); st != gl.NO_ERROR { 779 return fmt.Errorf("glGetError: %#x", st) 780 } 781 return nil 782 } 783 784 func (b *Backend) Release() { 785 if b.sRGBFBO != nil { 786 b.sRGBFBO.Release() 787 } 788 if b.vertArray.Valid() { 789 b.glstate.deleteVertexArray(b.funcs, b.vertArray) 790 } 791 *b = Backend{} 792 } 793 794 func (b *Backend) DispatchCompute(x, y, z int) { 795 for binding, buf := range b.storage { 796 if buf != nil { 797 b.glstate.bindBufferBase(b.funcs, gl.SHADER_STORAGE_BUFFER, binding, buf.obj) 798 } 799 } 800 b.funcs.DispatchCompute(x, y, z) 801 b.funcs.MemoryBarrier(gl.ALL_BARRIER_BITS) 802 } 803 804 func (b *Backend) BindImageTexture(unit int, tex driver.Texture) { 805 t := tex.(*texture) 806 var acc gl.Enum 807 switch t.bindings & (driver.BufferBindingShaderStorageRead | driver.BufferBindingShaderStorageWrite) { 808 case driver.BufferBindingShaderStorageRead: 809 acc = gl.READ_ONLY 810 case driver.BufferBindingShaderStorageWrite: 811 acc = gl.WRITE_ONLY 812 case driver.BufferBindingShaderStorageRead | driver.BufferBindingShaderStorageWrite: 813 acc = gl.READ_WRITE 814 default: 815 panic("unsupported access bits") 816 } 817 b.funcs.BindImageTexture(unit, t.obj, 0, false, 0, acc, t.triple.internalFormat) 818 } 819 820 func (b *Backend) BlendFunc(sfactor, dfactor driver.BlendFactor) { 821 src, dst := toGLBlendFactor(sfactor), toGLBlendFactor(dfactor) 822 b.glstate.setBlendFuncSeparate(b.funcs, src, dst, src, dst) 823 } 824 825 func toGLBlendFactor(f driver.BlendFactor) gl.Enum { 826 switch f { 827 case driver.BlendFactorOne: 828 return gl.ONE 829 case driver.BlendFactorOneMinusSrcAlpha: 830 return gl.ONE_MINUS_SRC_ALPHA 831 case driver.BlendFactorZero: 832 return gl.ZERO 833 case driver.BlendFactorDstColor: 834 return gl.DST_COLOR 835 default: 836 panic("unsupported blend factor") 837 } 838 } 839 840 func (b *Backend) SetBlend(enable bool) { 841 b.glstate.set(b.funcs, gl.BLEND, enable) 842 } 843 844 func (b *Backend) DrawElements(off, count int) { 845 b.prepareDraw() 846 // off is in 16-bit indices, but DrawElements take a byte offset. 847 byteOff := off * 2 848 b.funcs.DrawElements(toGLDrawMode(b.state.pipeline.topology), count, gl.UNSIGNED_SHORT, byteOff) 849 } 850 851 func (b *Backend) DrawArrays(off, count int) { 852 b.prepareDraw() 853 b.funcs.DrawArrays(toGLDrawMode(b.state.pipeline.topology), off, count) 854 } 855 856 func (b *Backend) prepareDraw() { 857 p := b.state.pipeline 858 if p == nil { 859 return 860 } 861 b.setupVertexArrays() 862 } 863 864 func toGLDrawMode(mode driver.Topology) gl.Enum { 865 switch mode { 866 case driver.TopologyTriangleStrip: 867 return gl.TRIANGLE_STRIP 868 case driver.TopologyTriangles: 869 return gl.TRIANGLES 870 default: 871 panic("unsupported draw mode") 872 } 873 } 874 875 func (b *Backend) Viewport(x, y, width, height int) { 876 b.glstate.setViewport(b.funcs, x, y, width, height) 877 } 878 879 func (b *Backend) clearOutput(colR, colG, colB, colA float32) { 880 b.glstate.setClearColor(b.funcs, colR, colG, colB, colA) 881 b.funcs.Clear(gl.COLOR_BUFFER_BIT) 882 } 883 884 func (b *Backend) NewComputeProgram(src shader.Sources) (driver.Program, error) { 885 // We don't support ES 3.1 compute, see brokenGLES31 above. 886 const GLES31Source = "" 887 p, err := gl.CreateComputeProgram(b.funcs, GLES31Source) 888 if err != nil { 889 return nil, fmt.Errorf("%s: %v", src.Name, err) 890 } 891 return &program{ 892 backend: b, 893 obj: p, 894 }, nil 895 } 896 897 func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) { 898 glslSrc := b.glslFor(src) 899 sh, err := gl.CreateShader(b.funcs, gl.VERTEX_SHADER, glslSrc) 900 return &glshader{backend: b, obj: sh, src: src}, err 901 } 902 903 func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) { 904 glslSrc := b.glslFor(src) 905 sh, err := gl.CreateShader(b.funcs, gl.FRAGMENT_SHADER, glslSrc) 906 return &glshader{backend: b, obj: sh, src: src}, err 907 } 908 909 func (b *Backend) glslFor(src shader.Sources) string { 910 if b.gles { 911 return src.GLSL100ES 912 } else { 913 return src.GLSL150 914 } 915 } 916 917 func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) { 918 p, err := b.newProgram(desc) 919 if err != nil { 920 return nil, err 921 } 922 layout := desc.VertexLayout 923 vsrc := desc.VertexShader.(*glshader).src 924 if len(vsrc.Inputs) != len(layout.Inputs) { 925 return nil, fmt.Errorf("opengl: got %d inputs, expected %d", len(layout.Inputs), len(vsrc.Inputs)) 926 } 927 for i, inp := range vsrc.Inputs { 928 if exp, got := inp.Size, layout.Inputs[i].Size; exp != got { 929 return nil, fmt.Errorf("opengl: data size mismatch for %q: got %d expected %d", inp.Name, got, exp) 930 } 931 } 932 return &pipeline{ 933 prog: p, 934 inputs: vsrc.Inputs, 935 layout: layout, 936 blend: desc.BlendDesc, 937 topology: desc.Topology, 938 }, nil 939 } 940 941 func (b *Backend) newProgram(desc driver.PipelineDesc) (*program, error) { 942 p := b.funcs.CreateProgram() 943 if !p.Valid() { 944 return nil, errors.New("opengl: glCreateProgram failed") 945 } 946 vsh, fsh := desc.VertexShader.(*glshader), desc.FragmentShader.(*glshader) 947 b.funcs.AttachShader(p, vsh.obj) 948 b.funcs.AttachShader(p, fsh.obj) 949 for _, inp := range vsh.src.Inputs { 950 b.funcs.BindAttribLocation(p, gl.Attrib(inp.Location), inp.Name) 951 } 952 b.funcs.LinkProgram(p) 953 if b.funcs.GetProgrami(p, gl.LINK_STATUS) == 0 { 954 log := b.funcs.GetProgramInfoLog(p) 955 b.funcs.DeleteProgram(p) 956 return nil, fmt.Errorf("opengl: program link failed: %s", strings.TrimSpace(log)) 957 } 958 prog := &program{ 959 backend: b, 960 obj: p, 961 } 962 b.glstate.useProgram(b.funcs, p) 963 // Bind texture uniforms. 964 for _, tex := range vsh.src.Textures { 965 u := b.funcs.GetUniformLocation(p, tex.Name) 966 if u.Valid() { 967 b.funcs.Uniform1i(u, tex.Binding) 968 } 969 } 970 for _, tex := range fsh.src.Textures { 971 u := b.funcs.GetUniformLocation(p, tex.Name) 972 if u.Valid() { 973 b.funcs.Uniform1i(u, tex.Binding) 974 } 975 } 976 prog.vertUniforms.setup(b.funcs, p, vsh.src.Uniforms.Size, vsh.src.Uniforms.Locations) 977 prog.fragUniforms.setup(b.funcs, p, fsh.src.Uniforms.Size, fsh.src.Uniforms.Locations) 978 return prog, nil 979 } 980 981 func (b *Backend) BindStorageBuffer(binding int, buf driver.Buffer) { 982 bf := buf.(*buffer) 983 if bf.typ&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) == 0 { 984 panic("not a shader storage buffer") 985 } 986 b.storage[binding] = bf 987 } 988 989 func (b *Backend) BindUniforms(buf driver.Buffer) { 990 bf := buf.(*buffer) 991 if bf.typ&driver.BufferBindingUniforms == 0 { 992 panic("not a uniform buffer") 993 } 994 b.state.pipeline.prog.vertUniforms.update(b.funcs, bf) 995 b.state.pipeline.prog.fragUniforms.update(b.funcs, bf) 996 } 997 998 func (b *Backend) BindProgram(prog driver.Program) { 999 p := prog.(*program) 1000 b.glstate.useProgram(b.funcs, p.obj) 1001 } 1002 1003 func (s *glshader) Release() { 1004 s.backend.funcs.DeleteShader(s.obj) 1005 } 1006 1007 func (p *program) Release() { 1008 p.backend.glstate.deleteProgram(p.backend.funcs, p.obj) 1009 } 1010 1011 func (u *uniforms) setup(funcs *gl.Functions, p gl.Program, uniformSize int, uniforms []shader.UniformLocation) { 1012 u.locs = make([]uniformLocation, len(uniforms)) 1013 for i, uniform := range uniforms { 1014 loc := funcs.GetUniformLocation(p, uniform.Name) 1015 u.locs[i] = uniformLocation{uniform: loc, offset: uniform.Offset, typ: uniform.Type, size: uniform.Size} 1016 } 1017 u.size = uniformSize 1018 } 1019 1020 func (p *uniforms) update(funcs *gl.Functions, buf *buffer) { 1021 if buf.size < p.size { 1022 panic(fmt.Errorf("uniform buffer too small, got %d need %d", buf.size, p.size)) 1023 } 1024 data := buf.data 1025 for _, u := range p.locs { 1026 if !u.uniform.Valid() { 1027 continue 1028 } 1029 data := data[u.offset:] 1030 switch { 1031 case u.typ == shader.DataTypeFloat && u.size == 1: 1032 data := data[:4] 1033 v := *(*[1]float32)(unsafe.Pointer(&data[0])) 1034 funcs.Uniform1f(u.uniform, v[0]) 1035 case u.typ == shader.DataTypeFloat && u.size == 2: 1036 data := data[:8] 1037 v := *(*[2]float32)(unsafe.Pointer(&data[0])) 1038 funcs.Uniform2f(u.uniform, v[0], v[1]) 1039 case u.typ == shader.DataTypeFloat && u.size == 3: 1040 data := data[:12] 1041 v := *(*[3]float32)(unsafe.Pointer(&data[0])) 1042 funcs.Uniform3f(u.uniform, v[0], v[1], v[2]) 1043 case u.typ == shader.DataTypeFloat && u.size == 4: 1044 data := data[:16] 1045 v := *(*[4]float32)(unsafe.Pointer(&data[0])) 1046 funcs.Uniform4f(u.uniform, v[0], v[1], v[2], v[3]) 1047 default: 1048 panic("unsupported uniform data type or size") 1049 } 1050 } 1051 } 1052 1053 func (b *buffer) Upload(data []byte) { 1054 if b.immutable { 1055 panic("immutable buffer") 1056 } 1057 if len(data) > b.size { 1058 panic("buffer size overflow") 1059 } 1060 copy(b.data, data) 1061 if b.hasBuffer { 1062 firstBinding := firstBufferType(b.typ) 1063 b.backend.glstate.bindBuffer(b.backend.funcs, firstBinding, b.obj) 1064 if len(data) == b.size { 1065 // the iOS GL implementation doesn't recognize when BufferSubData 1066 // clears the entire buffer. Tell it and avoid GPU stalls. 1067 // See also https://github.com/godotengine/godot/issues/23956. 1068 b.backend.funcs.BufferData(firstBinding, b.size, gl.DYNAMIC_DRAW, data) 1069 } else { 1070 b.backend.funcs.BufferSubData(firstBinding, 0, data) 1071 } 1072 } 1073 } 1074 1075 func (b *buffer) Download(data []byte) error { 1076 if len(data) > b.size { 1077 panic("buffer size overflow") 1078 } 1079 if !b.hasBuffer { 1080 copy(data, b.data) 1081 return nil 1082 } 1083 firstBinding := firstBufferType(b.typ) 1084 b.backend.glstate.bindBuffer(b.backend.funcs, firstBinding, b.obj) 1085 bufferMap := b.backend.funcs.MapBufferRange(firstBinding, 0, len(data), gl.MAP_READ_BIT) 1086 if bufferMap == nil { 1087 return fmt.Errorf("MapBufferRange: error %#x", b.backend.funcs.GetError()) 1088 } 1089 copy(data, bufferMap) 1090 if !b.backend.funcs.UnmapBuffer(firstBinding) { 1091 return driver.ErrContentLost 1092 } 1093 return nil 1094 } 1095 1096 func (b *buffer) Release() { 1097 if b.hasBuffer { 1098 b.backend.glstate.deleteBuffer(b.backend.funcs, b.obj) 1099 b.hasBuffer = false 1100 } 1101 } 1102 1103 func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) { 1104 gbuf := buf.(*buffer) 1105 if gbuf.typ&driver.BufferBindingVertices == 0 { 1106 panic("not a vertex buffer") 1107 } 1108 b.state.buffer = bufferBinding{obj: gbuf.obj, offset: offset} 1109 } 1110 1111 func (b *Backend) setupVertexArrays() { 1112 p := b.state.pipeline 1113 inputs := p.inputs 1114 if len(inputs) == 0 { 1115 return 1116 } 1117 layout := p.layout 1118 const max = len(b.glstate.vertAttribs) 1119 var enabled [max]bool 1120 buf := b.state.buffer 1121 for i, inp := range inputs { 1122 l := layout.Inputs[i] 1123 var gltyp gl.Enum 1124 switch l.Type { 1125 case shader.DataTypeFloat: 1126 gltyp = gl.FLOAT 1127 case shader.DataTypeShort: 1128 gltyp = gl.SHORT 1129 default: 1130 panic("unsupported data type") 1131 } 1132 enabled[inp.Location] = true 1133 b.glstate.vertexAttribPointer(b.funcs, buf.obj, inp.Location, l.Size, gltyp, false, p.layout.Stride, buf.offset+l.Offset) 1134 } 1135 for i := 0; i < max; i++ { 1136 b.glstate.setVertexAttribArray(b.funcs, i, enabled[i]) 1137 } 1138 } 1139 1140 func (b *Backend) BindIndexBuffer(buf driver.Buffer) { 1141 gbuf := buf.(*buffer) 1142 if gbuf.typ&driver.BufferBindingIndices == 0 { 1143 panic("not an index buffer") 1144 } 1145 b.glstate.bindBuffer(b.funcs, gl.ELEMENT_ARRAY_BUFFER, gbuf.obj) 1146 } 1147 1148 func (b *Backend) CopyTexture(dst driver.Texture, dstOrigin image.Point, src driver.Texture, srcRect image.Rectangle) { 1149 const unit = 0 1150 oldTex := b.glstate.texUnits.binds[unit] 1151 defer func() { 1152 b.glstate.bindTexture(b.funcs, unit, oldTex) 1153 }() 1154 b.glstate.bindTexture(b.funcs, unit, dst.(*texture).obj) 1155 b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, src.(*texture).ensureFBO()) 1156 sz := srcRect.Size() 1157 b.funcs.CopyTexSubImage2D(gl.TEXTURE_2D, 0, dstOrigin.X, dstOrigin.Y, srcRect.Min.X, srcRect.Min.Y, sz.X, sz.Y) 1158 } 1159 1160 func (t *texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error { 1161 glErr(t.backend.funcs) 1162 t.backend.glstate.bindFramebuffer(t.backend.funcs, gl.FRAMEBUFFER, t.ensureFBO()) 1163 w, h := src.Dx(), src.Dy() 1164 if len(pixels) < w*h*4 { 1165 return errors.New("unexpected RGBA size") 1166 } 1167 // OpenGL ES 2 doesn't support PACK_ROW_LENGTH != 0. Avoid it if possible. 1168 rowLen := 0 1169 if n := stride / 4; n != w { 1170 rowLen = n 1171 } 1172 if rowLen == 0 || t.backend.glver[0] > 2 { 1173 t.backend.glstate.pixelStorei(t.backend.funcs, gl.PACK_ROW_LENGTH, rowLen) 1174 t.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels) 1175 } else { 1176 tmp := make([]byte, w*h*4) 1177 t.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, tmp) 1178 for y := 0; y < h; y++ { 1179 copy(pixels[y*stride:], tmp[y*w*4:]) 1180 } 1181 } 1182 return glErr(t.backend.funcs) 1183 } 1184 1185 func (b *Backend) BindPipeline(pl driver.Pipeline) { 1186 p := pl.(*pipeline) 1187 b.state.pipeline = p 1188 b.glstate.useProgram(b.funcs, p.prog.obj) 1189 b.SetBlend(p.blend.Enable) 1190 b.BlendFunc(p.blend.SrcFactor, p.blend.DstFactor) 1191 } 1192 1193 func (b *Backend) BeginCompute() { 1194 b.funcs.MemoryBarrier(gl.ALL_BARRIER_BITS) 1195 } 1196 1197 func (b *Backend) EndCompute() { 1198 } 1199 1200 func (b *Backend) BeginRenderPass(tex driver.Texture, desc driver.LoadDesc) { 1201 fbo := tex.(*texture).ensureFBO() 1202 b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fbo) 1203 switch desc.Action { 1204 case driver.LoadActionClear: 1205 c := desc.ClearColor 1206 b.clearOutput(c.R, c.G, c.B, c.A) 1207 case driver.LoadActionInvalidate: 1208 b.funcs.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0) 1209 } 1210 } 1211 1212 func (b *Backend) EndRenderPass() { 1213 } 1214 1215 func (f *texture) ImplementsRenderTarget() {} 1216 1217 func (p *pipeline) Release() { 1218 p.prog.Release() 1219 *p = pipeline{} 1220 } 1221 1222 func toTexFilter(f driver.TextureFilter) (int, bool) { 1223 switch f { 1224 case driver.FilterNearest: 1225 return gl.NEAREST, false 1226 case driver.FilterLinear: 1227 return gl.LINEAR, false 1228 case driver.FilterLinearMipmapLinear: 1229 return gl.LINEAR_MIPMAP_LINEAR, true 1230 default: 1231 panic("unsupported texture filter") 1232 } 1233 } 1234 1235 func (b *Backend) PrepareTexture(tex driver.Texture) {} 1236 1237 func (b *Backend) BindTexture(unit int, t driver.Texture) { 1238 b.glstate.bindTexture(b.funcs, unit, t.(*texture).obj) 1239 } 1240 1241 func (t *texture) Release() { 1242 if t.foreign { 1243 panic("texture not created by NewTexture") 1244 } 1245 if t.hasFBO { 1246 t.backend.glstate.deleteFramebuffer(t.backend.funcs, t.fbo) 1247 } 1248 t.backend.glstate.deleteTexture(t.backend.funcs, t.obj) 1249 } 1250 1251 func (t *texture) Upload(offset, size image.Point, pixels []byte, stride int) { 1252 if min := size.X * size.Y * 4; min > len(pixels) { 1253 panic(fmt.Errorf("size %d larger than data %d", min, len(pixels))) 1254 } 1255 t.backend.BindTexture(0, t) 1256 // WebGL 1 doesn't support UNPACK_ROW_LENGTH != 0. Avoid it if possible. 1257 rowLen := 0 1258 if n := stride / 4; n != size.X { 1259 rowLen = n 1260 } 1261 t.backend.glstate.pixelStorei(t.backend.funcs, gl.UNPACK_ROW_LENGTH, rowLen) 1262 t.backend.funcs.TexSubImage2D(gl.TEXTURE_2D, 0, offset.X, offset.Y, size.X, size.Y, t.triple.format, t.triple.typ, pixels) 1263 if t.mipmap { 1264 t.backend.funcs.GenerateMipmap(gl.TEXTURE_2D) 1265 } 1266 } 1267 1268 func (t *timer) Begin() { 1269 t.funcs.BeginQuery(gl.TIME_ELAPSED_EXT, t.obj) 1270 } 1271 1272 func (t *timer) End() { 1273 t.funcs.EndQuery(gl.TIME_ELAPSED_EXT) 1274 } 1275 1276 func (t *timer) ready() bool { 1277 return t.funcs.GetQueryObjectuiv(t.obj, gl.QUERY_RESULT_AVAILABLE) == gl.TRUE 1278 } 1279 1280 func (t *timer) Release() { 1281 t.funcs.DeleteQuery(t.obj) 1282 } 1283 1284 func (t *timer) Duration() (time.Duration, bool) { 1285 if !t.ready() { 1286 return 0, false 1287 } 1288 nanos := t.funcs.GetQueryObjectuiv(t.obj, gl.QUERY_RESULT) 1289 return time.Duration(nanos), true 1290 } 1291 1292 // floatTripleFor determines the best texture triple for floating point FBOs. 1293 func floatTripleFor(f *gl.Functions, ver [2]int, exts []string) (textureTriple, error) { 1294 var triples []textureTriple 1295 if ver[0] >= 3 { 1296 triples = append(triples, textureTriple{gl.R16F, gl.Enum(gl.RED), gl.Enum(gl.HALF_FLOAT)}) 1297 } 1298 // According to the OES_texture_half_float specification, EXT_color_buffer_half_float is needed to 1299 // render to FBOs. However, the Safari WebGL1 implementation does support half-float FBOs but does not 1300 // report EXT_color_buffer_half_float support. The triples are verified below, so it doesn't matter if we're 1301 // wrong. 1302 if hasExtension(exts, "GL_OES_texture_half_float") || hasExtension(exts, "GL_EXT_color_buffer_half_float") { 1303 // Try single channel. 1304 triples = append(triples, textureTriple{gl.LUMINANCE, gl.Enum(gl.LUMINANCE), gl.Enum(gl.HALF_FLOAT_OES)}) 1305 // Fallback to 4 channels. 1306 triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.HALF_FLOAT_OES)}) 1307 } 1308 if hasExtension(exts, "GL_OES_texture_float") || hasExtension(exts, "GL_EXT_color_buffer_float") { 1309 triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.FLOAT)}) 1310 } 1311 tex := f.CreateTexture() 1312 defer f.DeleteTexture(tex) 1313 defTex := gl.Texture(f.GetBinding(gl.TEXTURE_BINDING_2D)) 1314 defer f.BindTexture(gl.TEXTURE_2D, defTex) 1315 f.BindTexture(gl.TEXTURE_2D, tex) 1316 f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) 1317 f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) 1318 f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) 1319 f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) 1320 fbo := f.CreateFramebuffer() 1321 defer f.DeleteFramebuffer(fbo) 1322 defFBO := gl.Framebuffer(f.GetBinding(gl.FRAMEBUFFER_BINDING)) 1323 f.BindFramebuffer(gl.FRAMEBUFFER, fbo) 1324 defer f.BindFramebuffer(gl.FRAMEBUFFER, defFBO) 1325 var attempts []string 1326 for _, tt := range triples { 1327 const size = 256 1328 f.TexImage2D(gl.TEXTURE_2D, 0, tt.internalFormat, size, size, tt.format, tt.typ) 1329 f.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0) 1330 st := f.CheckFramebufferStatus(gl.FRAMEBUFFER) 1331 if st == gl.FRAMEBUFFER_COMPLETE { 1332 return tt, nil 1333 } 1334 attempts = append(attempts, fmt.Sprintf("(0x%x, 0x%x, 0x%x): 0x%x", tt.internalFormat, tt.format, tt.typ, st)) 1335 } 1336 return textureTriple{}, fmt.Errorf("floating point fbos not supported (attempted %s)", attempts) 1337 } 1338 1339 func srgbaTripleFor(ver [2]int, exts []string) (textureTriple, error) { 1340 switch { 1341 case ver[0] >= 3: 1342 return textureTriple{gl.SRGB8_ALPHA8, gl.Enum(gl.RGBA), gl.Enum(gl.UNSIGNED_BYTE)}, nil 1343 case hasExtension(exts, "GL_EXT_sRGB"): 1344 return textureTriple{gl.SRGB_ALPHA_EXT, gl.Enum(gl.SRGB_ALPHA_EXT), gl.Enum(gl.UNSIGNED_BYTE)}, nil 1345 default: 1346 return textureTriple{}, errors.New("no sRGB texture formats found") 1347 } 1348 } 1349 1350 func alphaTripleFor(ver [2]int) textureTriple { 1351 intf, f := gl.Enum(gl.R8), gl.Enum(gl.RED) 1352 if ver[0] < 3 { 1353 // R8, RED not supported on OpenGL ES 2.0. 1354 intf, f = gl.LUMINANCE, gl.Enum(gl.LUMINANCE) 1355 } 1356 return textureTriple{intf, f, gl.UNSIGNED_BYTE} 1357 } 1358 1359 func hasExtension(exts []string, ext string) bool { 1360 for _, e := range exts { 1361 if ext == e { 1362 return true 1363 } 1364 } 1365 return false 1366 } 1367 1368 func firstBufferType(typ driver.BufferBinding) gl.Enum { 1369 switch { 1370 case typ&driver.BufferBindingIndices != 0: 1371 return gl.ELEMENT_ARRAY_BUFFER 1372 case typ&driver.BufferBindingVertices != 0: 1373 return gl.ARRAY_BUFFER 1374 case typ&driver.BufferBindingUniforms != 0: 1375 return gl.UNIFORM_BUFFER 1376 case typ&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0: 1377 return gl.SHADER_STORAGE_BUFFER 1378 default: 1379 panic("unsupported buffer type") 1380 } 1381 }