From ddef7351c11da779a9233bed2a1b1d7cf12b4cc8 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 19 Jun 2022 17:18:13 +0900 Subject: [PATCH] internal/graphicsdriver/directx: bug fix: clear constant buffers at the end of DrawTriangles Constant buffers should not be reset while a stencil buffer is used. Closes #2138 --- .../directx/graphics_windows.go | 18 ++-- internal/processtest/testdata/issue2138.go | 94 +++++++++++++++++++ 2 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 internal/processtest/testdata/issue2138.go diff --git a/internal/graphicsdriver/directx/graphics_windows.go b/internal/graphicsdriver/directx/graphics_windows.go index 598cdc7c2..94f4d26f0 100644 --- a/internal/graphicsdriver/directx/graphics_windows.go +++ b/internal/graphicsdriver/directx/graphics_windows.go @@ -1156,6 +1156,15 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.Sh } } + // Release constant buffers when too many ones were created. + // This is needed espciallly for testings, where present is always false. + if len(g.pipelineStates.constantBuffers[g.frameIndex]) >= 16 { + if err := g.flushCommandList(g.drawCommandList); err != nil { + return err + } + g.pipelineStates.releaseConstantBuffers(g.frameIndex) + } + return nil } @@ -1166,15 +1175,6 @@ func (g *Graphics) drawTriangles(pipelineState *iD3D12PipelineState, srcs [graph g.drawCommandList.DrawIndexedInstanced(uint32(indexLen), 1, uint32(indexOffset), 0, 0) - // Release constant buffers when too many ones were created. - // This is needed espciallly for testings, where present is always false. - if len(g.pipelineStates.constantBuffers[g.frameIndex]) >= 16 { - if err := g.flushCommandList(g.drawCommandList); err != nil { - return err - } - g.pipelineStates.releaseConstantBuffers(g.frameIndex) - } - return nil } diff --git a/internal/processtest/testdata/issue2138.go b/internal/processtest/testdata/issue2138.go new file mode 100644 index 000000000..6358e4473 --- /dev/null +++ b/internal/processtest/testdata/issue2138.go @@ -0,0 +1,94 @@ +// Copyright 2022 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. + +//go:build ignore +// +build ignore + +package main + +import ( + "errors" + "image" + "image/color" + "math" + + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/text" + "github.com/hajimehoshi/ebiten/v2/vector" + "golang.org/x/image/font" + "golang.org/x/image/font/gofont/goregular" + "golang.org/x/image/font/opentype" +) + +var regularTermination = errors.New("regular termination") + +var ( + emptyImage = ebiten.NewImage(3, 3) + debugCircleImage *ebiten.Image + emptyTextureImage = emptyImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image) + face font.Face +) + +func init() { + emptyImage.Fill(color.White) + + img := image.NewRGBA(image.Rect(0, 0, 20, 20)) + debugCircleImage = ebiten.NewImageFromImage(img) + + emptyImage.Fill(color.Black) + + f, _ := opentype.Parse(goregular.TTF) + face, _ = opentype.NewFace(f, &opentype.FaceOptions{ + Size: 12, + DPI: 72, + }) +} + +type Game struct { + counter int +} + +func (g *Game) Update() error { + g.counter++ + if g.counter > 16 { + return regularTermination + } + return nil +} + +func (g *Game) Draw(screen *ebiten.Image) { + // Before the fix, some complex renderings with EvenOdd might cause a DirectX error like this (#2138): + // panic: directx: IDXGISwapChain4::Present failed: HRESULT(2289696773) + + screen.DrawImage(debugCircleImage, nil) + text.Draw(screen, "014678.,", face, 100, 100, color.White) + + p := vector.Path{} + p.Arc(100, 100, 6, 0, 2*math.Pi, vector.Clockwise) + filling, indicies := p.AppendVerticesAndIndicesForFilling(nil, nil) + screen.DrawTriangles(filling, indicies, emptyTextureImage, &ebiten.DrawTrianglesOptions{ + FillRule: ebiten.EvenOdd, + }) +} + +func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { + return 800, 600 +} + +func main() { + if err := ebiten.RunGame(&Game{}); err != nil && !errors.Is(err, regularTermination) { + panic(err) + } + +}