2016-06-02 19:34:34 +02:00
// Copyright 2016 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2018-10-28 12:10:05 +01:00
package graphicscommand
2016-06-02 19:34:34 +02:00
import (
2016-06-03 05:41:18 +02:00
"fmt"
2019-08-27 15:33:14 +02:00
"math"
2020-07-17 18:09:58 +02:00
"strings"
2016-06-02 19:34:34 +02:00
2020-10-03 19:35:13 +02:00
"github.com/hajimehoshi/ebiten/v2/internal/affine"
2020-11-24 14:22:45 +01:00
"github.com/hajimehoshi/ebiten/v2/internal/debug"
2020-10-03 19:35:13 +02:00
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
2022-02-06 12:41:32 +01:00
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
2022-04-03 19:15:33 +02:00
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
2016-06-02 19:34:34 +02:00
)
2017-09-11 20:12:17 +02:00
// command represents a drawing command.
//
2019-04-22 16:12:03 +02:00
// A command for drawing that is created when Image functions are called like DrawTriangles,
2017-09-11 20:12:17 +02:00
// or Fill.
// A command is not immediately executed after created. Instaed, it is queued after created,
// and executed only when necessary.
2016-06-02 19:34:34 +02:00
type command interface {
2018-03-04 16:45:03 +01:00
fmt . Stringer
2022-03-19 16:09:17 +01:00
Exec ( graphicsDriver graphicsdriver . Graphics , indexOffset int ) error
2016-06-02 19:34:34 +02:00
}
2021-10-29 16:41:47 +02:00
type drawTrianglesCommandPool struct {
pool [ ] * drawTrianglesCommand
}
func ( p * drawTrianglesCommandPool ) get ( ) * drawTrianglesCommand {
if len ( p . pool ) == 0 {
return & drawTrianglesCommand { }
}
v := p . pool [ len ( p . pool ) - 1 ]
p . pool = p . pool [ : len ( p . pool ) - 1 ]
return v
}
func ( p * drawTrianglesCommandPool ) put ( v * drawTrianglesCommand ) {
if len ( p . pool ) >= 1024 {
return
}
p . pool = append ( p . pool , v )
}
2017-09-16 08:49:29 +02:00
// commandQueue is a command queue for drawing commands.
2016-06-02 19:34:34 +02:00
type commandQueue struct {
2017-09-18 18:37:24 +02:00
// commands is a queue of drawing commands.
commands [ ] command
// vertices represents a vertices data in OpenGL's array buffer.
vertices [ ] float32
2018-03-04 15:35:14 +01:00
// nvertices represents the current length of vertices.
// nvertices must <= len(vertices).
2017-09-18 18:37:24 +02:00
// vertices is never shrunk since re-extending a vertices buffer is heavy.
2018-06-10 10:10:11 +02:00
//
// TODO: This is a number of float32 values, not a number of vertices.
// Rename or fix the program.
2018-03-04 15:35:14 +01:00
nvertices int
2018-05-30 17:53:02 +02:00
2018-06-10 10:06:40 +02:00
indices [ ] uint16
nindices int
2018-06-09 21:55:44 +02:00
2021-12-25 22:01:43 +01:00
tmpNumVertexFloats int
tmpNumIndices int
2018-07-11 19:11:18 +02:00
2021-10-29 16:41:47 +02:00
drawTrianglesCommandPool drawTrianglesCommandPool
2016-06-02 19:34:34 +02:00
}
2017-09-16 08:49:29 +02:00
// theCommandQueue is the command queue for the current process.
2017-05-26 15:32:38 +02:00
var theCommandQueue = & commandQueue { }
2017-01-18 17:26:56 +01:00
2017-09-18 18:37:24 +02:00
// appendVertices appends vertices to the queue.
2020-05-24 09:43:08 +02:00
func ( q * commandQueue ) appendVertices ( vertices [ ] float32 , src * Image ) {
2018-03-04 15:35:14 +01:00
if len ( q . vertices ) < q . nvertices + len ( vertices ) {
n := q . nvertices + len ( vertices ) - len ( q . vertices )
2017-01-18 17:26:56 +01:00
q . vertices = append ( q . vertices , make ( [ ] float32 , n ) ... )
}
2018-06-16 18:09:05 +02:00
copy ( q . vertices [ q . nvertices : ] , vertices )
2018-03-04 15:35:14 +01:00
q . nvertices += len ( vertices )
2016-06-02 19:34:34 +02:00
}
2018-06-10 10:06:40 +02:00
func ( q * commandQueue ) appendIndices ( indices [ ] uint16 , offset uint16 ) {
if len ( q . indices ) < q . nindices + len ( indices ) {
n := q . nindices + len ( indices ) - len ( q . indices )
q . indices = append ( q . indices , make ( [ ] uint16 , n ) ... )
2018-06-03 11:48:14 +02:00
}
2018-06-09 21:55:44 +02:00
for i := range indices {
2018-06-10 10:06:40 +02:00
q . indices [ q . nindices + i ] = indices [ i ] + offset
2018-06-09 21:55:44 +02:00
}
2018-06-10 10:06:40 +02:00
q . nindices += len ( indices )
2018-06-03 11:48:14 +02:00
}
2022-08-03 15:40:39 +02:00
// mustUseDifferentVertexBuffer reports whether a different vertex buffer must be used.
2021-12-25 22:01:43 +01:00
func mustUseDifferentVertexBuffer ( nextNumVertexFloats , nextNumIndices int ) bool {
2022-07-12 18:46:02 +02:00
return nextNumVertexFloats > graphics . IndicesCount * graphics . VertexFloatCount || nextNumIndices > graphics . IndicesCount
2021-12-25 22:01:43 +01:00
}
2019-04-22 16:12:03 +02:00
// EnqueueDrawTrianglesCommand enqueues a drawing-image command.
2022-07-12 18:46:02 +02:00
func ( q * commandQueue ) EnqueueDrawTrianglesCommand ( dst * Image , srcs [ graphics . ShaderImageCount ] * Image , offsets [ graphics . ShaderImageCount - 1 ] [ 2 ] float32 , vertices [ ] float32 , indices [ ] uint16 , color affine . ColorM , mode graphicsdriver . CompositeMode , filter graphicsdriver . Filter , address graphicsdriver . Address , dstRegion , srcRegion graphicsdriver . Region , shader * Shader , uniforms [ ] [ ] float32 , evenOdd bool ) {
if len ( indices ) > graphics . IndicesCount {
panic ( fmt . Sprintf ( "graphicscommand: len(indices) must be <= graphics.IndicesCount but not at EnqueueDrawTrianglesCommand: len(indices): %d, graphics.IndicesCount: %d" , len ( indices ) , graphics . IndicesCount ) )
2018-06-03 11:48:14 +02:00
}
2018-06-09 21:55:44 +02:00
split := false
2021-12-25 22:01:43 +01:00
if mustUseDifferentVertexBuffer ( q . tmpNumVertexFloats + len ( vertices ) , q . tmpNumIndices + len ( indices ) ) {
q . tmpNumVertexFloats = 0
2018-06-10 10:06:40 +02:00
q . tmpNumIndices = 0
2018-06-09 21:55:44 +02:00
split = true
}
2020-07-17 18:09:58 +02:00
// Assume that all the image sizes are same.
2020-08-10 18:38:00 +02:00
// Assume that the images are packed from the front in the slice srcs.
q . appendVertices ( vertices , srcs [ 0 ] )
2022-07-12 18:46:02 +02:00
q . appendIndices ( indices , uint16 ( q . tmpNumVertexFloats / graphics . VertexFloatCount ) )
2021-12-25 22:01:43 +01:00
q . tmpNumVertexFloats += len ( vertices )
2018-06-10 10:06:40 +02:00
q . tmpNumIndices += len ( indices )
2018-06-09 21:55:44 +02:00
2020-08-10 18:38:00 +02:00
if srcs [ 0 ] != nil {
w , h := srcs [ 0 ] . InternalSize ( )
2020-11-07 11:14:06 +01:00
srcRegion . X /= float32 ( w )
srcRegion . Y /= float32 ( h )
srcRegion . Width /= float32 ( w )
srcRegion . Height /= float32 ( h )
2020-07-18 12:56:22 +02:00
for i := range offsets {
offsets [ i ] [ 0 ] /= float32 ( w )
offsets [ i ] [ 1 ] /= float32 ( h )
}
2020-06-29 17:02:33 +02:00
}
2020-09-04 17:42:47 +02:00
// TODO: If dst is the screen, reorder the command to be the last.
if ! split && 0 < len ( q . commands ) {
2021-07-05 14:16:01 +02:00
if last , ok := q . commands [ len ( q . commands ) - 1 ] . ( * drawTrianglesCommand ) ; ok {
2022-04-02 20:57:49 +02:00
if last . CanMergeWithDrawTrianglesCommand ( dst , srcs , vertices , color , mode , filter , address , dstRegion , srcRegion , shader , uniforms , evenOdd ) {
2021-07-05 12:35:34 +02:00
last . setVertices ( q . lastVertices ( len ( vertices ) + last . numVertices ( ) ) )
2021-07-05 14:16:01 +02:00
last . addNumIndices ( len ( indices ) )
return
}
2020-09-04 17:42:47 +02:00
}
}
2021-10-29 16:41:47 +02:00
c := q . drawTrianglesCommandPool . get ( )
c . dst = dst
c . srcs = srcs
c . offsets = offsets
c . vertices = q . lastVertices ( len ( vertices ) )
c . nindices = len ( indices )
c . color = color
c . mode = mode
c . filter = filter
c . address = address
c . dstRegion = dstRegion
c . srcRegion = srcRegion
c . shader = shader
c . uniforms = uniforms
c . evenOdd = evenOdd
2019-11-13 16:08:44 +01:00
q . commands = append ( q . commands , c )
2017-05-02 15:45:09 +02:00
}
2021-07-05 12:35:34 +02:00
func ( q * commandQueue ) lastVertices ( n int ) [ ] float32 {
return q . vertices [ q . nvertices - n : q . nvertices ]
}
2019-05-08 04:58:02 +02:00
// Enqueue enqueues a drawing command other than a draw-triangles command.
2017-09-19 18:35:56 +02:00
//
2019-05-08 04:58:02 +02:00
// For a draw-triangles command, use EnqueueDrawTrianglesCommand.
2017-05-02 15:45:09 +02:00
func ( q * commandQueue ) Enqueue ( command command ) {
2019-04-12 17:22:11 +02:00
// TODO: If dst is the screen, reorder the command to be the last.
2016-11-03 09:40:52 +01:00
q . commands = append ( q . commands , command )
2016-10-25 04:53:00 +02:00
}
2017-09-19 18:35:56 +02:00
// Flush flushes the command queue.
2022-03-19 15:03:34 +01:00
func ( q * commandQueue ) Flush ( graphicsDriver graphicsdriver . Graphics ) ( err error ) {
2022-02-13 17:49:39 +01:00
runOnRenderingThread ( func ( ) {
2022-03-19 15:03:34 +01:00
err = q . flush ( graphicsDriver )
2020-10-12 17:39:45 +02:00
} )
2022-01-02 19:30:29 +01:00
return
2020-10-12 17:39:45 +02:00
}
// flush must be called the main thread.
2022-03-19 15:03:34 +01:00
func ( q * commandQueue ) flush ( graphicsDriver graphicsdriver . Graphics ) error {
2020-04-22 17:51:25 +02:00
if len ( q . commands ) == 0 {
return nil
}
2018-06-10 10:06:40 +02:00
es := q . indices
2018-06-03 14:47:08 +02:00
vs := q . vertices
2021-08-04 18:15:41 +02:00
debug . Logf ( "Graphics commands:\n" )
2019-04-20 08:17:59 +02:00
2022-03-21 14:23:07 +01:00
if err := graphicsDriver . Begin ( ) ; err != nil {
return err
}
2022-02-21 17:37:13 +01:00
var present bool
2019-09-28 20:23:40 +02:00
cs := q . commands
for len ( cs ) > 0 {
2018-06-03 14:47:08 +02:00
nv := 0
ne := 0
nc := 0
2019-09-28 20:23:40 +02:00
for _ , c := range cs {
2021-07-05 14:16:01 +02:00
if dtc , ok := c . ( * drawTrianglesCommand ) ; ok {
2022-07-12 18:46:02 +02:00
if dtc . numIndices ( ) > graphics . IndicesCount {
panic ( fmt . Sprintf ( "graphicscommand: dtc.NumIndices() must be <= graphics.IndicesCount but not at Flush: dtc.NumIndices(): %d, graphics.IndicesCount: %d" , dtc . numIndices ( ) , graphics . IndicesCount ) )
2021-07-05 14:16:01 +02:00
}
2021-12-25 22:01:43 +01:00
if nc > 0 && mustUseDifferentVertexBuffer ( nv + dtc . numVertices ( ) , ne + dtc . numIndices ( ) ) {
2021-07-05 14:16:01 +02:00
break
}
nv += dtc . numVertices ( )
ne += dtc . numIndices ( )
2022-02-21 17:37:13 +01:00
if dtc . dst . screen {
present = true
}
2018-06-03 14:47:08 +02:00
}
nc ++
2016-06-03 05:41:18 +02:00
}
2018-06-03 14:47:08 +02:00
if 0 < ne {
2022-03-21 14:23:07 +01:00
if err := graphicsDriver . SetVertices ( vs [ : nv ] , es [ : ne ] ) ; err != nil {
return err
}
2018-06-03 14:47:08 +02:00
es = es [ ne : ]
vs = vs [ nv : ]
2016-07-16 12:17:57 +02:00
}
2018-11-11 15:51:16 +01:00
indexOffset := 0
2019-09-28 20:23:40 +02:00
for _ , c := range cs [ : nc ] {
2022-03-19 16:09:17 +01:00
if err := c . Exec ( graphicsDriver , indexOffset ) ; err != nil {
2020-01-18 17:18:56 +01:00
return err
2016-07-16 12:17:57 +02:00
}
2020-11-24 14:22:45 +01:00
debug . Logf ( " %s\n" , c )
2018-11-11 15:51:16 +01:00
// TODO: indexOffset should be reset if the command type is different
2018-05-27 17:49:16 +02:00
// from the previous one. This fix is needed when another drawing command is
2019-04-22 16:12:03 +02:00
// introduced than drawTrianglesCommand.
2021-07-05 14:16:01 +02:00
if dtc , ok := c . ( * drawTrianglesCommand ) ; ok {
indexOffset += dtc . numIndices ( )
}
2016-07-16 12:17:57 +02:00
}
2019-09-28 20:23:40 +02:00
cs = cs [ nc : ]
2016-06-02 19:34:34 +02:00
}
2022-03-21 14:23:07 +01:00
if err := graphicsDriver . End ( present ) ; err != nil {
return err
}
2022-03-21 13:57:59 +01:00
// Release the commands explicitly (#1803).
// Apparently, the part of a slice between len and cap-1 still holds references.
// Then, resetting the length by [:0] doesn't release the references.
for i , c := range q . commands {
if c , ok := c . ( * drawTrianglesCommand ) ; ok {
q . drawTrianglesCommandPool . put ( c )
}
q . commands [ i ] = nil
}
q . commands = q . commands [ : 0 ]
q . nvertices = 0
q . nindices = 0
q . tmpNumVertexFloats = 0
q . tmpNumIndices = 0
2020-01-18 17:18:56 +01:00
return nil
2016-06-02 19:34:34 +02:00
}
2017-09-19 18:35:56 +02:00
// FlushCommands flushes the command queue.
2022-03-19 15:55:14 +01:00
func FlushCommands ( graphicsDriver graphicsdriver . Graphics ) error {
return theCommandQueue . Flush ( graphicsDriver )
2016-06-10 22:48:09 +02:00
}
2019-04-22 16:12:03 +02:00
// drawTrianglesCommand represents a drawing command to draw an image on another image.
type drawTrianglesCommand struct {
2020-11-07 11:14:06 +01:00
dst * Image
2022-07-12 18:46:02 +02:00
srcs [ graphics . ShaderImageCount ] * Image
offsets [ graphics . ShaderImageCount - 1 ] [ 2 ] float32
2021-07-05 12:35:34 +02:00
vertices [ ] float32
2020-11-07 11:14:06 +01:00
nindices int
2021-07-27 03:37:14 +02:00
color affine . ColorM
2022-02-06 12:41:32 +01:00
mode graphicsdriver . CompositeMode
filter graphicsdriver . Filter
address graphicsdriver . Address
dstRegion graphicsdriver . Region
srcRegion graphicsdriver . Region
2020-11-07 11:14:06 +01:00
shader * Shader
2022-04-03 19:15:33 +02:00
uniforms [ ] [ ] float32
2021-07-02 12:26:09 +02:00
evenOdd bool
2016-06-02 19:34:34 +02:00
}
2019-04-22 16:12:03 +02:00
func ( c * drawTrianglesCommand ) String ( ) string {
2019-07-19 16:18:07 +02:00
mode := ""
switch c . mode {
2022-02-06 12:41:32 +01:00
case graphicsdriver . CompositeModeSourceOver :
2019-07-19 16:18:07 +02:00
mode = "source-over"
2022-02-06 12:41:32 +01:00
case graphicsdriver . CompositeModeClear :
2019-07-19 16:18:07 +02:00
mode = "clear"
2022-02-06 12:41:32 +01:00
case graphicsdriver . CompositeModeCopy :
2019-07-19 16:18:07 +02:00
mode = "copy"
2022-02-06 12:41:32 +01:00
case graphicsdriver . CompositeModeDestination :
2019-07-19 16:18:07 +02:00
mode = "destination"
2022-02-06 12:41:32 +01:00
case graphicsdriver . CompositeModeDestinationOver :
2019-07-19 16:18:07 +02:00
mode = "destination-over"
2022-02-06 12:41:32 +01:00
case graphicsdriver . CompositeModeSourceIn :
2019-07-19 16:18:07 +02:00
mode = "source-in"
2022-02-06 12:41:32 +01:00
case graphicsdriver . CompositeModeDestinationIn :
2019-07-19 16:18:07 +02:00
mode = "destination-in"
2022-02-06 12:41:32 +01:00
case graphicsdriver . CompositeModeSourceOut :
2019-07-19 16:18:07 +02:00
mode = "source-out"
2022-02-06 12:41:32 +01:00
case graphicsdriver . CompositeModeDestinationOut :
2019-07-19 16:18:07 +02:00
mode = "destination-out"
2022-02-06 12:41:32 +01:00
case graphicsdriver . CompositeModeSourceAtop :
2019-07-19 16:18:07 +02:00
mode = "source-atop"
2022-02-06 12:41:32 +01:00
case graphicsdriver . CompositeModeDestinationAtop :
2019-07-19 16:18:07 +02:00
mode = "destination-atop"
2022-02-06 12:41:32 +01:00
case graphicsdriver . CompositeModeXor :
2019-07-19 16:18:07 +02:00
mode = "xor"
2022-02-06 12:41:32 +01:00
case graphicsdriver . CompositeModeLighter :
2019-07-19 16:18:07 +02:00
mode = "lighter"
2022-02-06 12:41:32 +01:00
case graphicsdriver . CompositeModeMultiply :
2021-03-14 18:14:03 +01:00
mode = "multiply"
2019-07-19 16:18:07 +02:00
default :
panic ( fmt . Sprintf ( "graphicscommand: invalid composite mode: %d" , c . mode ) )
}
2020-05-17 20:45:58 +02:00
dst := fmt . Sprintf ( "%d" , c . dst . id )
if c . dst . screen {
dst += " (screen)"
}
2022-07-06 12:29:19 +02:00
shader := "default shader"
2020-05-17 20:45:58 +02:00
if c . shader != nil {
2022-07-06 12:29:19 +02:00
shader = "custom shader"
2020-05-17 20:45:58 +02:00
}
2019-07-19 16:18:07 +02:00
filter := ""
switch c . filter {
2022-02-06 12:41:32 +01:00
case graphicsdriver . FilterNearest :
2019-07-19 16:18:07 +02:00
filter = "nearest"
2022-02-06 12:41:32 +01:00
case graphicsdriver . FilterLinear :
2019-07-19 16:18:07 +02:00
filter = "linear"
2022-08-29 18:47:23 +02:00
case graphicsdriver . FilterScreen :
filter = "screen"
2019-07-19 16:18:07 +02:00
default :
panic ( fmt . Sprintf ( "graphicscommand: invalid filter: %d" , c . filter ) )
}
address := ""
switch c . address {
2022-02-06 12:41:32 +01:00
case graphicsdriver . AddressClampToZero :
2019-07-19 16:18:07 +02:00
address = "clamp_to_zero"
2022-02-06 12:41:32 +01:00
case graphicsdriver . AddressRepeat :
2019-07-19 16:18:07 +02:00
address = "repeat"
2022-02-06 12:41:32 +01:00
case graphicsdriver . AddressUnsafe :
2020-06-24 17:47:18 +02:00
address = "unsafe"
2019-07-19 16:18:07 +02:00
default :
panic ( fmt . Sprintf ( "graphicscommand: invalid address: %d" , c . address ) )
}
2022-07-12 18:46:02 +02:00
var srcstrs [ graphics . ShaderImageCount ] string
2020-07-17 18:09:58 +02:00
for i , src := range c . srcs {
if src == nil {
srcstrs [ i ] = "(nil)"
continue
}
srcstrs [ i ] = fmt . Sprintf ( "%d" , src . id )
if src . screen {
srcstrs [ i ] += " (screen)"
}
2019-07-20 07:29:04 +02:00
}
2020-11-26 17:48:17 +01:00
r := fmt . Sprintf ( "(x:%d, y:%d, width:%d, height:%d)" ,
int ( c . dstRegion . X ) , int ( c . dstRegion . Y ) , int ( c . dstRegion . Width ) , int ( c . dstRegion . Height ) )
2022-07-06 12:29:19 +02:00
return fmt . Sprintf ( "draw-triangles: dst: %s <- src: [%s], %s, dst region: %s, num of indices: %d, colorm: %s, mode: %s, filter: %s, address: %s, even-odd: %t" , dst , strings . Join ( srcstrs [ : ] , ", " ) , shader , r , c . nindices , c . color , mode , filter , address , c . evenOdd )
2018-03-04 16:45:03 +01:00
}
2019-04-22 16:12:03 +02:00
// Exec executes the drawTrianglesCommand.
2022-03-19 16:09:17 +01:00
func ( c * drawTrianglesCommand ) Exec ( graphicsDriver graphicsdriver . Graphics , indexOffset int ) error {
2018-11-05 19:44:43 +01:00
// TODO: Is it ok not to bind any framebuffer here?
2018-06-10 10:06:40 +02:00
if c . nindices == 0 {
2016-06-03 05:41:18 +02:00
return nil
}
2018-11-05 19:44:43 +01:00
2022-02-06 12:41:32 +01:00
var shaderID graphicsdriver . ShaderID = graphicsdriver . InvalidShaderID
2022-07-12 18:46:02 +02:00
var imgs [ graphics . ShaderImageCount ] graphicsdriver . ImageID
2020-05-17 20:45:58 +02:00
if c . shader != nil {
2021-07-01 08:41:25 +02:00
shaderID = c . shader . shader . ID ( )
2020-07-17 18:09:58 +02:00
for i , src := range c . srcs {
if src == nil {
2022-02-06 12:41:32 +01:00
imgs [ i ] = graphicsdriver . InvalidImageID
2020-07-17 18:09:58 +02:00
continue
}
imgs [ i ] = src . image . ID ( )
2020-05-24 19:31:54 +02:00
}
2021-07-01 08:41:25 +02:00
} else {
imgs [ 0 ] = c . srcs [ 0 ] . image . ID ( )
2018-11-05 19:44:43 +01:00
}
2021-07-01 08:41:25 +02:00
2022-04-03 19:15:33 +02:00
return graphicsDriver . DrawTriangles ( c . dst . image . ID ( ) , imgs , c . offsets , shaderID , c . nindices , indexOffset , c . mode , c . color , c . filter , c . address , c . dstRegion , c . srcRegion , c . uniforms , c . evenOdd )
2016-06-02 19:34:34 +02:00
}
2021-07-05 14:16:01 +02:00
func ( c * drawTrianglesCommand ) numVertices ( ) int {
2021-07-05 12:35:34 +02:00
return len ( c . vertices )
2018-03-04 15:35:14 +01:00
}
2021-07-05 14:16:01 +02:00
func ( c * drawTrianglesCommand ) numIndices ( ) int {
2018-06-10 10:06:40 +02:00
return c . nindices
2018-06-03 08:16:30 +02:00
}
2021-07-05 12:35:34 +02:00
func ( c * drawTrianglesCommand ) setVertices ( vertices [ ] float32 ) {
c . vertices = vertices
2018-03-18 11:58:32 +01:00
}
2021-07-05 14:16:01 +02:00
func ( c * drawTrianglesCommand ) addNumIndices ( n int ) {
2018-06-10 10:06:40 +02:00
c . nindices += n
2018-06-03 08:16:30 +02:00
}
2019-11-13 16:08:44 +01:00
// CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged
2019-04-22 16:12:03 +02:00
// with the drawTrianglesCommand c.
2022-07-12 18:46:02 +02:00
func ( c * drawTrianglesCommand ) CanMergeWithDrawTrianglesCommand ( dst * Image , srcs [ graphics . ShaderImageCount ] * Image , vertices [ ] float32 , color affine . ColorM , mode graphicsdriver . CompositeMode , filter graphicsdriver . Filter , address graphicsdriver . Address , dstRegion , srcRegion graphicsdriver . Region , shader * Shader , uniforms [ ] [ ] float32 , evenOdd bool ) bool {
2022-04-02 20:57:49 +02:00
if c . shader != shader {
2020-05-17 20:45:58 +02:00
return false
}
2022-04-02 20:57:49 +02:00
if c . shader != nil {
if len ( c . uniforms ) != len ( uniforms ) {
return false
}
2022-04-03 19:15:33 +02:00
for i := range c . uniforms {
if len ( c . uniforms [ i ] ) != len ( uniforms [ i ] ) {
2022-04-02 20:57:49 +02:00
return false
}
2022-04-03 19:15:33 +02:00
for j := range c . uniforms [ i ] {
if c . uniforms [ i ] [ j ] != uniforms [ i ] [ j ] {
2022-04-02 20:57:49 +02:00
return false
}
}
}
}
2017-05-02 15:45:09 +02:00
if c . dst != dst {
2016-10-25 04:53:00 +02:00
return false
}
2020-07-17 18:09:58 +02:00
if c . srcs != srcs {
2016-10-25 04:53:00 +02:00
return false
}
2018-08-05 14:30:06 +02:00
if ! c . color . Equals ( color ) {
return false
}
2017-05-02 15:45:09 +02:00
if c . mode != mode {
2016-10-25 04:53:00 +02:00
return false
}
2018-02-13 18:02:48 +01:00
if c . filter != filter {
return false
}
2018-12-23 19:00:00 +01:00
if c . address != address {
return false
}
2020-11-07 11:14:06 +01:00
if c . dstRegion != dstRegion {
return false
}
if c . srcRegion != srcRegion {
2020-06-29 17:02:33 +02:00
return false
}
2021-07-05 11:08:52 +02:00
if c . evenOdd || evenOdd {
2021-07-05 12:35:34 +02:00
if c . evenOdd && evenOdd {
2021-07-05 16:32:19 +02:00
return ! mightOverlapDstRegions ( c . vertices , vertices )
2021-07-05 12:35:34 +02:00
}
2021-07-02 12:26:09 +02:00
return false
}
2016-10-25 04:53:00 +02:00
return true
}
2021-07-05 12:35:34 +02:00
var (
posInf32 = float32 ( math . Inf ( 1 ) )
negInf32 = float32 ( math . Inf ( - 1 ) )
)
func dstRegionFromVertices ( vertices [ ] float32 ) ( minX , minY , maxX , maxY float32 ) {
minX = posInf32
minY = posInf32
maxX = negInf32
maxY = negInf32
2022-07-12 18:46:02 +02:00
for i := 0 ; i < len ( vertices ) / graphics . VertexFloatCount ; i ++ {
x := vertices [ graphics . VertexFloatCount * i ]
y := vertices [ graphics . VertexFloatCount * i + 1 ]
2021-07-05 12:35:34 +02:00
if x < minX {
minX = x
}
if y < minY {
minY = y
}
if maxX < x {
maxX = x
}
if maxY < y {
maxY = y
}
}
return
}
2021-07-05 16:32:19 +02:00
func mightOverlapDstRegions ( vertices1 , vertices2 [ ] float32 ) bool {
2021-07-05 12:35:34 +02:00
minX1 , minY1 , maxX1 , maxY1 := dstRegionFromVertices ( vertices1 )
minX2 , minY2 , maxX2 , maxY2 := dstRegionFromVertices ( vertices2 )
const mergin = 1
return minX1 < maxX2 + mergin && minX2 < maxX1 + mergin && minY1 < maxY2 + mergin && minY2 < maxY1 + mergin
}
2022-08-07 20:02:12 +02:00
// writePixelsCommand represents a command to replace pixels of an image.
type writePixelsCommand struct {
2019-11-21 15:42:46 +01:00
dst * Image
2022-08-07 20:02:12 +02:00
args [ ] * graphicsdriver . WritePixelsArgs
2016-06-02 19:34:34 +02:00
}
2022-08-07 20:02:12 +02:00
func ( c * writePixelsCommand ) String ( ) string {
return fmt . Sprintf ( "write-pixels: dst: %d, len(args): %d" , c . dst . id , len ( c . args ) )
2018-03-04 16:45:03 +01:00
}
2022-08-07 20:02:12 +02:00
// Exec executes the writePixelsCommand.
func ( c * writePixelsCommand ) Exec ( graphicsDriver graphicsdriver . Graphics , indexOffset int ) error {
2022-07-05 06:26:45 +02:00
if len ( c . args ) == 0 {
return nil
2022-03-20 17:03:00 +01:00
}
2022-08-07 20:02:12 +02:00
if err := c . dst . image . WritePixels ( c . args ) ; err != nil {
2022-07-05 06:26:45 +02:00
return err
2022-03-20 17:03:00 +01:00
}
2016-06-02 19:34:34 +02:00
return nil
}
2016-06-11 15:52:07 +02:00
2022-08-07 18:46:45 +02:00
type readPixelsCommand struct {
2018-07-11 19:40:06 +02:00
result [ ] byte
img * Image
2022-08-27 14:22:31 +02:00
x int
y int
width int
height int
2018-07-11 19:40:06 +02:00
}
2022-08-07 18:46:45 +02:00
// Exec executes a readPixelsCommand.
func ( c * readPixelsCommand ) Exec ( graphicsDriver graphicsdriver . Graphics , indexOffset int ) error {
2022-08-27 14:22:31 +02:00
if err := c . img . image . ReadPixels ( c . result , c . x , c . y , c . width , c . height ) ; err != nil {
2018-07-11 19:40:06 +02:00
return err
}
return nil
}
2022-08-07 18:46:45 +02:00
func ( c * readPixelsCommand ) String ( ) string {
return fmt . Sprintf ( "read-pixels: image: %d" , c . img . id )
2018-03-04 16:45:03 +01:00
}
2020-05-17 20:45:58 +02:00
// disposeImageCommand represents a command to dispose an image.
type disposeImageCommand struct {
2016-06-12 15:44:23 +02:00
target * Image
2016-06-11 15:52:07 +02:00
}
2020-05-17 20:45:58 +02:00
func ( c * disposeImageCommand ) String ( ) string {
return fmt . Sprintf ( "dispose-image: target: %d" , c . target . id )
2018-03-04 16:45:03 +01:00
}
2020-05-17 20:45:58 +02:00
// Exec executes the disposeImageCommand.
2022-03-19 16:09:17 +01:00
func ( c * disposeImageCommand ) Exec ( graphicsDriver graphicsdriver . Graphics , indexOffset int ) error {
2018-11-12 15:44:13 +01:00
c . target . image . Dispose ( )
2016-06-11 15:52:07 +02:00
return nil
}
2016-06-11 17:23:26 +02:00
2020-05-17 20:45:58 +02:00
// disposeShaderCommand represents a command to dispose a shader.
type disposeShaderCommand struct {
target * Shader
}
func ( c * disposeShaderCommand ) String ( ) string {
return fmt . Sprintf ( "dispose-shader: target" )
}
// Exec executes the disposeShaderCommand.
2022-03-19 16:09:17 +01:00
func ( c * disposeShaderCommand ) Exec ( graphicsDriver graphicsdriver . Graphics , indexOffset int ) error {
2020-05-17 20:45:58 +02:00
c . target . shader . Dispose ( )
return nil
}
2017-09-19 18:35:56 +02:00
// newImageCommand represents a command to create an empty image with given width and height.
2016-06-11 17:23:26 +02:00
type newImageCommand struct {
2016-06-12 15:44:23 +02:00
result * Image
width int
height int
2022-06-06 02:21:11 +02:00
screen bool
2016-06-11 17:23:26 +02:00
}
2018-03-04 16:45:03 +01:00
func ( c * newImageCommand ) String ( ) string {
2022-06-06 04:13:04 +02:00
return fmt . Sprintf ( "new-image: result: %d, width: %d, height: %d, screen: %t" , c . result . id , c . width , c . height , c . screen )
2018-03-04 16:45:03 +01:00
}
2017-09-19 18:35:56 +02:00
// Exec executes a newImageCommand.
2022-03-19 16:09:17 +01:00
func ( c * newImageCommand ) Exec ( graphicsDriver graphicsdriver . Graphics , indexOffset int ) error {
2018-11-11 15:57:23 +01:00
var err error
2022-06-06 02:21:11 +02:00
if c . screen {
c . result . image , err = graphicsDriver . NewScreenFramebufferImage ( c . width , c . height )
} else {
c . result . image , err = graphicsDriver . NewImage ( c . width , c . height )
}
2018-11-11 15:57:23 +01:00
return err
2016-06-17 21:46:33 +02:00
}
2018-03-04 15:35:14 +01:00
2020-05-17 20:45:58 +02:00
// newShaderCommand is a command to create a shader.
type newShaderCommand struct {
result * Shader
2022-04-03 19:15:33 +02:00
ir * shaderir . Program
2020-05-17 20:45:58 +02:00
}
func ( c * newShaderCommand ) String ( ) string {
2021-06-27 16:20:27 +02:00
return fmt . Sprintf ( "new-shader" )
2020-05-17 20:45:58 +02:00
}
// Exec executes a newShaderCommand.
2022-03-19 16:09:17 +01:00
func ( c * newShaderCommand ) Exec ( graphicsDriver graphicsdriver . Graphics , indexOffset int ) error {
2022-04-03 19:15:33 +02:00
s , err := graphicsDriver . NewShader ( c . ir )
2022-03-21 09:48:47 +01:00
if err != nil {
return err
}
c . result . shader = s
return nil
2020-05-17 20:45:58 +02:00
}
2021-07-07 06:58:42 +02:00
// InitializeGraphicsDriverState initialize the current graphics driver state.
2022-03-19 15:55:14 +01:00
func InitializeGraphicsDriverState ( graphicsDriver graphicsdriver . Graphics ) ( err error ) {
2022-02-13 17:49:39 +01:00
runOnRenderingThread ( func ( ) {
2022-03-19 15:55:14 +01:00
err = graphicsDriver . Initialize ( )
2021-07-07 06:58:42 +02:00
} )
2022-01-02 19:30:29 +01:00
return
2021-07-07 06:58:42 +02:00
}
// ResetGraphicsDriverState resets the current graphics driver state.
2021-07-09 13:20:45 +02:00
// If the graphics driver doesn't have an API to reset, ResetGraphicsDriverState does nothing.
2022-03-19 15:55:14 +01:00
func ResetGraphicsDriverState ( graphicsDriver graphicsdriver . Graphics ) ( err error ) {
if r , ok := graphicsDriver . ( interface { Reset ( ) error } ) ; ok {
2022-02-13 17:49:39 +01:00
runOnRenderingThread ( func ( ) {
2022-01-02 19:30:29 +01:00
err = r . Reset ( )
2021-07-09 13:20:45 +02:00
} )
}
return nil
2018-10-29 17:27:31 +01:00
}
2020-10-12 18:36:40 +02:00
// MaxImageSize returns the maximum size of an image.
2022-03-19 15:55:14 +01:00
func MaxImageSize ( graphicsDriver graphicsdriver . Graphics ) int {
2020-10-12 17:39:45 +02:00
var size int
2022-02-13 17:49:39 +01:00
runOnRenderingThread ( func ( ) {
2022-03-19 15:55:14 +01:00
size = graphicsDriver . MaxImageSize ( )
2020-10-12 17:39:45 +02:00
} )
return size
2020-10-12 18:36:40 +02:00
}