Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: MRT Proof of concept #2953

Draft
wants to merge 40 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
32b5e3e
Saving
Zyko0 Apr 5, 2024
9003692
Restore unmanaged images
Zyko0 Apr 6, 2024
c247da0
Remove useless debug + setviewport only once
Zyko0 Apr 6, 2024
577664c
Dirty directx11
Zyko0 Apr 6, 2024
280cc1a
Update glsl
Zyko0 Apr 8, 2024
4536fad
Update hlsl
Zyko0 Apr 8, 2024
c7eeae7
Cleanup
Zyko0 Apr 8, 2024
d1fd704
Revert to gl_FragData (future webgl)
Zyko0 Apr 9, 2024
c3a358b
Fixed missing arg on gl with CGo
Zyko0 Apr 9, 2024
fc3a6ed
Fixed nil dst image checks
Zyko0 Apr 10, 2024
55f1a5d
(Fixed) webgl
Zyko0 Apr 10, 2024
c9eb30d
Fixed magic number
Zyko0 Apr 10, 2024
1b3bfda
Fixed empty target for directx11
Zyko0 Apr 10, 2024
c73221a
Merge upstream - fixed conflicts
Zyko0 Apr 10, 2024
65646df
Temporary fix for directx11
Zyko0 Apr 10, 2024
92a257a
directx: Better logic to assume MRT
Zyko0 Apr 10, 2024
6a8c00e
Fixed opengl tests
Zyko0 Apr 10, 2024
ced0c62
go vet error check
Zyko0 Apr 10, 2024
8c0bc0a
Implement the correct DrawTriangles definition on metal
Zyko0 Apr 10, 2024
15b5c88
Fixed DrawTriangles for ps5
Zyko0 Apr 10, 2024
15ccaf1
Fixed ps5 src argument constant name
Zyko0 Apr 10, 2024
042fdab
Fixed shader testdata (except msl)
Zyko0 Apr 11, 2024
b2b88f4
Disable metal shader compilation tests tmp
Zyko0 Apr 11, 2024
f021b5d
Fixed IR tests + skipping metal for now
Zyko0 Apr 12, 2024
75b6e1f
Fixed conflict
Zyko0 Apr 12, 2024
2faf8a5
Set max dst images to 8 + some wording
Zyko0 Apr 12, 2024
7dd4aa9
Directx 12 mrt working - 1st iteration
Zyko0 Apr 12, 2024
0b53525
Add a basic MRT test
Zyko0 Apr 13, 2024
d706d38
Add test with empty locations + fix directx 11 && 12 nil first argument
Zyko0 Apr 13, 2024
dbe06ec
Add a test when a single image is used with DrawMRT
Zyko0 Apr 13, 2024
dc6074b
Fixed stencil tests for directx
Zyko0 Apr 13, 2024
4830591
Debug image entry index
Zyko0 Apr 13, 2024
9881530
Attempt at fixing Directx 12
Zyko0 Apr 13, 2024
ec59239
nth attempt at fixing directx 12
Zyko0 Apr 14, 2024
c180c3c
Little tweak to dx12
Zyko0 Jun 9, 2024
6a00b86
Merge with main - fixed conflicts
Zyko0 Jun 9, 2024
b405003
Fixed misses
Zyko0 Jun 9, 2024
f34c1c0
Merge with main - fixed conflicts
Zyko0 Jun 9, 2024
5e40bce
Fixed authors name
Zyko0 Jun 14, 2024
ea462f4
Fixed comment
Zyko0 Jun 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
130 changes: 130 additions & 0 deletions examples/mrt/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright 2024 The Ebitengine Authors
//
// 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.

package main

import (
"fmt"
"image"
_ "image/jpeg"
"log"

"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
)

const (
dstSize = 128
screenWidth = dstSize * 2
screenHeight = dstSize * 2
)

