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"
2023-10-08 15:59:35 +02:00
"image"
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/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
2023-10-08 15:59:35 +02:00
Exec ( commandQueue * commandQueue , 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 ]
2023-10-08 10:18:16 +02:00
p . pool [ len ( p . pool ) - 1 ] = nil
2021-10-29 16:41:47 +02:00
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 )
}
2019-04-22 16:12:03 +02:00
// drawTrianglesCommand represents a drawing command to draw an image on another image.
type drawTrianglesCommand struct {
2024-07-16 15:43:49 +02:00
dst * Image
2024-06-09 19:02:47 +02:00
srcs [ graphics . ShaderSrcImageCount ] * Image
2022-11-04 08:17:40 +01:00
vertices [ ] float32
blend graphicsdriver . Blend
dstRegions [ ] graphicsdriver . DstRegion
shader * Shader
2022-12-03 08:11:26 +01:00
uniforms [ ] uint32
2023-11-04 10:09:47 +01:00
fillRule graphicsdriver . FillRule
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
2024-07-16 15:43:49 +02:00
dst := fmt . Sprintf ( "%d" , c . dst . id )
if c . dst . screen {
dst += " (screen)"
2020-05-17 20:45:58 +02:00
}
2024-06-09 19:02:47 +02:00
var srcstrs [ graphics . ShaderSrcImageCount ] 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
}
2024-07-16 15:43:49 +02:00
return fmt . Sprintf ( "draw-triangles: dst: %s <- src: [%s], num of dst regions: %d, num of indices: %d, blend: %s, fill rule: %s, shader id: %d" , dst , strings . Join ( srcstrs [ : ] , ", " ) , len ( c . dstRegions ) , c . numIndices ( ) , blend , c . fillRule , c . shader . id )
2018-03-04 16:45:03 +01:00
}
2019-04-22 16:12:03 +02:00
// Exec executes the drawTrianglesCommand.
2023-10-08 15:59:35 +02:00
func ( c * drawTrianglesCommand ) Exec ( commandQueue * commandQueue , 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
2024-07-16 15:43:49 +02:00
var imgs [ graphics . ShaderSrcImageCount ] graphicsdriver . ImageID
2022-10-02 16:49:07 +02:00
for i , src := range c . srcs {
if src == nil {
2024-07-16 15:43:49 +02:00
imgs [ i ] = graphicsdriver . InvalidImageID
2022-10-02 16:49:07 +02:00
continue
2020-05-24 19:31:54 +02:00
}
2024-07-16 15:43:49 +02:00
imgs [ i ] = src . image . ID ( )
2018-11-05 19:44:43 +01:00
}
2021-07-01 08:41:25 +02:00
2024-07-16 15:43:49 +02:00
return graphicsDriver . DrawTriangles ( c . dst . image . ID ( ) , imgs , c . shader . shader . ID ( ) , c . dstRegions , indexOffset , c . blend , c . uniforms , c . fillRule )
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.
2024-07-16 15:43:49 +02:00
func ( c * drawTrianglesCommand ) CanMergeWithDrawTrianglesCommand ( dst * Image , srcs [ graphics . ShaderSrcImageCount ] * Image , vertices [ ] float32 , blend graphicsdriver . Blend , shader * Shader , uniforms [ ] uint32 , fillRule graphicsdriver . FillRule ) 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
}
}
2024-07-16 15:43:49 +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
}
2023-11-04 10:09:47 +01:00
if c . fillRule != fillRule {
2022-11-12 14:26:28 +01:00
return false
}
2024-06-08 04:54:44 +02:00
if c . fillRule != graphicsdriver . FillRuleFillAll && 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 )
2023-12-17 14:42:34 +01:00
const margin = 1
return minX1 < maxX2 + margin && minX2 < maxX1 + margin && minY1 < maxY2 + margin && minY2 < maxY1 + margin
2021-07-05 12:35:34 +02:00
}
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-10-08 15:59:35 +02:00
args [ ] writePixelsCommandArgs
}
type writePixelsCommandArgs struct {
pixels * graphics . ManagedBytes
region image . Rectangle
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.
2023-10-08 15:59:35 +02:00
func ( c * writePixelsCommand ) Exec ( commandQueue * commandQueue , 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
}
2023-10-08 15:59:35 +02:00
args := make ( [ ] graphicsdriver . PixelsArgs , 0 , len ( c . args ) )
for _ , a := range c . args {
pix , f := a . pixels . GetAndRelease ( )
// A finalizer is executed when flushing the queue at the end of the frame.
// At the end of the frame, the last command is rendering triangles onto the screen,
// so the bytes are already sent to GPU and synced.
// TODO: This might be fragile. When is the better time to call finalizers by a command queue?
commandQueue . addFinalizer ( f )
args = append ( args , graphicsdriver . PixelsArgs {
Pixels : pix ,
Region : a . region ,
} )
}
if err := c . dst . image . WritePixels ( 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.
2023-10-08 15:59:35 +02:00
func ( c * readPixelsCommand ) Exec ( commandQueue * commandQueue , 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.
2023-10-08 15:59:35 +02:00
func ( c * disposeImageCommand ) Exec ( commandQueue * commandQueue , 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.
2023-10-08 15:59:35 +02:00
func ( c * disposeShaderCommand ) Exec ( commandQueue * commandQueue , 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.
2023-10-08 15:59:35 +02:00
func ( c * newImageCommand ) Exec ( commandQueue * commandQueue , 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.
2023-10-08 15:59:35 +02:00
func ( c * newShaderCommand ) Exec ( commandQueue * commandQueue , 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
}
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
}