Compare commits

..

1 Commits

Author SHA1 Message Date
Bertrand Jung
a666f485b8
Merge 7dd4aa9150 into 88dae9c7d4 2024-04-12 23:11:56 +00:00
8 changed files with 135 additions and 215 deletions

View File

@ -711,8 +711,10 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
//
// When the image i is disposed, DrawTrianglesShader does nothing.
func DrawTrianglesShaderMRT(dsts [graphics.ShaderDstImageCount]*Image, vertices []Vertex, indices []uint16, shader *Shader, options *DrawTrianglesShaderOptions) {
if dsts[0] == nil || dsts[0].isDisposed() {
panic("ebiten: the first destination image given to DrawTrianglesShaderMRT must not be nil or disposed")
}
var dstImgs [graphics.ShaderDstImageCount]*ui.Image
var firstDst *Image
for i, dst := range dsts {
if dst == nil {
continue
@ -721,9 +723,6 @@ func DrawTrianglesShaderMRT(dsts [graphics.ShaderDstImageCount]*Image, vertices
if dst.isDisposed() {
panic("ebiten: the destination images given to DrawTrianglesShaderMRT must not be disposed")
}
if firstDst == nil {
firstDst = dst
}
dstImgs[i] = dst.image
}
@ -755,7 +754,7 @@ func DrawTrianglesShaderMRT(dsts [graphics.ShaderDstImageCount]*Image, vertices
blend = options.CompositeMode.blend().internalBlend()
}
dst := firstDst
dst := dsts[0]
vs := dst.ensureTmpVertices(len(vertices) * graphics.VertexFloatCount)
src := options.Images[0]
for i, v := range vertices {

View File

@ -576,14 +576,12 @@ func drawTrianglesMRT(dsts [graphics.ShaderDstImageCount]*Image, srcs [graphics.
src.backend.sourceInThisFrame = true
}
var firstDst *Image
var dstImgs [graphics.ShaderDstImageCount]*graphicscommand.Image
for i, dst := range dsts {
if dst == nil {
continue
}
dst.ensureIsolatedFromSource(backends)
firstDst = dst
dstImgs[i] = dst.backend.image
}
@ -597,7 +595,7 @@ func drawTrianglesMRT(dsts [graphics.ShaderDstImageCount]*Image, srcs [graphics.
}
}
r := firstDst.regionWithPadding()
r := dsts[0].regionWithPadding()
// TODO: Check if dstRegion does not to violate the region.
dstRegion = dstRegion.Add(r.Min)

View File

@ -330,14 +330,7 @@ func (q *commandQueue) prependPreservedUniforms(uniforms []uint32, shader *Shade
copy(uniforms[graphics.PreservedUniformUint32Count:], origUniforms)
// Set the destination texture size.
var firstDst *Image
for _, dst := range dsts {
if dst != nil {
firstDst = dst
break
}
}
dw, dh := firstDst.InternalSize()
dw, dh := dsts[0].InternalSize()
uniforms[0] = math.Float32bits(float32(dw))
uniforms[1] = math.Float32bits(float32(dh))
uniformIndex := 2

View File

@ -517,31 +517,30 @@ func (g *graphics11) removeShader(s *shader11) {
func (g *graphics11) setAsRenderTargets(dsts []*image11, useStencil bool) error {
var rtvs []*_ID3D11RenderTargetView
var dsv *_ID3D11DepthStencilView
for _, dst := range dsts {
for _, i := range dsts {
// Ignore a nil image in case of MRT
if dst == nil {
if i == nil {
rtvs = append(rtvs, nil)
continue
}
if dst.renderTargetView == nil {
rtv, err := g.device.CreateRenderTargetView(unsafe.Pointer(dst.texture), nil)
if i.renderTargetView == nil {
rtv, err := g.device.CreateRenderTargetView(unsafe.Pointer(i.texture), nil)
if err != nil {
return err
}
dst.renderTargetView = rtv
i.renderTargetView = rtv
}
rtvs = append(rtvs, dst.renderTargetView)
rtvs = append(rtvs, i.renderTargetView)
if !useStencil || dsv != nil {
if !useStencil {
continue
}
if dst.screen {
if i.screen {
return fmt.Errorf("directx: a stencil buffer is not available for a screen image")
}
if dst.stencil == nil {
w, h := dst.internalSize()
if i.stencil == nil {
w, h := i.internalSize()
s, err := g.device.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{
Width: uint32(w),
Height: uint32(h),
@ -560,21 +559,20 @@ func (g *graphics11) setAsRenderTargets(dsts []*image11, useStencil bool) error
if err != nil {
return err
}
dst.stencil = s
i.stencil = s
}
if dst.stencilView == nil {
sv, err := g.device.CreateDepthStencilView(unsafe.Pointer(dst.stencil), nil)
if i.stencilView == nil {
sv, err := g.device.CreateDepthStencilView(unsafe.Pointer(i.stencil), nil)
if err != nil {
return err
}
dst.stencilView = sv
i.stencilView = sv
}
dsv = dst.stencilView
}
g.deviceContext.OMSetRenderTargets(rtvs, dsv)
g.deviceContext.OMSetRenderTargets(rtvs, dsts[0].stencilView)
if useStencil {
g.deviceContext.ClearDepthStencilView(dsv, uint8(_D3D11_CLEAR_STENCIL), 0, 0)
g.deviceContext.ClearDepthStencilView(dsts[0].stencilView, uint8(_D3D11_CLEAR_STENCIL), 0, 0)
}
return nil
@ -587,7 +585,7 @@ func (g *graphics11) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphics
g.deviceContext.PSSetShaderResources(0, srvs[:])
var dsts [graphics.ShaderDstImageCount]*image11
var vp _D3D11_VIEWPORT
var viewports [graphics.ShaderDstImageCount]_D3D11_VIEWPORT
var targetCount int
firstTarget := -1
for i, id := range dstIDs {
@ -600,7 +598,7 @@ func (g *graphics11) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphics
}
dsts[i] = img
w, h := img.internalSize()
vp = _D3D11_VIEWPORT{
viewports[i] = _D3D11_VIEWPORT{
TopLeftX: 0,
TopLeftY: 0,
Width: float32(w),
@ -623,12 +621,13 @@ func (g *graphics11) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphics
// If the number of targets is more than one, or if the only target is the first one, then
// it is safe to assume that MRT is used.
// Also, it only matters in order to specify empty targets/viewports when not all slots are
// being filled, even though it's not a MRT scenario.
// being filled.
if targetCount > 1 || firstTarget > 0 {
targetCount = graphics.ShaderDstImageCount
}
g.deviceContext.RSSetViewports(viewports[:targetCount])
g.deviceContext.RSSetViewports([]_D3D11_VIEWPORT{vp})
if err := g.setAsRenderTargets(dsts[:targetCount], fillRule != graphicsdriver.FillAll); err != nil {
return err
}

View File

@ -1084,7 +1084,7 @@ func (g *graphics12) NewShader(program *shaderir.Program) (graphicsdriver.Shader
func (g *graphics12) setAsRenderTargets(dsts []*image12, useStencil bool) error {
var rtvs []_D3D12_CPU_DESCRIPTOR_HANDLE
var dsv *_D3D12_CPU_DESCRIPTOR_HANDLE
var dsvPtr *_D3D12_CPU_DESCRIPTOR_HANDLE
for i, img := range dsts {
// Ignore a nil image in case of MRT
@ -1127,18 +1127,18 @@ func (g *graphics12) setAsRenderTargets(dsts []*image12, useStencil bool) error
rtv := rtvBase
rtvs = append(rtvs, rtv)
if !useStencil || dsv != nil {
if !useStencil || dsvPtr != nil {
continue
}
if err := img.ensureDepthStencilView(g.device); err != nil {
return err
}
sv, err := img.dsvDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
dsv, err := img.dsvDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
if err != nil {
return err
}
dsv = &sv
dsvPtr = &dsv
}
if !useStencil {
@ -1147,8 +1147,8 @@ func (g *graphics12) setAsRenderTargets(dsts []*image12, useStencil bool) error
}
g.drawCommandList.OMSetStencilRef(0)
g.drawCommandList.OMSetRenderTargets(rtvs, false, dsv)
g.drawCommandList.ClearDepthStencilView(*dsv, _D3D12_CLEAR_FLAG_STENCIL, 0, 0, nil)
g.drawCommandList.OMSetRenderTargets(rtvs, false, dsvPtr)
g.drawCommandList.ClearDepthStencilView(*dsvPtr, _D3D12_CLEAR_FLAG_STENCIL, 0, 0, nil)
return nil
}
@ -1177,7 +1177,7 @@ func (g *graphics12) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphics
var resourceBarriers []_D3D12_RESOURCE_BARRIER_Transition
var dsts [graphics.ShaderDstImageCount]*image12
var vp _D3D12_VIEWPORT
var viewports [graphics.ShaderDstImageCount]_D3D12_VIEWPORT
var targetCount int
firstTarget := -1
for i, id := range dstIDs {
@ -1190,7 +1190,7 @@ func (g *graphics12) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphics
}
dsts[i] = img
w, h := img.internalSize()
vp = _D3D12_VIEWPORT{
viewports[i] = _D3D12_VIEWPORT{
TopLeftX: 0,
TopLeftY: 0,
Width: float32(w),
@ -1225,12 +1225,14 @@ func (g *graphics12) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphics
// If the number of targets is more than one, or if the only target is the first one, then
// it is safe to assume that MRT is used.
// Also, it only matters in order to specify empty targets/viewports when not all slots are
// being filled, even though it's not a MRT scenario.
// being filled.
usesMRT := targetCount > 1 || firstTarget > 0
if usesMRT {
targetCount = graphics.ShaderDstImageCount
}
g.drawCommandList.RSSetViewports(viewports[:targetCount])
if err := g.setAsRenderTargets(dsts[:targetCount], fillRule != graphicsdriver.FillAll); err != nil {
return err
}
@ -1240,8 +1242,6 @@ func (g *graphics12) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphics
g.needFlushDrawCommandList = true
g.drawCommandList.RSSetViewports([]_D3D12_VIEWPORT{vp})
g.drawCommandList.IASetPrimitiveTopology(_D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST)
g.drawCommandList.IASetVertexBuffers(0, []_D3D12_VERTEX_BUFFER_VIEW{
{

View File

@ -70,7 +70,7 @@ func (i *image12) disposeImpl() {
func (i *image12) ReadPixels(args []graphicsdriver.PixelsArgs) error {
if i.screen {
return errors.New("directx: ReadPixels cannot be called on the screen")
return errors.New("directx: Pixels cannot be called on the screen")
}
if err := i.graphics.flushCommandList(i.graphics.drawCommandList); err != nil {

View File

@ -484,18 +484,6 @@ func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3D
}
// Create a pipeline state.
rtBlendDesc := _D3D12_RENDER_TARGET_BLEND_DESC{
BlendEnable: 1,
LogicOpEnable: 0,
SrcBlend: blendFactorToBlend12(blend.BlendFactorSourceRGB, false),
DestBlend: blendFactorToBlend12(blend.BlendFactorDestinationRGB, false),
BlendOp: blendOperationToBlendOp12(blend.BlendOperationRGB),
SrcBlendAlpha: blendFactorToBlend12(blend.BlendFactorSourceAlpha, true),
DestBlendAlpha: blendFactorToBlend12(blend.BlendFactorDestinationAlpha, true),
BlendOpAlpha: blendOperationToBlendOp12(blend.BlendOperationAlpha),
LogicOp: _D3D12_LOGIC_OP_NOOP,
RenderTargetWriteMask: writeMask,
}
psoDesc := _D3D12_GRAPHICS_PIPELINE_STATE_DESC{
pRootSignature: rootSignature,
VS: _D3D12_SHADER_BYTECODE{
@ -510,7 +498,102 @@ func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3D
AlphaToCoverageEnable: 0,
IndependentBlendEnable: 0,
RenderTarget: [8]_D3D12_RENDER_TARGET_BLEND_DESC{
rtBlendDesc, // TODO: need to fill them all?
{
BlendEnable: 1,
LogicOpEnable: 0,
SrcBlend: blendFactorToBlend12(blend.BlendFactorSourceRGB, false),
DestBlend: blendFactorToBlend12(blend.BlendFactorDestinationRGB, false),
BlendOp: blendOperationToBlendOp12(blend.BlendOperationRGB),
SrcBlendAlpha: blendFactorToBlend12(blend.BlendFactorSourceAlpha, true),
DestBlendAlpha: blendFactorToBlend12(blend.BlendFactorDestinationAlpha, true),
BlendOpAlpha: blendOperationToBlendOp12(blend.BlendOperationAlpha),
LogicOp: _D3D12_LOGIC_OP_NOOP,
RenderTargetWriteMask: writeMask,
},
{
BlendEnable: 1,
LogicOpEnable: 0,
SrcBlend: blendFactorToBlend12(blend.BlendFactorSourceRGB, false),
DestBlend: blendFactorToBlend12(blend.BlendFactorDestinationRGB, false),
BlendOp: blendOperationToBlendOp12(blend.BlendOperationRGB),
SrcBlendAlpha: blendFactorToBlend12(blend.BlendFactorSourceAlpha, true),
DestBlendAlpha: blendFactorToBlend12(blend.BlendFactorDestinationAlpha, true),
BlendOpAlpha: blendOperationToBlendOp12(blend.BlendOperationAlpha),
LogicOp: _D3D12_LOGIC_OP_NOOP,
RenderTargetWriteMask: writeMask,
},
{
BlendEnable: 1,
LogicOpEnable: 0,
SrcBlend: blendFactorToBlend12(blend.BlendFactorSourceRGB, false),
DestBlend: blendFactorToBlend12(blend.BlendFactorDestinationRGB, false),
BlendOp: blendOperationToBlendOp12(blend.BlendOperationRGB),
SrcBlendAlpha: blendFactorToBlend12(blend.BlendFactorSourceAlpha, true),
DestBlendAlpha: blendFactorToBlend12(blend.BlendFactorDestinationAlpha, true),
BlendOpAlpha: blendOperationToBlendOp12(blend.BlendOperationAlpha),
LogicOp: _D3D12_LOGIC_OP_NOOP,
RenderTargetWriteMask: writeMask,
},
{
BlendEnable: 1,
LogicOpEnable: 0,
SrcBlend: blendFactorToBlend12(blend.BlendFactorSourceRGB, false),
DestBlend: blendFactorToBlend12(blend.BlendFactorDestinationRGB, false),
BlendOp: blendOperationToBlendOp12(blend.BlendOperationRGB),
SrcBlendAlpha: blendFactorToBlend12(blend.BlendFactorSourceAlpha, true),
DestBlendAlpha: blendFactorToBlend12(blend.BlendFactorDestinationAlpha, true),
BlendOpAlpha: blendOperationToBlendOp12(blend.BlendOperationAlpha),
LogicOp: _D3D12_LOGIC_OP_NOOP,
RenderTargetWriteMask: writeMask,
},
{
BlendEnable: 1,
LogicOpEnable: 0,
SrcBlend: blendFactorToBlend12(blend.BlendFactorSourceRGB, false),
DestBlend: blendFactorToBlend12(blend.BlendFactorDestinationRGB, false),
BlendOp: blendOperationToBlendOp12(blend.BlendOperationRGB),
SrcBlendAlpha: blendFactorToBlend12(blend.BlendFactorSourceAlpha, true),
DestBlendAlpha: blendFactorToBlend12(blend.BlendFactorDestinationAlpha, true),
BlendOpAlpha: blendOperationToBlendOp12(blend.BlendOperationAlpha),
LogicOp: _D3D12_LOGIC_OP_NOOP,
RenderTargetWriteMask: writeMask,
},
{
BlendEnable: 1,
LogicOpEnable: 0,
SrcBlend: blendFactorToBlend12(blend.BlendFactorSourceRGB, false),
DestBlend: blendFactorToBlend12(blend.BlendFactorDestinationRGB, false),
BlendOp: blendOperationToBlendOp12(blend.BlendOperationRGB),
SrcBlendAlpha: blendFactorToBlend12(blend.BlendFactorSourceAlpha, true),
DestBlendAlpha: blendFactorToBlend12(blend.BlendFactorDestinationAlpha, true),
BlendOpAlpha: blendOperationToBlendOp12(blend.BlendOperationAlpha),
LogicOp: _D3D12_LOGIC_OP_NOOP,
RenderTargetWriteMask: writeMask,
},
{
BlendEnable: 1,
LogicOpEnable: 0,
SrcBlend: blendFactorToBlend12(blend.BlendFactorSourceRGB, false),
DestBlend: blendFactorToBlend12(blend.BlendFactorDestinationRGB, false),
BlendOp: blendOperationToBlendOp12(blend.BlendOperationRGB),
SrcBlendAlpha: blendFactorToBlend12(blend.BlendFactorSourceAlpha, true),
DestBlendAlpha: blendFactorToBlend12(blend.BlendFactorDestinationAlpha, true),
BlendOpAlpha: blendOperationToBlendOp12(blend.BlendOperationAlpha),
LogicOp: _D3D12_LOGIC_OP_NOOP,
RenderTargetWriteMask: writeMask,
},
{
BlendEnable: 1,
LogicOpEnable: 0,
SrcBlend: blendFactorToBlend12(blend.BlendFactorSourceRGB, false),
DestBlend: blendFactorToBlend12(blend.BlendFactorDestinationRGB, false),
BlendOp: blendOperationToBlendOp12(blend.BlendOperationRGB),
SrcBlendAlpha: blendFactorToBlend12(blend.BlendFactorSourceAlpha, true),
DestBlendAlpha: blendFactorToBlend12(blend.BlendFactorDestinationAlpha, true),
BlendOpAlpha: blendOperationToBlendOp12(blend.BlendOperationAlpha),
LogicOp: _D3D12_LOGIC_OP_NOOP,
RenderTargetWriteMask: writeMask,
},
},
},
SampleMask: math.MaxUint32,

View File

@ -2595,155 +2595,3 @@ func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
}
}
}
// Issue #2930
func TestShaderMRT(t *testing.T) {
const w, h = 16, 16
s, err := ebiten.NewShader([]byte(`//kage:unit pixels
package main
func Fragment(dstPos vec4, srcPos vec2, color vec4) (vec4, vec4, vec4, 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),
vec4(1, 1, 0, 1),
vec4(0, 1, 1, 1),
vec4(1, 1, 1, 1),
vec4(1, 1, 1, 0)
}
`))
if err != nil {
t.Fatal(err)
}
bounds := image.Rect(0, 0, w, h)
opts := &ebiten.NewImageOptions{
Unmanaged: true,
}
vertices := []ebiten.Vertex{
{
DstX: 0,
DstY: 0,
},
{
DstX: w,
DstY: 0,
},
{
DstX: 0,
DstY: h,
},
{
DstX: w,
DstY: h,
},
}
indices := []uint16{0, 1, 2, 1, 2, 3}
t.Run("8 locations", func(t *testing.T) {
imgs := [8]*ebiten.Image{
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
}
wantColors := [8]color.RGBA{
{R: 0xff, G: 0, B: 0, A: 0xff},
{R: 0, G: 0xff, B: 0, A: 0xff},
{R: 0, G: 0, B: 0xff, A: 0xff},
{R: 0xff, G: 0, B: 0xff, A: 0xff},
{R: 0xff, G: 0xff, B: 0, A: 0xff},
{R: 0, G: 0xff, B: 0xff, A: 0xff},
{R: 0xff, G: 0xff, B: 0xff, A: 0xff},
{R: 0xff, G: 0xff, B: 0xff, A: 0},
}
ebiten.DrawTrianglesShaderMRT(imgs, vertices, indices, s, nil)
for k, dst := range imgs {
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
got := dst.At(i, j).(color.RGBA)
want := wantColors[k]
if !sameColors(got, want, 1) {
t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
}
}
}
}
})
/*t.Run("Empty locations", func(t *testing.T) {
imgs := [8]*ebiten.Image{
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
}
wantColors := [8]color.RGBA{
{},
{R: 0, G: 0xff, B: 0, A: 0xff},
{},
{R: 0xff, G: 0, B: 0xff, A: 0xff},
{},
{R: 0, G: 0xff, B: 0xff, A: 0xff},
{},
{R: 0xff, G: 0xff, B: 0xff, A: 0},
}
dsts := [8]*ebiten.Image{
nil, imgs[1], nil, imgs[3], nil, imgs[5], nil, imgs[7],
}
ebiten.DrawTrianglesShaderMRT(dsts, vertices, indices, s, nil)
for k, dst := range imgs {
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
got := dst.At(i, j).(color.RGBA)
want := wantColors[k]
if !sameColors(got, want, 1) {
t.Errorf("%d dst.At(%d, %d): got: %v, want: %v", k, i, j, got, want)
}
}
}
}
})*/
t.Run("1 location (first slot)", func(t *testing.T) {
imgs := [8]*ebiten.Image{
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
ebiten.NewImageWithOptions(bounds, opts),
}
wantColors := [8]color.RGBA{
{R: 0xff, G: 0, B: 0, A: 0xff},
{}, {}, {}, {}, {}, {}, {},
}
dsts := [8]*ebiten.Image{
imgs[0], nil, nil, nil, nil, nil, nil, nil,
}
ebiten.DrawTrianglesShaderMRT(dsts, vertices, indices, s, nil)
for k, dst := range imgs {
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
got := dst.At(i, j).(color.RGBA)
want := wantColors[k]
if !sameColors(got, want, 1) {
t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
}
}
}
}
})
}