var (
dsts = [8]*ebiten.Image{
ebiten.NewImageWithOptions(image.Rect(0, 0, dstSize, dstSize), &ebiten.NewImageOptions{
Unmanaged: true,
}),
ebiten.NewImageWithOptions(image.Rect(0, 0, dstSize, dstSize), &ebiten.NewImageOptions{
Unmanaged: true,
}),
ebiten.NewImageWithOptions(image.Rect(0, 0, dstSize, dstSize), &ebiten.NewImageOptions{
Unmanaged: true,
}),
ebiten.NewImageWithOptions(image.Rect(0, 0, dstSize, dstSize), &ebiten.NewImageOptions{
Unmanaged: true,
}),
}

shaderSrc = []byte(
`
//kage:units pixels

package main

func Fragment(dst vec4, src vec2, color vec4) (vec4, vec4, vec4, vec4) {
return vec4(1,0,0,1), vec4(0,1,0,1), vec4(0,0,1,1), vec4(1,0,1,1)
}
`)
s *ebiten.Shader
)

func init() {
var err error

s, err = ebiten.NewShader(shaderSrc)
if err != nil {
log.Fatal(err)
}
}

type Game struct {
}

func (g *Game) Update() error {
return nil
}

func (g *Game) Draw(screen *ebiten.Image) {
vertices := []ebiten.Vertex{
{
DstX: 0,
DstY: 0,
},
{
DstX: dstSize,
DstY: 0,
},
{
DstX: 0,
DstY: dstSize,
},
{
DstX: dstSize,
DstY: dstSize,
},
}
indices := []uint16{0, 1, 2, 1, 2, 3}
ebiten.DrawTrianglesShaderMRT(dsts, vertices, indices, s, nil)
// Dst 0
screen.DrawImage(dsts[0], nil)
// Dst 1
opts := &ebiten.DrawImageOptions{}
opts.GeoM.Translate(dstSize, 0)
screen.DrawImage(dsts[1], opts)
// Dst 2
opts.GeoM.Reset()
opts.GeoM.Translate(0, dstSize)
screen.DrawImage(dsts[2], opts)
// Dst 3
opts.GeoM.Reset()
opts.GeoM.Translate(dstSize, dstSize)
screen.DrawImage(dsts[3], opts)

ebitenutil.DebugPrint(screen, fmt.Sprintf("FPS: %.2f", ebiten.ActualFPS()))
}

func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
return screenWidth, screenHeight
}

func main() {
ebiten.SetWindowSize(screenWidth, screenHeight)
ebiten.SetVsyncEnabled(false)
ebiten.SetWindowTitle("MRT (Ebitengine Demo)")
if err := ebiten.RunGameWithOptions(&Game{}, &ebiten.RunGameOptions{
GraphicsLibrary: ebiten.GraphicsLibraryDirectX,
}); err != nil {
log.Fatal(err)
}
}
133 changes: 133 additions & 0 deletions image.go
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,139 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRule(options.FillRule), true, options.AntiAlias)
}

