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-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-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
tmpNumIndices 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-11-12 11:02:38 +01:00
uint32sBuffer buffer [ uint32 ]
uint32SlicesBuffer buffer [ [ ] uint32 ]
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
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 ... )
2018-06-09 21:55:44 +02:00
for i := range indices {
2022-09-14 16:13:08 +02:00
q . indices [ n + 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.
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-11-12 11:02:38 +01:00
func ( q * commandQueue ) EnqueueDrawTrianglesCommand ( dst * Image , srcs [ graphics . ShaderImageCount ] * Image , offsets [ graphics . ShaderImageCount - 1 ] [ 2 ] float32 , vertices [ ] float32 , indices [ ] uint16 , blend graphicsdriver . Blend , dstRegion , srcRegion graphicsdriver . Region , shader * Shader , uniforms [ ] [ ] uint32 , evenOdd bool ) {
2022-07-12 18:46:02 +02:00
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.
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-10 10:06:40 +02:00
q . tmpNumIndices += len ( indices )
2018-06-09 21:55:44 +02:00
2022-11-03 18:25:55 +01:00
uniforms = q . prependPreservedUniforms ( uniforms , dst , srcs , offsets , dstRegion , srcRegion )
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.
2022-10-14 20:36:25 +02:00
func ( q * commandQueue ) Flush ( graphicsDriver graphicsdriver . Graphics , endFrame bool ) ( err error ) {
2022-02-13 17:49:39 +01:00
runOnRenderingThread ( func ( ) {
2022-10-14 20:36:25 +02:00
err = q . flush ( graphicsDriver , endFrame )
2020-10-12 17:39:45 +02:00
} )
2022-11-03 19:55:05 +01:00
if endFrame {
2022-11-12 11:02:38 +01:00
q . uint32sBuffer . reset ( )
q . uint32SlicesBuffer . reset ( )
2022-11-03 19:55:05 +01: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-10-15 08:23:02 +02:00
func ( q * commandQueue ) flush ( graphicsDriver graphicsdriver . Graphics , endFrame bool ) ( 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
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-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
q . tmpNumIndices = 0
} ( )
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 ( )
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 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.
func FlushCommands ( graphicsDriver graphicsdriver . Graphics , endFrame bool ) error {
2022-10-29 20:40:39 +02:00
flushImageBuffers ( )
2022-10-14 20:36:25 +02:00
return theCommandQueue . Flush ( graphicsDriver , endFrame )
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-11-12 11:02:38 +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
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
}
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-11-12 11:02:38 +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 {
if len ( c . uniforms [ i ] ) != len ( uniforms [ i ] ) {
2022-04-02 20:57:49 +02:00
return false
}
2022-10-02 15:11:06 +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
}
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
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
}
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
}
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
}
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
}
2022-11-12 11:02:38 +01:00
func ( q * commandQueue ) prependPreservedUniforms ( uniforms [ ] [ ] uint32 , dst * Image , srcs [ graphics . ShaderImageCount ] * Image , offsets [ graphics . ShaderImageCount - 1 ] [ 2 ] float32 , dstRegion , srcRegion graphicsdriver . Region ) [ ] [ ] uint32 {
2022-11-04 13:51:33 +01:00
origUniforms := uniforms
2022-11-12 11:02:38 +01:00
uniforms = q . uint32SlicesBuffer . alloc ( len ( origUniforms ) + graphics . PreservedUniformVariablesCount )
2022-11-04 13:51:33 +01:00
copy ( uniforms [ graphics . PreservedUniformVariablesCount : ] , origUniforms )
2022-10-30 10:31:06 +01:00
// Set the destination texture size.
dw , dh := dst . InternalSize ( )
2022-11-12 11:02:38 +01:00
udstsize := q . uint32sBuffer . alloc ( 2 )
udstsize [ 0 ] = math . Float32bits ( float32 ( dw ) )
udstsize [ 1 ] = math . Float32bits ( float32 ( dh ) )
2022-11-03 18:25:55 +01:00
uniforms [ graphics . TextureDestinationSizeUniformVariableIndex ] = udstsize
2022-10-30 10:31:06 +01:00
// Set the source texture sizes.
2022-11-12 11:02:38 +01:00
usizes := q . uint32sBuffer . alloc ( 2 * len ( srcs ) )
2022-10-30 10:31:06 +01:00
for i , src := range srcs {
if src != nil {
w , h := src . InternalSize ( )
2022-11-12 11:02:38 +01:00
usizes [ 2 * i ] = math . Float32bits ( float32 ( w ) )
usizes [ 2 * i + 1 ] = math . Float32bits ( float32 ( h ) )
2022-10-30 10:31:06 +01:00
}
}
uniforms [ graphics . TextureSourceSizesUniformVariableIndex ] = usizes
// Set the destination region.
2022-11-12 11:02:38 +01:00
udstrorig := q . uint32sBuffer . alloc ( 2 )
udstrorig [ 0 ] = math . Float32bits ( float32 ( dstRegion . X ) / float32 ( dw ) )
udstrorig [ 1 ] = math . Float32bits ( float32 ( dstRegion . Y ) / float32 ( dh ) )
2022-11-03 18:25:55 +01:00
uniforms [ graphics . TextureDestinationRegionOriginUniformVariableIndex ] = udstrorig
2022-11-12 11:02:38 +01:00
udstrsize := q . uint32sBuffer . alloc ( 2 )
udstrsize [ 0 ] = math . Float32bits ( float32 ( dstRegion . Width ) / float32 ( dw ) )
udstrsize [ 1 ] = math . Float32bits ( float32 ( dstRegion . Height ) / float32 ( dh ) )
2022-11-03 18:25:55 +01:00
uniforms [ graphics . TextureDestinationRegionSizeUniformVariableIndex ] = udstrsize
2022-10-30 10:31:06 +01:00
if srcs [ 0 ] != nil {
w , h := srcs [ 0 ] . InternalSize ( )
srcRegion . X /= float32 ( w )
srcRegion . Y /= float32 ( h )
srcRegion . Width /= float32 ( w )
srcRegion . Height /= float32 ( h )
for i := range offsets {
offsets [ i ] [ 0 ] /= float32 ( w )
offsets [ i ] [ 1 ] /= float32 ( h )
}
}
// Set the source offsets.
2022-11-12 11:02:38 +01:00
uoffsets := q . uint32sBuffer . alloc ( 2 * len ( offsets ) )
2022-10-30 10:31:06 +01:00
for i , offset := range offsets {
2022-11-12 11:02:38 +01:00
uoffsets [ 2 * i ] = math . Float32bits ( offset [ 0 ] )
uoffsets [ 2 * i + 1 ] = math . Float32bits ( offset [ 1 ] )
2022-10-30 10:31:06 +01:00
}
uniforms [ graphics . TextureSourceOffsetsUniformVariableIndex ] = uoffsets
// Set the source region of texture0.
2022-11-12 11:02:38 +01:00
usrcrorig := q . uint32sBuffer . alloc ( 2 )
usrcrorig [ 0 ] = math . Float32bits ( float32 ( srcRegion . X ) )
usrcrorig [ 1 ] = math . Float32bits ( float32 ( srcRegion . Y ) )
2022-11-03 18:25:55 +01:00
uniforms [ graphics . TextureSourceRegionOriginUniformVariableIndex ] = usrcrorig
2022-11-12 11:02:38 +01:00
usrcrsize := q . uint32sBuffer . alloc ( 2 )
usrcrsize [ 0 ] = math . Float32bits ( float32 ( srcRegion . Width ) )
usrcrsize [ 1 ] = math . Float32bits ( float32 ( srcRegion . Height ) )
2022-11-03 18:25:55 +01:00
uniforms [ graphics . TextureSourceRegionSizeUniformVariableIndex ] = usrcrsize
2022-11-12 11:02:38 +01:00
umatrix := q . uint32sBuffer . alloc ( 16 )
umatrix [ 0 ] = math . Float32bits ( 2 / float32 ( dw ) )
2022-11-03 18:25:55 +01:00
umatrix [ 1 ] = 0
umatrix [ 2 ] = 0
umatrix [ 3 ] = 0
umatrix [ 4 ] = 0
2022-11-12 11:02:38 +01:00
umatrix [ 5 ] = math . Float32bits ( 2 / float32 ( dh ) )
2022-11-03 18:25:55 +01:00
umatrix [ 6 ] = 0
umatrix [ 7 ] = 0
umatrix [ 8 ] = 0
umatrix [ 9 ] = 0
2022-11-12 11:02:38 +01:00
umatrix [ 10 ] = math . Float32bits ( 1 )
2022-11-03 18:25:55 +01:00
umatrix [ 11 ] = 0
2022-11-12 11:02:38 +01:00
umatrix [ 12 ] = math . Float32bits ( - 1 )
umatrix [ 13 ] = math . Float32bits ( - 1 )
2022-11-03 18:25:55 +01:00
umatrix [ 14 ] = 0
2022-11-12 11:02:38 +01:00
umatrix [ 15 ] = math . Float32bits ( 1 )
2022-11-03 18:25:55 +01:00
uniforms [ graphics . ProjectionMatrixUniformVariableIndex ] = umatrix
2022-10-30 10:31:06 +01:00
return uniforms
}
2022-11-04 13:51:33 +01:00
// buffer is a reusable buffer to allocate []T.
type buffer [ T any ] struct {
buf [ 2 ] [ ] T
// index is switched at the end of the frame,
// and the buffer of the original index is kept until the next frame ends.
index int
}
func ( b * buffer [ T ] ) alloc ( n int ) [ ] T {
buf := b . buf [ b . index ]
if len ( buf ) + n > cap ( buf ) {
buf = make ( [ ] T , 0 , max ( roundUpPower2 ( len ( buf ) + n ) , 16 ) )
}
s := buf [ len ( buf ) : len ( buf ) + n ]
b . buf [ b . index ] = buf [ : len ( buf ) + n ]
return s
}
func ( b * buffer [ T ] ) reset ( ) {
b . buf [ b . index ] = b . buf [ b . index ] [ : 0 ]
b . index ++
b . index %= len ( b . buf )
}