command.go (20226B)
1 // Copyright 2016 Hajime Hoshi 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package graphicscommand 16 17 import ( 18 "fmt" 19 "math" 20 "strings" 21 22 "github.com/hajimehoshi/ebiten/v2/internal/affine" 23 "github.com/hajimehoshi/ebiten/v2/internal/driver" 24 "github.com/hajimehoshi/ebiten/v2/internal/graphics" 25 "github.com/hajimehoshi/ebiten/v2/internal/shaderir" 26 ) 27 28 var theGraphicsDriver driver.Graphics 29 30 func SetGraphicsDriver(driver driver.Graphics) { 31 theGraphicsDriver = driver 32 } 33 34 func NeedsRestoring() bool { 35 if theGraphicsDriver == nil { 36 // This happens on initialization. 37 // Return true for fail-safe 38 return true 39 } 40 return theGraphicsDriver.NeedsRestoring() 41 } 42 43 // command represents a drawing command. 44 // 45 // A command for drawing that is created when Image functions are called like DrawTriangles, 46 // or Fill. 47 // A command is not immediately executed after created. Instaed, it is queued after created, 48 // and executed only when necessary. 49 type command interface { 50 fmt.Stringer 51 52 Exec(indexOffset int) error 53 NumVertices() int 54 NumIndices() int 55 AddNumVertices(n int) 56 AddNumIndices(n int) 57 CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool 58 } 59 60 type size struct { 61 width float32 62 height float32 63 } 64 65 // commandQueue is a command queue for drawing commands. 66 type commandQueue struct { 67 // commands is a queue of drawing commands. 68 commands []command 69 70 // vertices represents a vertices data in OpenGL's array buffer. 71 vertices []float32 72 73 // nvertices represents the current length of vertices. 74 // nvertices must <= len(vertices). 75 // vertices is never shrunk since re-extending a vertices buffer is heavy. 76 // 77 // TODO: This is a number of float32 values, not a number of vertices. 78 // Rename or fix the program. 79 nvertices int 80 81 srcSizes []size 82 83 indices []uint16 84 nindices int 85 86 tmpNumIndices int 87 nextIndex int 88 89 err error 90 } 91 92 // theCommandQueue is the command queue for the current process. 93 var theCommandQueue = &commandQueue{} 94 95 // appendVertices appends vertices to the queue. 96 func (q *commandQueue) appendVertices(vertices []float32, src *Image) { 97 if len(q.vertices) < q.nvertices+len(vertices) { 98 n := q.nvertices + len(vertices) - len(q.vertices) 99 q.vertices = append(q.vertices, make([]float32, n)...) 100 q.srcSizes = append(q.srcSizes, make([]size, n/graphics.VertexFloatNum)...) 101 } 102 copy(q.vertices[q.nvertices:], vertices) 103 104 n := len(vertices) / graphics.VertexFloatNum 105 base := q.nvertices / graphics.VertexFloatNum 106 107 width := float32(1) 108 height := float32(1) 109 // src is nil when a shader is used and there are no specified images. 110 if src != nil { 111 w, h := src.InternalSize() 112 width = float32(w) 113 height = float32(h) 114 } 115 for i := 0; i < n; i++ { 116 idx := base + i 117 q.srcSizes[idx].width = width 118 q.srcSizes[idx].height = height 119 } 120 q.nvertices += len(vertices) 121 } 122 123 func (q *commandQueue) appendIndices(indices []uint16, offset uint16) { 124 if len(q.indices) < q.nindices+len(indices) { 125 n := q.nindices + len(indices) - len(q.indices) 126 q.indices = append(q.indices, make([]uint16, n)...) 127 } 128 for i := range indices { 129 q.indices[q.nindices+i] = indices[i] + offset 130 } 131 q.nindices += len(indices) 132 } 133 134 // EnqueueDrawTrianglesCommand enqueues a drawing-image command. 135 func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) { 136 if len(indices) > graphics.IndicesNum { 137 panic(fmt.Sprintf("graphicscommand: len(indices) must be <= graphics.IndicesNum but not at EnqueueDrawTrianglesCommand: len(indices): %d, graphics.IndicesNum: %d", len(indices), graphics.IndicesNum)) 138 } 139 140 split := false 141 if q.tmpNumIndices+len(indices) > graphics.IndicesNum { 142 q.tmpNumIndices = 0 143 q.nextIndex = 0 144 split = true 145 } 146 147 // Assume that all the image sizes are same. 148 // Assume that the images are packed from the front in the slice srcs. 149 q.appendVertices(vertices, srcs[0]) 150 q.appendIndices(indices, uint16(q.nextIndex)) 151 q.nextIndex += len(vertices) / graphics.VertexFloatNum 152 q.tmpNumIndices += len(indices) 153 154 if srcs[0] != nil { 155 w, h := srcs[0].InternalSize() 156 sourceRegion.X /= float32(w) 157 sourceRegion.Y /= float32(h) 158 sourceRegion.Width /= float32(w) 159 sourceRegion.Height /= float32(h) 160 for i := range offsets { 161 offsets[i][0] /= float32(w) 162 offsets[i][1] /= float32(h) 163 } 164 } 165 166 // TODO: If dst is the screen, reorder the command to be the last. 167 if !split && 0 < len(q.commands) { 168 // TODO: Pass offsets and uniforms when merging considers the shader. 169 if last := q.commands[len(q.commands)-1]; last.CanMergeWithDrawTrianglesCommand(dst, srcs, color, mode, filter, address, sourceRegion, shader) { 170 last.AddNumVertices(len(vertices)) 171 last.AddNumIndices(len(indices)) 172 return 173 } 174 } 175 176 c := &drawTrianglesCommand{ 177 dst: dst, 178 srcs: srcs, 179 offsets: offsets, 180 nvertices: len(vertices), 181 nindices: len(indices), 182 color: color, 183 mode: mode, 184 filter: filter, 185 address: address, 186 sourceRegion: sourceRegion, 187 shader: shader, 188 uniforms: uniforms, 189 } 190 q.commands = append(q.commands, c) 191 } 192 193 // Enqueue enqueues a drawing command other than a draw-triangles command. 194 // 195 // For a draw-triangles command, use EnqueueDrawTrianglesCommand. 196 func (q *commandQueue) Enqueue(command command) { 197 // TODO: If dst is the screen, reorder the command to be the last. 198 q.commands = append(q.commands, command) 199 } 200 201 // Flush flushes the command queue. 202 func (q *commandQueue) Flush() error { 203 if len(q.commands) == 0 { 204 return nil 205 } 206 207 es := q.indices 208 vs := q.vertices 209 if recordLog() { 210 fmt.Println("--") 211 } 212 213 if theGraphicsDriver.HasHighPrecisionFloat() { 214 n := q.nvertices / graphics.VertexFloatNum 215 for i := 0; i < n; i++ { 216 s := q.srcSizes[i] 217 218 // Convert pixels to texels. 219 vs[i*graphics.VertexFloatNum+2] /= s.width 220 vs[i*graphics.VertexFloatNum+3] /= s.height 221 222 // Avoid the center of the pixel, which is problematic (#929, #1171). 223 // Instead, align the vertices with about 1/3 pixels. 224 for idx := 0; idx < 2; idx++ { 225 x := vs[i*graphics.VertexFloatNum+idx] 226 int := float32(math.Floor(float64(x))) 227 frac := x - int 228 switch { 229 case frac < 3.0/16.0: 230 vs[i*graphics.VertexFloatNum+idx] = int 231 case frac < 8.0/16.0: 232 vs[i*graphics.VertexFloatNum+idx] = int + 5.0/16.0 233 case frac < 13.0/16.0: 234 vs[i*graphics.VertexFloatNum+idx] = int + 11.0/16.0 235 default: 236 vs[i*graphics.VertexFloatNum+idx] = int + 16.0/16.0 237 } 238 } 239 } 240 } else { 241 n := q.nvertices / graphics.VertexFloatNum 242 for i := 0; i < n; i++ { 243 s := q.srcSizes[i] 244 245 // Convert pixels to texels. 246 vs[i*graphics.VertexFloatNum+2] /= s.width 247 vs[i*graphics.VertexFloatNum+3] /= s.height 248 } 249 } 250 251 theGraphicsDriver.Begin() 252 cs := q.commands 253 for len(cs) > 0 { 254 nv := 0 255 ne := 0 256 nc := 0 257 for _, c := range cs { 258 if c.NumIndices() > graphics.IndicesNum { 259 panic(fmt.Sprintf("graphicscommand: c.NumIndices() must be <= graphics.IndicesNum but not at Flush: c.NumIndices(): %d, graphics.IndicesNum: %d", c.NumIndices(), graphics.IndicesNum)) 260 } 261 if ne+c.NumIndices() > graphics.IndicesNum { 262 break 263 } 264 nv += c.NumVertices() 265 ne += c.NumIndices() 266 nc++ 267 } 268 if 0 < ne { 269 theGraphicsDriver.SetVertices(vs[:nv], es[:ne]) 270 es = es[ne:] 271 vs = vs[nv:] 272 } 273 indexOffset := 0 274 for _, c := range cs[:nc] { 275 if err := c.Exec(indexOffset); err != nil { 276 return err 277 } 278 if recordLog() { 279 fmt.Printf("%s\n", c) 280 } 281 // TODO: indexOffset should be reset if the command type is different 282 // from the previous one. This fix is needed when another drawing command is 283 // introduced than drawTrianglesCommand. 284 indexOffset += c.NumIndices() 285 } 286 cs = cs[nc:] 287 } 288 theGraphicsDriver.End() 289 q.commands = q.commands[:0] 290 q.nvertices = 0 291 q.nindices = 0 292 q.tmpNumIndices = 0 293 q.nextIndex = 0 294 return nil 295 } 296 297 // FlushCommands flushes the command queue. 298 func FlushCommands() error { 299 return theCommandQueue.Flush() 300 } 301 302 // drawTrianglesCommand represents a drawing command to draw an image on another image. 303 type drawTrianglesCommand struct { 304 dst *Image 305 srcs [graphics.ShaderImageNum]*Image 306 offsets [graphics.ShaderImageNum - 1][2]float32 307 nvertices int 308 nindices int 309 color *affine.ColorM 310 mode driver.CompositeMode 311 filter driver.Filter 312 address driver.Address 313 sourceRegion driver.Region 314 shader *Shader 315 uniforms []interface{} 316 } 317 318 func (c *drawTrianglesCommand) String() string { 319 mode := "" 320 switch c.mode { 321 case driver.CompositeModeSourceOver: 322 mode = "source-over" 323 case driver.CompositeModeClear: 324 mode = "clear" 325 case driver.CompositeModeCopy: 326 mode = "copy" 327 case driver.CompositeModeDestination: 328 mode = "destination" 329 case driver.CompositeModeDestinationOver: 330 mode = "destination-over" 331 case driver.CompositeModeSourceIn: 332 mode = "source-in" 333 case driver.CompositeModeDestinationIn: 334 mode = "destination-in" 335 case driver.CompositeModeSourceOut: 336 mode = "source-out" 337 case driver.CompositeModeDestinationOut: 338 mode = "destination-out" 339 case driver.CompositeModeSourceAtop: 340 mode = "source-atop" 341 case driver.CompositeModeDestinationAtop: 342 mode = "destination-atop" 343 case driver.CompositeModeXor: 344 mode = "xor" 345 case driver.CompositeModeLighter: 346 mode = "lighter" 347 default: 348 panic(fmt.Sprintf("graphicscommand: invalid composite mode: %d", c.mode)) 349 } 350 351 dst := fmt.Sprintf("%d", c.dst.id) 352 if c.dst.screen { 353 dst += " (screen)" 354 } 355 356 if c.shader != nil { 357 return fmt.Sprintf("draw-triangles: dst: %s, shader, num of indices: %d, mode %s", dst, c.nindices, mode) 358 } 359 360 filter := "" 361 switch c.filter { 362 case driver.FilterNearest: 363 filter = "nearest" 364 case driver.FilterLinear: 365 filter = "linear" 366 case driver.FilterScreen: 367 filter = "screen" 368 default: 369 panic(fmt.Sprintf("graphicscommand: invalid filter: %d", c.filter)) 370 } 371 372 address := "" 373 switch c.address { 374 case driver.AddressClampToZero: 375 address = "clamp_to_zero" 376 case driver.AddressRepeat: 377 address = "repeat" 378 case driver.AddressUnsafe: 379 address = "unsafe" 380 default: 381 panic(fmt.Sprintf("graphicscommand: invalid address: %d", c.address)) 382 } 383 384 var srcstrs [graphics.ShaderImageNum]string 385 for i, src := range c.srcs { 386 if src == nil { 387 srcstrs[i] = "(nil)" 388 continue 389 } 390 srcstrs[i] = fmt.Sprintf("%d", src.id) 391 if src.screen { 392 srcstrs[i] += " (screen)" 393 } 394 } 395 396 return fmt.Sprintf("draw-triangles: dst: %s <- src: [%s], num of indices: %d, colorm: %v, mode %s, filter: %s, address: %s", dst, strings.Join(srcstrs[:], ", "), c.nindices, c.color, mode, filter, address) 397 } 398 399 // Exec executes the drawTrianglesCommand. 400 func (c *drawTrianglesCommand) Exec(indexOffset int) error { 401 // TODO: Is it ok not to bind any framebuffer here? 402 if c.nindices == 0 { 403 return nil 404 } 405 406 if c.shader != nil { 407 var imgs [graphics.ShaderImageNum]driver.ImageID 408 for i, src := range c.srcs { 409 if src == nil { 410 imgs[i] = theGraphicsDriver.InvalidImageID() 411 continue 412 } 413 imgs[i] = src.image.ID() 414 } 415 416 return theGraphicsDriver.DrawShader(c.dst.image.ID(), imgs, c.offsets, c.shader.shader.ID(), c.nindices, indexOffset, c.sourceRegion, c.mode, c.uniforms) 417 } 418 return theGraphicsDriver.Draw(c.dst.image.ID(), c.srcs[0].image.ID(), c.nindices, indexOffset, c.mode, c.color, c.filter, c.address, c.sourceRegion) 419 } 420 421 func (c *drawTrianglesCommand) NumVertices() int { 422 return c.nvertices 423 } 424 425 func (c *drawTrianglesCommand) NumIndices() int { 426 return c.nindices 427 } 428 429 func (c *drawTrianglesCommand) AddNumVertices(n int) { 430 c.nvertices += n 431 } 432 433 func (c *drawTrianglesCommand) AddNumIndices(n int) { 434 c.nindices += n 435 } 436 437 // CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged 438 // with the drawTrianglesCommand c. 439 func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { 440 // If a shader is used, commands are not merged. 441 // 442 // TODO: Merge shader commands considering uniform variables. 443 if c.shader != nil || shader != nil { 444 return false 445 } 446 if c.dst != dst { 447 return false 448 } 449 if c.srcs != srcs { 450 return false 451 } 452 if !c.color.Equals(color) { 453 return false 454 } 455 if c.mode != mode { 456 return false 457 } 458 if c.filter != filter { 459 return false 460 } 461 if c.address != address { 462 return false 463 } 464 if c.sourceRegion != sourceRegion { 465 return false 466 } 467 return true 468 } 469 470 // replacePixelsCommand represents a command to replace pixels of an image. 471 type replacePixelsCommand struct { 472 dst *Image 473 args []*driver.ReplacePixelsArgs 474 } 475 476 func (c *replacePixelsCommand) String() string { 477 return fmt.Sprintf("replace-pixels: dst: %d, len(args): %d", c.dst.id, len(c.args)) 478 } 479 480 // Exec executes the replacePixelsCommand. 481 func (c *replacePixelsCommand) Exec(indexOffset int) error { 482 c.dst.image.ReplacePixels(c.args) 483 return nil 484 } 485 486 func (c *replacePixelsCommand) NumVertices() int { 487 return 0 488 } 489 490 func (c *replacePixelsCommand) NumIndices() int { 491 return 0 492 } 493 494 func (c *replacePixelsCommand) AddNumVertices(n int) { 495 } 496 497 func (c *replacePixelsCommand) AddNumIndices(n int) { 498 } 499 500 func (c *replacePixelsCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { 501 return false 502 } 503 504 type pixelsCommand struct { 505 result []byte 506 img *Image 507 } 508 509 // Exec executes a pixelsCommand. 510 func (c *pixelsCommand) Exec(indexOffset int) error { 511 p, err := c.img.image.Pixels() 512 if err != nil { 513 return err 514 } 515 c.result = p 516 return nil 517 } 518 519 func (c *pixelsCommand) String() string { 520 return fmt.Sprintf("pixels: image: %d", c.img.id) 521 } 522 523 func (c *pixelsCommand) NumVertices() int { 524 return 0 525 } 526 527 func (c *pixelsCommand) NumIndices() int { 528 return 0 529 } 530 531 func (c *pixelsCommand) AddNumVertices(n int) { 532 } 533 534 func (c *pixelsCommand) AddNumIndices(n int) { 535 } 536 537 func (c *pixelsCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { 538 return false 539 } 540 541 // disposeImageCommand represents a command to dispose an image. 542 type disposeImageCommand struct { 543 target *Image 544 } 545 546 func (c *disposeImageCommand) String() string { 547 return fmt.Sprintf("dispose-image: target: %d", c.target.id) 548 } 549 550 // Exec executes the disposeImageCommand. 551 func (c *disposeImageCommand) Exec(indexOffset int) error { 552 c.target.image.Dispose() 553 return nil 554 } 555 556 func (c *disposeImageCommand) NumVertices() int { 557 return 0 558 } 559 560 func (c *disposeImageCommand) NumIndices() int { 561 return 0 562 } 563 564 func (c *disposeImageCommand) AddNumVertices(n int) { 565 } 566 567 func (c *disposeImageCommand) AddNumIndices(n int) { 568 } 569 570 func (c *disposeImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { 571 return false 572 } 573 574 // disposeShaderCommand represents a command to dispose a shader. 575 type disposeShaderCommand struct { 576 target *Shader 577 } 578 579 func (c *disposeShaderCommand) String() string { 580 return fmt.Sprintf("dispose-shader: target") 581 } 582 583 // Exec executes the disposeShaderCommand. 584 func (c *disposeShaderCommand) Exec(indexOffset int) error { 585 c.target.shader.Dispose() 586 return nil 587 } 588 589 func (c *disposeShaderCommand) NumVertices() int { 590 return 0 591 } 592 593 func (c *disposeShaderCommand) NumIndices() int { 594 return 0 595 } 596 597 func (c *disposeShaderCommand) AddNumVertices(n int) { 598 } 599 600 func (c *disposeShaderCommand) AddNumIndices(n int) { 601 } 602 603 func (c *disposeShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { 604 return false 605 } 606 607 // newImageCommand represents a command to create an empty image with given width and height. 608 type newImageCommand struct { 609 result *Image 610 width int 611 height int 612 } 613 614 func (c *newImageCommand) String() string { 615 return fmt.Sprintf("new-image: result: %d, width: %d, height: %d", c.result.id, c.width, c.height) 616 } 617 618 // Exec executes a newImageCommand. 619 func (c *newImageCommand) Exec(indexOffset int) error { 620 i, err := theGraphicsDriver.NewImage(c.width, c.height) 621 if err != nil { 622 return err 623 } 624 c.result.image = i 625 return nil 626 } 627 628 func (c *newImageCommand) NumVertices() int { 629 return 0 630 } 631 632 func (c *newImageCommand) NumIndices() int { 633 return 0 634 } 635 636 func (c *newImageCommand) AddNumVertices(n int) { 637 } 638 639 func (c *newImageCommand) AddNumIndices(n int) { 640 } 641 642 func (c *newImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { 643 return false 644 } 645 646 // newScreenFramebufferImageCommand is a command to create a special image for the screen. 647 type newScreenFramebufferImageCommand struct { 648 result *Image 649 width int 650 height int 651 } 652 653 func (c *newScreenFramebufferImageCommand) String() string { 654 return fmt.Sprintf("new-screen-framebuffer-image: result: %d, width: %d, height: %d", c.result.id, c.width, c.height) 655 } 656 657 // Exec executes a newScreenFramebufferImageCommand. 658 func (c *newScreenFramebufferImageCommand) Exec(indexOffset int) error { 659 var err error 660 c.result.image, err = theGraphicsDriver.NewScreenFramebufferImage(c.width, c.height) 661 return err 662 } 663 664 func (c *newScreenFramebufferImageCommand) NumVertices() int { 665 return 0 666 } 667 668 func (c *newScreenFramebufferImageCommand) NumIndices() int { 669 return 0 670 } 671 672 func (c *newScreenFramebufferImageCommand) AddNumVertices(n int) { 673 } 674 675 func (c *newScreenFramebufferImageCommand) AddNumIndices(n int) { 676 } 677 678 func (c *newScreenFramebufferImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { 679 return false 680 } 681 682 // newShaderCommand is a command to create a shader. 683 type newShaderCommand struct { 684 result *Shader 685 ir *shaderir.Program 686 } 687 688 func (c *newShaderCommand) String() string { 689 return fmt.Sprintf("new-shader-image") 690 } 691 692 // Exec executes a newShaderCommand. 693 func (c *newShaderCommand) Exec(indexOffset int) error { 694 var err error 695 c.result.shader, err = theGraphicsDriver.NewShader(c.ir) 696 return err 697 } 698 699 func (c *newShaderCommand) NumVertices() int { 700 return 0 701 } 702 703 func (c *newShaderCommand) NumIndices() int { 704 return 0 705 } 706 707 func (c *newShaderCommand) AddNumVertices(n int) { 708 } 709 710 func (c *newShaderCommand) AddNumIndices(n int) { 711 } 712 713 func (c *newShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { 714 return false 715 } 716 717 // ResetGraphicsDriverState resets or initializes the current graphics driver state. 718 func ResetGraphicsDriverState() error { 719 return theGraphicsDriver.Reset() 720 }