// DrawTrianglesShader draws triangles with the specified vertices and their indices with the specified shader.
//
// Vertex contains color values, which can be interpreted for any purpose by the shader.
//
// For the details about the shader, see https://ebitengine.org/en/documents/shader.html.
//
// If the shader unit is texels, one of the specified image is non-nil and its size is different from (width, height),
// DrawTrianglesShader panics.
// If one of the specified image is non-nil and is disposed, DrawTrianglesShader panics.
//
// If len(vertices) is more than MaxVertexCount, the exceeding part is ignored.
//
// If len(indices) is not multiple of 3, DrawTrianglesShader panics.
//
// If a value in indices is out of range of vertices, or not less than MaxVertexCount, DrawTrianglesShader panics.
//
// When a specified image is non-nil and is disposed, DrawTrianglesShader panics.
//
// If a specified uniform variable's length or type doesn't match with an expected one, DrawTrianglesShader panics.
//
// Even if a result is an invalid color as a premultiplied-alpha color, i.e. an alpha value exceeds other color values,
// the value is kept and is not clamped.
//
// When the image i is disposed, DrawTrianglesShader does nothing.
func DrawTrianglesShaderMRT(dsts [graphics.ShaderDstImageCount]*Image, vertices []Vertex, indices []uint16, shader *Shader, options *DrawTrianglesShaderOptions) {
var dstImgs [graphics.ShaderDstImageCount]*ui.Image
var firstDst *Image
for i, dst := range dsts {
if dst == nil {
continue
}
dst.copyCheck()
if dst.isDisposed() {
panic("ebiten: the destination images given to DrawTrianglesShaderMRT must not be disposed")
}
if firstDst == nil {
firstDst = dst
}
dstImgs[i] = dst.image
}

if shader.isDisposed() {
panic("ebiten: the given shader to DrawTrianglesShaderMRT must not be disposed")
}

if len(vertices) > graphicscommand.MaxVertexCount {
// The last part cannot be specified by indices. Just omit them.
vertices = vertices[:graphicscommand.MaxVertexCount]
}
if len(indices)%3 != 0 {
panic("ebiten: len(indices) % 3 must be 0")
}
for i, idx := range indices {
if int(idx) >= len(vertices) {
panic(fmt.Sprintf("ebiten: indices[%d] must be less than len(vertices) (%d) but was %d", i, len(vertices), idx))
}
}

if options == nil {
options = &DrawTrianglesShaderOptions{}
}

var blend graphicsdriver.Blend
if options.CompositeMode == CompositeModeCustom {
blend = options.Blend.internalBlend()
} else {
blend = options.CompositeMode.blend().internalBlend()
}

dst := firstDst
vs := dst.ensureTmpVertices(len(vertices) * graphics.VertexFloatCount)
src := options.Images[0]
for i, v := range vertices {
dx, dy := dst.adjustPositionF32(v.DstX, v.DstY)
vs[i*graphics.VertexFloatCount] = dx
vs[i*graphics.VertexFloatCount+1] = dy
sx, sy := v.SrcX, v.SrcY
if src != nil {
sx, sy = src.adjustPositionF32(sx, sy)
}
vs[i*graphics.VertexFloatCount+2] = sx
vs[i*graphics.VertexFloatCount+3] = sy
vs[i*graphics.VertexFloatCount+4] = v.ColorR
vs[i*graphics.VertexFloatCount+5] = v.ColorG
vs[i*graphics.VertexFloatCount+6] = v.ColorB
vs[i*graphics.VertexFloatCount+7] = v.ColorA
}

is := make([]uint32, len(indices))
for i := range is {
is[i] = uint32(indices[i])
}

var srcImgs [graphics.ShaderSrcImageCount]*ui.Image
var imgSize image.Point
for i, img := range options.Images {
if img == nil {
continue
}
if img.isDisposed() {
panic("ebiten: the given image to DrawTrianglesShader must not be disposed")
}
if shader.unit == shaderir.Texels {
if i == 0 {
imgSize = img.Bounds().Size()
} else {
// TODO: Check imgw > 0 && imgh > 0
if img.Bounds().Size() != imgSize {
panic("ebiten: all the source images must be the same size with the rectangle")
}
}
}
srcImgs[i] = img.image
}

var srcRegions [graphics.ShaderSrcImageCount]image.Rectangle
for i, img := range options.Images {
if img == nil {
continue
}
srcRegions[i] = img.adjustedBounds()
}

for _, dst := range dsts {
if dst == nil {
continue
}
dst.tmpUniforms = dst.tmpUniforms[:0]
dst.tmpUniforms = shader.appendUniforms(dst.tmpUniforms, options.Uniforms)
}
ui.DrawTrianglesMRT(dstImgs, srcImgs, vs, is, blend, dst.adjustedBounds(), srcRegions, shader.shader, dst.tmpUniforms, graphicsdriver.FillRule(options.FillRule), true, options.AntiAlias)
}

// DrawRectShaderOptions represents options for DrawRectShader.
type DrawRectShaderOptions struct {
// GeoM is a geometry matrix to draw.
Expand Down