twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

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 }