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"
2023-07-30 10:36:50 +02:00
"sync/atomic"
2016-06-02 19:34:34 +02:00
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.
2023-01-28 11:06:38 +01:00
// A command is not immediately executed after created. Instead, it is queued after created,
2017-09-11 20:12:17 +02:00
// 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
2023-07-30 10:36:50 +02:00
NeedsSync ( ) bool
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-06-10 10:06:40 +02:00
indices [ ] uint16
2018-06-09 21:55:44 +02:00
2021-12-25 22:01:43 +01:00
tmpNumVertexFloats int
2018-07-11 19:11:18 +02:00
2021-10-29 16:41:47 +02:00
drawTrianglesCommandPool drawTrianglesCommandPool
2022-11-03 18:25:55 +01:00
2022-12-09 16:43:41 +01:00
uint32sBuffer uint32sBuffer
2023-07-30 10:36:50 +02:00
2023-07-31 17:11:35 +02:00
temporaryBytes temporaryBytes
2023-07-30 10:36:50 +02:00
err atomic . Value
2016-06-02 19:34:34 +02:00
}
2023-07-30 11:47:45 +02:00
// theCommandQueues is the set of command queues for the current process.
var (
theCommandQueues = [ ... ] * commandQueue {
{ } ,
{ } ,
}
commandQueueIndex int
)
func currentCommandQueue ( ) * commandQueue {
return theCommandQueues [ commandQueueIndex ]
}
func switchCommandQueue ( ) {
commandQueueIndex ++
commandQueueIndex = commandQueueIndex % len ( theCommandQueues )
}
2017-01-18 17:26:56 +01:00
2018-06-10 10:06:40 +02:00
func ( q * commandQueue ) appendIndices ( indices [ ] uint16 , offset uint16 ) {
2022-09-14 16:13:08 +02:00
n := len ( q . indices )
q . indices = append ( q . indices , indices ... )
2023-08-20 20:15:31 +02:00
for i := n ; i < len ( q . indices ) ; i ++ {
q . indices [ i ] += offset
2018-06-09 21:55:44 +02:00
}
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.
2023-03-23 05:28:00 +01:00
func mustUseDifferentVertexBuffer ( nextNumVertexFloats int ) bool {
2023-03-23 12:48:39 +01:00
return nextNumVertexFloats > graphics . MaxVertexFloatsCount
2021-12-25 22:01:43 +01:00
}
2019-04-22 16:12:03 +02:00
// EnqueueDrawTrianglesCommand enqueues a drawing-image command.
2023-08-25 01:01:32 +02:00
func ( q * commandQueue ) EnqueueDrawTrianglesCommand ( dst * Image , srcs [ graphics . ShaderImageCount ] * Image , vertices [ ] float32 , indices [ ] uint16 , blend graphicsdriver . Blend , dstRegion graphicsdriver . Region , srcRegions [ graphics . ShaderImageCount ] graphicsdriver . Region , shader * Shader , uniforms [ ] uint32 , evenOdd bool ) {
2023-03-23 12:48:39 +01:00
if len ( vertices ) > graphics . MaxVertexFloatsCount {
2023-03-23 13:22:30 +01:00
panic ( fmt . Sprintf ( "graphicscommand: len(vertices) must equal to or less than %d but was %d" , graphics . MaxVertexFloatsCount , len ( vertices ) ) )
2023-03-23 05:43:47 +01:00
}
2018-06-09 21:55:44 +02:00
split := false
2023-03-23 05:28:00 +01:00
if mustUseDifferentVertexBuffer ( q . tmpNumVertexFloats + len ( vertices ) ) {
2021-12-25 22:01:43 +01:00
q . tmpNumVertexFloats = 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.
2022-09-14 16:13:08 +02:00
q . vertices = append ( q . vertices , vertices ... )
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-09 21:55:44 +02:00
2023-03-14 05:06:11 +01:00
// prependPreservedUniforms not only prepends values to the given slice but also creates a new slice.
// Allocating a new slice is necessary to make EnqueueDrawTrianglesCommand safe so far.
// TODO: This might cause a performance issue (#2601).
2023-08-25 01:01:32 +02:00
uniforms = q . prependPreservedUniforms ( uniforms , shader , dst , srcs , dstRegion , srcRegions )
2020-06-29 17:02:33 +02:00
2022-11-03 10:48:59 +01:00
// Remove unused uniform variables so that more commands can be merged.
2022-11-03 17:23:17 +01:00
shader . ir . FilterUniformVariables ( uniforms )
2022-11-03 10:48:59 +01: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-11-04 08:17:40 +01:00
if last . CanMergeWithDrawTrianglesCommand ( dst , srcs , vertices , blend , shader , uniforms , evenOdd ) {
2021-07-05 12:35:34 +02:00
last . setVertices ( q . lastVertices ( len ( vertices ) + last . numVertices ( ) ) )
2022-11-04 08:17:40 +01:00
if last . dstRegions [ len ( last . dstRegions ) - 1 ] . Region == dstRegion {
last . dstRegions [ len ( last . dstRegions ) - 1 ] . IndexCount += len ( indices )
} else {
last . dstRegions = append ( last . dstRegions , graphicsdriver . DstRegion {
Region : dstRegion ,
IndexCount : len ( indices ) ,
} )
}
2021-07-05 14:16:01 +02:00
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 . vertices = q . lastVertices ( len ( vertices ) )
2022-10-15 11:58:56 +02:00
c . blend = blend
2022-11-04 08:17:40 +01:00
c . dstRegions = [ ] graphicsdriver . DstRegion {
{
Region : dstRegion ,
IndexCount : len ( indices ) ,
} ,
}
2021-10-29 16:41:47 +02:00
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 {
2022-09-14 16:13:08 +02:00
return q . vertices [ len ( q . vertices ) - n : len ( q . vertices ) ]
2021-07-05 12:35:34 +02:00
}
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.
2023-07-30 10:36:50 +02:00
func ( q * commandQueue ) Flush ( graphicsDriver graphicsdriver . Graphics , endFrame bool , swapBuffersForGL func ( ) ) error {
if err := q . err . Load ( ) ; err != nil {
return err . ( error )
}
var sync bool
for _ , c := range q . commands {
if c . NeedsSync ( ) {
sync = true
break
}
}
2023-08-25 17:50:45 +02:00
logger := debug . SwitchLogger ( )
2023-07-30 10:36:50 +02:00
var flushErr error
2022-12-30 05:51:32 +01:00
runOnRenderThread ( func ( ) {
2023-08-25 17:50:45 +02:00
defer logger . Flush ( )
if err := q . flush ( graphicsDriver , endFrame , logger ) ; err != nil {
2023-07-30 10:36:50 +02:00
if sync {
return
}
q . err . Store ( err )
2023-07-29 19:25:10 +02:00
return
}
if endFrame && swapBuffersForGL != nil {
swapBuffersForGL ( )
}
2023-07-30 10:36:50 +02:00
} , sync )
if sync && flushErr != nil {
return flushErr
}
return nil
2020-10-12 17:39:45 +02:00
}
// flush must be called the main thread.
2023-08-25 17:50:45 +02:00
func ( q * commandQueue ) flush ( graphicsDriver graphicsdriver . Graphics , endFrame bool , logger debug . Logger ) ( err error ) {
2022-10-23 18:51:37 +02:00
// If endFrame is true, Begin/End should be called to ensure the framebuffer is swapped.
if len ( q . commands ) == 0 && ! endFrame {
2020-04-22 17:51:25 +02:00
return nil
}
2018-06-10 10:06:40 +02:00
es := q . indices
2018-06-03 14:47:08 +02:00
vs := q . vertices
2023-08-25 17:50:45 +02:00
logger . 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-10-15 08:23:02 +02:00
defer func ( ) {
// Call End even if an error causes, or the graphics driver's state might be stale (#2388).
if err1 := graphicsDriver . End ( endFrame ) ; err1 != nil && err == nil {
err = err1
}
// 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 . vertices = q . vertices [ : 0 ]
q . indices = q . indices [ : 0 ]
q . tmpNumVertexFloats = 0
2023-07-30 11:37:18 +02:00
if endFrame {
q . uint32sBuffer . reset ( )
2023-07-31 17:11:35 +02:00
q . temporaryBytes . reset ( )
2023-07-30 11:37:18 +02:00
}
2022-10-15 08:23:02 +02:00
} ( )
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 {
2023-03-23 05:28:00 +01:00
if nc > 0 && mustUseDifferentVertexBuffer ( nv + dtc . numVertices ( ) ) {
2021-07-05 14:16:01 +02:00
break
}
nv += dtc . numVertices ( )
ne += dtc . numIndices ( )
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
}
2023-08-25 17:50:45 +02:00
logger . 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 13:57:59 +01:00
2020-01-18 17:18:56 +01:00
return nil
2016-06-02 19:34:34 +02:00
}
2022-10-14 20:36:25 +02:00
// FlushCommands flushes the command queue and present the screen if needed.
// If endFrame is true, the current screen might be used to present.
2023-07-29 19:25:10 +02:00
func FlushCommands ( graphicsDriver graphicsdriver . Graphics , endFrame bool , swapBuffersForGL func ( ) ) error {
2022-10-29 20:40:39 +02:00
flushImageBuffers ( )
2023-07-30 11:47:45 +02:00
if err := currentCommandQueue ( ) . Flush ( graphicsDriver , endFrame , swapBuffersForGL ) ; err != nil {
return err
}
switchCommandQueue ( )
return nil
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 {
2022-11-04 08:17:40 +01:00
dst * Image
srcs [ graphics . ShaderImageCount ] * Image
vertices [ ] float32
blend graphicsdriver . Blend
dstRegions [ ] graphicsdriver . DstRegion
shader * Shader
2022-12-03 08:11:26 +01:00
uniforms [ ] uint32
2022-11-04 08:17:40 +01:00
evenOdd bool
2016-06-02 19:34:34 +02:00
}
2019-04-22 16:12:03 +02:00
func ( c * drawTrianglesCommand ) String ( ) string {
2022-10-15 11:58:56 +02:00
// TODO: Improve readability
2023-04-17 18:19:28 +02:00
blend := fmt . Sprintf ( "{src-color: %d, src-alpha: %d, dst-color: %d, dst-alpha: %d, op-color: %d, op-alpha: %d}" ,
2022-10-16 17:49:56 +02:00
c . blend . BlendFactorSourceRGB ,
2022-10-15 11:58:56 +02:00
c . blend . BlendFactorSourceAlpha ,
2022-10-16 17:49:56 +02:00
c . blend . BlendFactorDestinationRGB ,
2022-10-15 11:58:56 +02:00
c . blend . BlendFactorDestinationAlpha ,
2022-10-16 17:49:56 +02:00
c . blend . BlendOperationRGB ,
2022-10-15 11:58:56 +02:00
c . blend . BlendOperationAlpha )
2019-07-19 16:18:07 +02:00
2020-05-17 20:45:58 +02:00
dst := fmt . Sprintf ( "%d" , c . dst . id )
if c . dst . screen {
dst += " (screen)"
}
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
}
2022-11-04 08:17:40 +01:00
return fmt . Sprintf ( "draw-triangles: dst: %s <- src: [%s], num of dst regions: %d, num of indices: %d, blend: %s, even-odd: %t" , dst , strings . Join ( srcstrs [ : ] , ", " ) , len ( c . dstRegions ) , c . numIndices ( ) , blend , 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?
2022-11-04 08:17:40 +01:00
if len ( c . dstRegions ) == 0 {
2016-06-03 05:41:18 +02:00
return nil
}
2018-11-05 19:44:43 +01:00
2022-07-12 18:46:02 +02:00
var imgs [ graphics . ShaderImageCount ] graphicsdriver . ImageID
2022-10-02 16:49:07 +02:00
for i , src := range c . srcs {
if src == nil {
imgs [ i ] = graphicsdriver . InvalidImageID
continue
2020-05-24 19:31:54 +02:00
}
2022-10-02 16:49:07 +02:00
imgs [ i ] = src . image . ID ( )
2018-11-05 19:44:43 +01:00
}
2021-07-01 08:41:25 +02:00
2022-11-04 08:17:40 +01:00
return graphicsDriver . DrawTriangles ( c . dst . image . ID ( ) , imgs , c . shader . shader . ID ( ) , c . dstRegions , indexOffset , c . blend , c . uniforms , c . evenOdd )
2016-06-02 19:34:34 +02:00
}
2023-07-30 10:36:50 +02:00
func ( c * drawTrianglesCommand ) NeedsSync ( ) bool {
return false
}
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 {
2022-11-04 08:17:40 +01:00
var nindices int
for _ , dstRegion := range c . dstRegions {
nindices += dstRegion . IndexCount
}
return 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
}
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-12-03 08:11:26 +01:00
func ( c * drawTrianglesCommand ) CanMergeWithDrawTrianglesCommand ( dst * Image , srcs [ graphics . ShaderImageCount ] * Image , vertices [ ] float32 , blend graphicsdriver . Blend , shader * Shader , uniforms [ ] uint32 , 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-10-02 15:11:06 +02:00
if len ( c . uniforms ) != len ( uniforms ) {
return false
}
for i := range c . uniforms {
2022-12-03 08:11:26 +01:00
if c . uniforms [ i ] != uniforms [ i ] {
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
}
2022-10-15 11:58:56 +02:00
if c . blend != blend {
2016-10-25 04:53:00 +02:00
return false
}
2022-11-12 14:26:28 +01:00
if c . evenOdd != evenOdd {
return false
}
if c . evenOdd && mightOverlapDstRegions ( c . vertices , vertices ) {
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
2023-08-16 16:18:37 +02:00
args [ ] graphicsdriver . PixelsArgs
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
2023-07-30 10:36:50 +02:00
func ( c * writePixelsCommand ) NeedsSync ( ) bool {
return false
}
2022-08-07 18:46:45 +02:00
type readPixelsCommand struct {
2023-08-16 16:18:37 +02:00
img * Image
args [ ] graphicsdriver . PixelsArgs
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 {
2023-08-16 16:18:37 +02:00
if err := c . img . image . ReadPixels ( c . args ) ; err != nil {
2018-07-11 19:40:06 +02:00
return err
}
return nil
}
2023-07-30 10:36:50 +02:00
func ( c * readPixelsCommand ) NeedsSync ( ) bool {
return true
}
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
2023-07-30 10:36:50 +02:00
func ( c * disposeImageCommand ) NeedsSync ( ) bool {
return false
}
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 {
2023-07-05 09:36:29 +02:00
return "dispose-shader: target"
2020-05-17 20:45:58 +02:00
}
// 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
}
2023-07-30 10:36:50 +02:00
func ( c * disposeShaderCommand ) NeedsSync ( ) bool {
return false
}
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
2023-07-30 10:36:50 +02:00
func ( c * newImageCommand ) NeedsSync ( ) bool {
return true
}
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 {
2023-07-05 09:36:29 +02:00
return "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
}
2023-07-30 10:36:50 +02:00
func ( c * newShaderCommand ) NeedsSync ( ) bool {
return true
}
2022-09-13 05:15:14 +02:00
type isInvalidatedCommand struct {
result bool
image * Image
}
func ( c * isInvalidatedCommand ) String ( ) string {
return fmt . Sprintf ( "is-invalidated: image: %d" , c . image . id )
}
func ( c * isInvalidatedCommand ) Exec ( graphicsDriver graphicsdriver . Graphics , indexOffset int ) error {
c . result = c . image . image . IsInvalidated ( )
return nil
}
2023-07-30 10:36:50 +02:00
func ( c * isInvalidatedCommand ) NeedsSync ( ) bool {
return true
}
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-12-30 05:51:32 +01:00
runOnRenderThread ( func ( ) {
2022-03-19 15:55:14 +01:00
err = graphicsDriver . Initialize ( )
2023-07-30 10:36:50 +02:00
} , true )
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 ) {
2023-03-22 10:35:46 +01:00
if r , ok := graphicsDriver . ( graphicsdriver . Resetter ) ; ok {
2022-12-30 05:51:32 +01:00
runOnRenderThread ( func ( ) {
2022-01-02 19:30:29 +01:00
err = r . Reset ( )
2023-07-30 10:36:50 +02:00
} , true )
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-12-30 05:51:32 +01:00
runOnRenderThread ( func ( ) {
2022-03-19 15:55:14 +01:00
size = graphicsDriver . MaxImageSize ( )
2023-07-30 10:36:50 +02:00
} , true )
2020-10-12 17:39:45 +02:00
return size
2020-10-12 18:36:40 +02:00
}
2022-10-30 10:31:06 +01:00
2022-11-03 18:25:55 +01:00
func max ( a , b int ) int {
if a < b {
return b
}
return a
}
func roundUpPower2 ( x int ) int {
p2 := 1
for p2 < x {
p2 *= 2
}
return p2
}
2023-08-25 01:01:32 +02:00
func ( q * commandQueue ) prependPreservedUniforms ( uniforms [ ] uint32 , shader * Shader , dst * Image , srcs [ graphics . ShaderImageCount ] * Image , dstRegion graphicsdriver . Region , srcRegions [ graphics . ShaderImageCount ] graphicsdriver . Region ) [ ] uint32 {
2022-11-04 13:51:33 +01:00
origUniforms := uniforms
2022-12-03 15:09:41 +01:00
uniforms = q . uint32sBuffer . alloc ( len ( origUniforms ) + graphics . PreservedUniformUint32Count )
copy ( uniforms [ graphics . PreservedUniformUint32Count : ] , origUniforms )
2022-10-30 10:31:06 +01:00
// Set the destination texture size.
dw , dh := dst . InternalSize ( )
2023-05-04 05:53:01 +02:00
uniforms [ 0 ] = math . Float32bits ( float32 ( dw ) )
uniforms [ 1 ] = math . Float32bits ( float32 ( dh ) )
2022-10-30 10:31:06 +01:00
// Set the source texture sizes.
2023-05-04 05:53:01 +02:00
if srcs [ 0 ] != nil {
w , h := srcs [ 0 ] . InternalSize ( )
uniforms [ 2 ] = math . Float32bits ( float32 ( w ) )
uniforms [ 3 ] = math . Float32bits ( float32 ( h ) )
2023-08-19 09:19:11 +02:00
} else {
uniforms [ 2 ] = 0
uniforms [ 3 ] = 0
2023-05-04 05:53:01 +02:00
}
if srcs [ 1 ] != nil {
w , h := srcs [ 1 ] . InternalSize ( )
uniforms [ 4 ] = math . Float32bits ( float32 ( w ) )
uniforms [ 5 ] = math . Float32bits ( float32 ( h ) )
2023-08-19 09:19:11 +02:00
} else {
uniforms [ 4 ] = 0
uniforms [ 5 ] = 0
2023-05-04 05:53:01 +02:00
}
if srcs [ 2 ] != nil {
w , h := srcs [ 2 ] . InternalSize ( )
uniforms [ 6 ] = math . Float32bits ( float32 ( w ) )
uniforms [ 7 ] = math . Float32bits ( float32 ( h ) )
2023-08-19 09:19:11 +02:00
} else {
uniforms [ 6 ] = 0
uniforms [ 7 ] = 0
2023-05-04 05:53:01 +02:00
}
if srcs [ 3 ] != nil {
w , h := srcs [ 3 ] . InternalSize ( )
uniforms [ 8 ] = math . Float32bits ( float32 ( w ) )
uniforms [ 9 ] = math . Float32bits ( float32 ( h ) )
2023-08-19 09:19:11 +02:00
} else {
uniforms [ 8 ] = 0
uniforms [ 9 ] = 0
2022-10-30 10:31:06 +01:00
}
2023-08-01 04:41:27 +02:00
if shader . unit ( ) == shaderir . Texels {
2023-04-16 11:56:14 +02:00
dstRegion . X /= float32 ( dw )
dstRegion . Y /= float32 ( dh )
dstRegion . Width /= float32 ( dw )
dstRegion . Height /= float32 ( dh )
}
2023-08-25 00:30:17 +02:00
// Set the destination region origin.
2023-05-04 05:53:01 +02:00
uniforms [ 10 ] = math . Float32bits ( dstRegion . X )
uniforms [ 11 ] = math . Float32bits ( dstRegion . Y )
2023-08-25 00:30:17 +02:00
// Set the destination region size.
2023-05-04 05:53:01 +02:00
uniforms [ 12 ] = math . Float32bits ( dstRegion . Width )
uniforms [ 13 ] = math . Float32bits ( dstRegion . Height )
2022-10-30 10:31:06 +01:00
2023-08-25 01:01:32 +02:00
if shader . unit ( ) == shaderir . Texels {
for i , src := range srcs {
if src == nil {
continue
}
w , h := src . InternalSize ( )
srcRegions [ i ] . X /= float32 ( w )
srcRegions [ i ] . Y /= float32 ( h )
srcRegions [ i ] . Width /= float32 ( w )
srcRegions [ i ] . Height /= float32 ( h )
}
2022-10-30 10:31:06 +01:00
}
2023-08-25 01:01:32 +02:00
// Set the source region origins.
uniforms [ 14 ] = math . Float32bits ( srcRegions [ 0 ] . X )
uniforms [ 15 ] = math . Float32bits ( srcRegions [ 0 ] . Y )
uniforms [ 16 ] = math . Float32bits ( srcRegions [ 1 ] . X )
uniforms [ 17 ] = math . Float32bits ( srcRegions [ 1 ] . Y )
uniforms [ 18 ] = math . Float32bits ( srcRegions [ 2 ] . X )
uniforms [ 19 ] = math . Float32bits ( srcRegions [ 2 ] . Y )
uniforms [ 20 ] = math . Float32bits ( srcRegions [ 3 ] . X )
uniforms [ 21 ] = math . Float32bits ( srcRegions [ 3 ] . Y )
2023-08-25 00:30:17 +02:00
// Set the source region sizes.
2023-08-25 01:01:32 +02:00
uniforms [ 22 ] = math . Float32bits ( srcRegions [ 0 ] . Width )
uniforms [ 23 ] = math . Float32bits ( srcRegions [ 0 ] . Height )
uniforms [ 24 ] = math . Float32bits ( srcRegions [ 1 ] . Width )
uniforms [ 25 ] = math . Float32bits ( srcRegions [ 1 ] . Height )
uniforms [ 26 ] = math . Float32bits ( srcRegions [ 2 ] . Width )
uniforms [ 27 ] = math . Float32bits ( srcRegions [ 2 ] . Height )
uniforms [ 28 ] = math . Float32bits ( srcRegions [ 3 ] . Width )
uniforms [ 29 ] = math . Float32bits ( srcRegions [ 3 ] . Height )
2023-08-25 00:30:17 +02:00
// Set the projection matrix.
uniforms [ 30 ] = math . Float32bits ( 2 / float32 ( dw ) )
2023-05-04 05:53:01 +02:00
uniforms [ 31 ] = 0
uniforms [ 32 ] = 0
uniforms [ 33 ] = 0
2023-08-25 00:30:17 +02:00
uniforms [ 34 ] = 0
uniforms [ 35 ] = math . Float32bits ( 2 / float32 ( dh ) )
uniforms [ 36 ] = 0
uniforms [ 37 ] = 0
2023-05-04 05:53:01 +02:00
uniforms [ 38 ] = 0
2023-08-25 00:30:17 +02:00
uniforms [ 39 ] = 0
uniforms [ 40 ] = math . Float32bits ( 1 )
uniforms [ 41 ] = 0
uniforms [ 42 ] = math . Float32bits ( - 1 )
uniforms [ 43 ] = math . Float32bits ( - 1 )
uniforms [ 44 ] = 0
uniforms [ 45 ] = math . Float32bits ( 1 )
2022-10-30 10:31:06 +01:00
return uniforms
}
2022-11-04 13:51:33 +01:00
2022-12-09 16:43:41 +01:00
// uint32sBuffer is a reusable buffer to allocate []uint32.
type uint32sBuffer struct {
2023-08-19 10:15:45 +02:00
buf [ ] uint32
2022-11-04 13:51:33 +01:00
}
2022-12-09 16:43:41 +01:00
func ( b * uint32sBuffer ) alloc ( n int ) [ ] uint32 {
2023-08-19 10:15:45 +02:00
buf := b . buf
2022-11-04 13:51:33 +01:00
if len ( buf ) + n > cap ( buf ) {
2022-12-09 16:43:41 +01:00
buf = make ( [ ] uint32 , 0 , max ( roundUpPower2 ( len ( buf ) + n ) , 16 ) )
2022-11-04 13:51:33 +01:00
}
s := buf [ len ( buf ) : len ( buf ) + n ]
2023-08-19 10:15:45 +02:00
b . buf = buf [ : len ( buf ) + n ]
2022-11-04 13:51:33 +01:00
return s
}
2022-12-09 16:43:41 +01:00
func ( b * uint32sBuffer ) reset ( ) {
2023-08-19 10:15:45 +02:00
b . buf = b . buf [ : 0 ]
2022-11-04 13:51:33 +01:00
}