From a5993f09a24204f81e29e8b5af951af440cce031 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 30 Oct 2022 18:31:06 +0900 Subject: [PATCH] internal/graphicscommand: refactoring: add preserved uniform variables at the graphicscommand package This is a preparation to skip setting unnecessary uniform variables like dstRegion. Updates #2232 --- internal/graphicscommand/command.go | 83 ++++++++++---- .../directx/graphics_windows.go | 48 ++------ internal/graphicsdriver/graphics.go | 2 +- .../graphicsdriver/metal/graphics_darwin.go | 70 +++--------- internal/graphicsdriver/opengl/graphics.go | 103 +++--------------- 5 files changed, 102 insertions(+), 204 deletions(-) diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index 568d0a1ae..247785cb1 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -108,22 +108,12 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh q.tmpNumVertexFloats += len(vertices) q.tmpNumIndices += len(indices) - 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) - } - } + uniforms = prependPreservedUniforms(uniforms, dst, srcs, offsets, dstRegion, srcRegion) // TODO: If dst is the screen, reorder the command to be the last. if !split && 0 < len(q.commands) { if last, ok := q.commands[len(q.commands)-1].(*drawTrianglesCommand); ok { - if last.CanMergeWithDrawTrianglesCommand(dst, srcs, vertices, blend, dstRegion, srcRegion, shader, uniforms, evenOdd) { + if last.CanMergeWithDrawTrianglesCommand(dst, srcs, vertices, blend, dstRegion, shader, uniforms, evenOdd) { last.setVertices(q.lastVertices(len(vertices) + last.numVertices())) last.addNumIndices(len(indices)) return @@ -134,12 +124,10 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh c := q.drawTrianglesCommandPool.get() c.dst = dst c.srcs = srcs - c.offsets = offsets c.vertices = q.lastVertices(len(vertices)) c.nindices = len(indices) c.blend = blend c.dstRegion = dstRegion - c.srcRegion = srcRegion c.shader = shader c.uniforms = uniforms c.evenOdd = evenOdd @@ -258,12 +246,10 @@ func FlushCommands(graphicsDriver graphicsdriver.Graphics, endFrame bool) error type drawTrianglesCommand struct { dst *Image srcs [graphics.ShaderImageCount]*Image - offsets [graphics.ShaderImageCount - 1][2]float32 vertices []float32 nindices int blend graphicsdriver.Blend dstRegion graphicsdriver.Region - srcRegion graphicsdriver.Region shader *Shader uniforms [][]float32 evenOdd bool @@ -317,7 +303,7 @@ func (c *drawTrianglesCommand) Exec(graphicsDriver graphicsdriver.Graphics, inde imgs[i] = src.image.ID() } - return graphicsDriver.DrawTriangles(c.dst.image.ID(), imgs, c.offsets, c.shader.shader.ID(), c.nindices, indexOffset, c.blend, c.dstRegion, c.srcRegion, c.uniforms, c.evenOdd) + return graphicsDriver.DrawTriangles(c.dst.image.ID(), imgs, c.shader.shader.ID(), c.nindices, indexOffset, c.blend, c.dstRegion, c.uniforms, c.evenOdd) } func (c *drawTrianglesCommand) numVertices() int { @@ -338,7 +324,7 @@ func (c *drawTrianglesCommand) addNumIndices(n int) { // CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged // with the drawTrianglesCommand c. -func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageCount]*Image, vertices []float32, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) bool { +func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageCount]*Image, vertices []float32, blend graphicsdriver.Blend, dstRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) bool { if c.shader != shader { return false } @@ -367,9 +353,6 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs if c.dstRegion != dstRegion { return false } - if c.srcRegion != srcRegion { - return false - } if c.evenOdd || evenOdd { if c.evenOdd && evenOdd { return !mightOverlapDstRegions(c.vertices, vertices) @@ -572,3 +555,61 @@ func MaxImageSize(graphicsDriver graphicsdriver.Graphics) int { }) return size } + +func prependPreservedUniforms(uniforms [][]float32, dst *Image, srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, dstRegion, srcRegion graphicsdriver.Region) [][]float32 { + uniforms = append(make([][]float32, graphics.PreservedUniformVariablesCount), uniforms...) + + // Set the destination texture size. + dw, dh := dst.InternalSize() + uniforms[graphics.TextureDestinationSizeUniformVariableIndex] = []float32{float32(dw), float32(dh)} + + // Set the source texture sizes. + usizes := make([]float32, 2*len(srcs)) + for i, src := range srcs { + if src != nil { + w, h := src.InternalSize() + usizes[2*i] = float32(w) + usizes[2*i+1] = float32(h) + } + } + uniforms[graphics.TextureSourceSizesUniformVariableIndex] = usizes + + // Set the destination region. + // TODO: Set them only when the shader refers this (#2232). + uniforms[graphics.TextureDestinationRegionOriginUniformVariableIndex] = []float32{float32(dstRegion.X) / float32(dw), float32(dstRegion.Y) / float32(dh)} + uniforms[graphics.TextureDestinationRegionSizeUniformVariableIndex] = []float32{float32(dstRegion.Width) / float32(dw), float32(dstRegion.Height) / float32(dh)} + + 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. + uoffsets := make([]float32, 2*len(offsets)) + for i, offset := range offsets { + uoffsets[2*i] = offset[0] + uoffsets[2*i+1] = offset[1] + } + uniforms[graphics.TextureSourceOffsetsUniformVariableIndex] = uoffsets + + // Set the source region of texture0. + // TODO: Set them only when the shader refers this (#2232). + uniforms[graphics.TextureSourceRegionOriginUniformVariableIndex] = []float32{float32(srcRegion.X), float32(srcRegion.Y)} + uniforms[graphics.TextureSourceRegionSizeUniformVariableIndex] = []float32{float32(srcRegion.Width), float32(srcRegion.Height)} + + uniforms[graphics.ProjectionMatrixUniformVariableIndex] = []float32{ + 2 / float32(dw), 0, 0, 0, + 0, 2 / float32(dh), 0, 0, + 0, 0, 1, 0, + -1, -1, 0, 1, + } + + return uniforms +} diff --git a/internal/graphicsdriver/directx/graphics_windows.go b/internal/graphicsdriver/directx/graphics_windows.go index 0c3b0d20e..aff77f0ff 100644 --- a/internal/graphicsdriver/directx/graphics_windows.go +++ b/internal/graphicsdriver/directx/graphics_windows.go @@ -1168,7 +1168,7 @@ func (g *Graphics) NewShader(program *shaderir.Program) (graphicsdriver.Shader, return s, nil } -func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.ShaderImageCount]graphicsdriver.ImageID, offsets [graphics.ShaderImageCount - 1][2]float32, shaderID graphicsdriver.ShaderID, indexLen int, indexOffset int, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, uniforms [][]float32, evenOdd bool) error { +func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.ShaderImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, indexLen int, indexOffset int, blend graphicsdriver.Blend, dstRegion graphicsdriver.Region, uniforms [][]float32, evenOdd bool) error { if shaderID == graphicsdriver.InvalidShaderID { return fmt.Errorf("directx: shader ID is invalid") } @@ -1217,45 +1217,15 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.Sh shader := g.shaders[shaderID] - // TODO: This logic is very similar to Metal's. Let's unify them. - dw, dh := dst.internalSize() - us := make([][]float32, graphics.PreservedUniformVariablesCount+len(uniforms)) - us[graphics.TextureDestinationSizeUniformVariableIndex] = []float32{float32(dw), float32(dh)} - usizes := make([]float32, 2*len(srcs)) - for i, src := range srcImages { - if src != nil { - w, h := src.internalSize() - usizes[2*i] = float32(w) - usizes[2*i+1] = float32(h) - } - } - us[graphics.TextureSourceSizesUniformVariableIndex] = usizes - udorigin := []float32{float32(dstRegion.X) / float32(dw), float32(dstRegion.Y) / float32(dh)} - us[graphics.TextureDestinationRegionOriginUniformVariableIndex] = udorigin - udsize := []float32{float32(dstRegion.Width) / float32(dw), float32(dstRegion.Height) / float32(dh)} - us[graphics.TextureDestinationRegionSizeUniformVariableIndex] = udsize - uoffsets := make([]float32, 2*len(offsets)) - for i, offset := range offsets { - uoffsets[2*i] = offset[0] - uoffsets[2*i+1] = offset[1] - } - us[graphics.TextureSourceOffsetsUniformVariableIndex] = uoffsets - usorigin := []float32{float32(srcRegion.X), float32(srcRegion.Y)} - us[graphics.TextureSourceRegionOriginUniformVariableIndex] = usorigin - ussize := []float32{float32(srcRegion.Width), float32(srcRegion.Height)} - us[graphics.TextureSourceRegionSizeUniformVariableIndex] = ussize - us[graphics.ProjectionMatrixUniformVariableIndex] = []float32{ - 2 / float32(dw), 0, 0, 0, - 0, -2 / float32(dh), 0, 0, - 0, 0, 1, 0, - -1, 1, 0, 1, - } + // In DirectX, the NDC's Y direction (upward) and the framebuffer's Y direction (downward) don't + // match. Then, the Y direction must be inverted. + const idx = graphics.ProjectionMatrixUniformVariableIndex + uniforms[idx][1] *= -1 + uniforms[idx][5] *= -1 + uniforms[idx][9] *= -1 + uniforms[idx][13] *= -1 - for i, u := range uniforms { - us[graphics.PreservedUniformVariablesCount+i] = u - } - - flattenUniforms := shader.uniformsToFloat32s(us) + flattenUniforms := shader.uniformsToFloat32s(uniforms) w, h := dst.internalSize() g.needFlushDrawCommandList = true diff --git a/internal/graphicsdriver/graphics.go b/internal/graphicsdriver/graphics.go index f30613ec0..b0fd5027f 100644 --- a/internal/graphicsdriver/graphics.go +++ b/internal/graphicsdriver/graphics.go @@ -52,7 +52,7 @@ type Graphics interface { NewShader(program *shaderir.Program) (Shader, error) // DrawTriangles draws an image onto another image with the given parameters. - DrawTriangles(dst ImageID, srcs [graphics.ShaderImageCount]ImageID, offsets [graphics.ShaderImageCount - 1][2]float32, shader ShaderID, indexLen int, indexOffset int, blend Blend, dstRegion, srcRegion Region, uniforms [][]float32, evenOdd bool) error + DrawTriangles(dst ImageID, srcs [graphics.ShaderImageCount]ImageID, shader ShaderID, indexLen int, indexOffset int, blend Blend, dstRegion Region, uniforms [][]float32, evenOdd bool) error } // GraphicsNotReady represents that the graphics driver is not ready for recovering from the context lost. diff --git a/internal/graphicsdriver/metal/graphics_darwin.go b/internal/graphicsdriver/metal/graphics_darwin.go index 18bf06832..30995c2f8 100644 --- a/internal/graphicsdriver/metal/graphics_darwin.go +++ b/internal/graphicsdriver/metal/graphics_darwin.go @@ -503,7 +503,7 @@ func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, dstRegion graph return nil } -func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, offsets [graphics.ShaderImageCount - 1][2]float32, shaderID graphicsdriver.ShaderID, indexLen int, indexOffset int, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, uniforms [][]float32, evenOdd bool) error { +func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, indexLen int, indexOffset int, blend graphicsdriver.Blend, dstRegion graphicsdriver.Region, uniforms [][]float32, evenOdd bool) error { if shaderID == graphicsdriver.InvalidShaderID { return fmt.Errorf("metal: shader ID is invalid") } @@ -519,58 +519,20 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. srcs[i] = g.images[srcID] } - uniformVars := make([][]float32, graphics.PreservedUniformVariablesCount+len(uniforms)) - - // Set the destination texture size. - dw, dh := dst.internalSize() - uniformVars[graphics.TextureDestinationSizeUniformVariableIndex] = []float32{float32(dw), float32(dh)} - - // Set the source texture sizes. - usizes := make([]float32, 2*len(srcs)) - for i, src := range srcs { - if src != nil { - w, h := src.internalSize() - usizes[2*i] = float32(w) - usizes[2*i+1] = float32(h) - } - } - uniformVars[graphics.TextureSourceSizesUniformVariableIndex] = usizes - - // Set the destination region's origin. - udorigin := []float32{float32(dstRegion.X) / float32(dw), float32(dstRegion.Y) / float32(dh)} - uniformVars[graphics.TextureDestinationRegionOriginUniformVariableIndex] = udorigin - - // Set the destination region's size. - udsize := []float32{float32(dstRegion.Width) / float32(dw), float32(dstRegion.Height) / float32(dh)} - uniformVars[graphics.TextureDestinationRegionSizeUniformVariableIndex] = udsize - - // Set the source offsets. - uoffsets := make([]float32, 2*len(offsets)) - for i, offset := range offsets { - uoffsets[2*i] = offset[0] - uoffsets[2*i+1] = offset[1] - } - uniformVars[graphics.TextureSourceOffsetsUniformVariableIndex] = uoffsets - - // Set the source region's origin of texture0. - usorigin := []float32{float32(srcRegion.X), float32(srcRegion.Y)} - uniformVars[graphics.TextureSourceRegionOriginUniformVariableIndex] = usorigin - - // Set the source region's size of texture0. - ussize := []float32{float32(srcRegion.Width), float32(srcRegion.Height)} - uniformVars[graphics.TextureSourceRegionSizeUniformVariableIndex] = ussize - - uniformVars[graphics.ProjectionMatrixUniformVariableIndex] = []float32{ - 2 / float32(dw), 0, 0, 0, - 0, -2 / float32(dh), 0, 0, - 0, 0, 1, 0, - -1, 1, 0, 1, - } + uniformVars := make([][]float32, len(uniforms)) // Set the additional uniform variables. for i, v := range uniforms { - const offset = graphics.PreservedUniformVariablesCount - t := g.shaders[shaderID].ir.Uniforms[offset+i] + if i == graphics.ProjectionMatrixUniformVariableIndex { + // In Metal, the NDC's Y direction (upward) and the framebuffer's Y direction (downward) don't + // match. Then, the Y direction must be inverted. + v[1] *= -1 + v[5] *= -1 + v[9] *= -1 + v[13] *= -1 + } + + t := g.shaders[shaderID].ir.Uniforms[i] switch t.Main { case shaderir.Mat3: // float3x3 requires 16-byte alignment (#2036). @@ -578,7 +540,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. copy(v1[0:3], v[0:3]) copy(v1[4:7], v[3:6]) copy(v1[8:11], v[6:9]) - uniformVars[offset+i] = v1 + uniformVars[i] = v1 case shaderir.Array: switch t.Sub[0].Main { case shaderir.Mat3: @@ -590,12 +552,12 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. copy(v1[offset1+4:offset1+7], v[offset0+3:offset0+6]) copy(v1[offset1+8:offset1+11], v[offset0+6:offset0+9]) } - uniformVars[offset+i] = v1 + uniformVars[i] = v1 default: - uniformVars[offset+i] = v + uniformVars[i] = v } default: - uniformVars[offset+i] = v + uniformVars[i] = v } } diff --git a/internal/graphicsdriver/opengl/graphics.go b/internal/graphicsdriver/opengl/graphics.go index 834e7a593..04df1d343 100644 --- a/internal/graphicsdriver/opengl/graphics.go +++ b/internal/graphicsdriver/opengl/graphics.go @@ -180,7 +180,7 @@ func (g *Graphics) uniformVariableName(idx int) string { return name } -func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, offsets [graphics.ShaderImageCount - 1][2]float32, shaderID graphicsdriver.ShaderID, indexLen int, indexOffset int, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, uniforms [][]float32, evenOdd bool) error { +func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, indexLen int, indexOffset int, blend graphicsdriver.Blend, dstRegion graphicsdriver.Region, uniforms [][]float32, evenOdd bool) error { if shaderID == graphicsdriver.InvalidShaderID { return fmt.Errorf("opengl: shader ID is invalid") } @@ -203,101 +203,26 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. shader := g.shaders[shaderID] program := shader.p - ulen := graphics.PreservedUniformVariablesCount + len(uniforms) + ulen := len(uniforms) if cap(g.uniformVars) < ulen { g.uniformVars = make([]uniformVariable, ulen) } else { g.uniformVars = g.uniformVars[:ulen] } - { - const idx = graphics.TextureDestinationSizeUniformVariableIndex - w, h := destination.framebufferSize() - g.uniformVars[idx].name = g.uniformVariableName(idx) - g.uniformVars[idx].value = []float32{float32(w), float32(h)} - g.uniformVars[idx].typ = shader.ir.Uniforms[idx] - } - { - sizes := make([]float32, 2*len(srcIDs)) - for i, srcID := range srcIDs { - if img := g.images[srcID]; img != nil { - w, h := img.framebufferSize() - sizes[2*i] = float32(w) - sizes[2*i+1] = float32(h) - } - - } - const idx = graphics.TextureSourceSizesUniformVariableIndex - g.uniformVars[idx].name = g.uniformVariableName(idx) - g.uniformVars[idx].value = sizes - g.uniformVars[idx].typ = shader.ir.Uniforms[idx] - } - dw, dh := destination.framebufferSize() - { - origin := []float32{float32(dstRegion.X) / float32(dw), float32(dstRegion.Y) / float32(dh)} - const idx = graphics.TextureDestinationRegionOriginUniformVariableIndex - g.uniformVars[idx].name = g.uniformVariableName(idx) - g.uniformVars[idx].value = origin - g.uniformVars[idx].typ = shader.ir.Uniforms[idx] - } - { - size := []float32{float32(dstRegion.Width) / float32(dw), float32(dstRegion.Height) / float32(dh)} - const idx = graphics.TextureDestinationRegionSizeUniformVariableIndex - g.uniformVars[idx].name = g.uniformVariableName(idx) - g.uniformVars[idx].value = size - g.uniformVars[idx].typ = shader.ir.Uniforms[idx] - } - { - voffsets := make([]float32, 2*len(offsets)) - for i, o := range offsets { - voffsets[2*i] = o[0] - voffsets[2*i+1] = o[1] - } - const idx = graphics.TextureSourceOffsetsUniformVariableIndex - g.uniformVars[idx].name = g.uniformVariableName(idx) - g.uniformVars[idx].value = voffsets - g.uniformVars[idx].typ = shader.ir.Uniforms[idx] - } - { - origin := []float32{float32(srcRegion.X), float32(srcRegion.Y)} - const idx = graphics.TextureSourceRegionOriginUniformVariableIndex - g.uniformVars[idx].name = g.uniformVariableName(idx) - g.uniformVars[idx].value = origin - g.uniformVars[idx].typ = shader.ir.Uniforms[idx] - } - { - size := []float32{float32(srcRegion.Width), float32(srcRegion.Height)} - const idx = graphics.TextureSourceRegionSizeUniformVariableIndex - g.uniformVars[idx].name = g.uniformVariableName(idx) - g.uniformVars[idx].value = size - g.uniformVars[idx].typ = shader.ir.Uniforms[idx] - } - { - const idx = graphics.ProjectionMatrixUniformVariableIndex - g.uniformVars[idx].name = g.uniformVariableName(idx) - if destination.screen { - g.uniformVars[idx].value = []float32{ - 2 / float32(dw), 0, 0, 0, - 0, -2 / float32(dh), 0, 0, - 0, 0, 1, 0, - -1, 1, 0, 1, - } - } else { - g.uniformVars[idx].value = []float32{ - 2 / float32(dw), 0, 0, 0, - 0, 2 / float32(dh), 0, 0, - 0, 0, 1, 0, - -1, -1, 0, 1, - } - } - g.uniformVars[idx].typ = shader.ir.Uniforms[idx] - } - for i, v := range uniforms { - const offset = graphics.PreservedUniformVariablesCount - g.uniformVars[i+offset].name = g.uniformVariableName(i + offset) - g.uniformVars[i+offset].value = v - g.uniformVars[i+offset].typ = shader.ir.Uniforms[i+offset] + g.uniformVars[i].name = g.uniformVariableName(i) + g.uniformVars[i].value = v + g.uniformVars[i].typ = shader.ir.Uniforms[i] + } + + // In OpenGL, the NDC's Y direction (upward), so flip the Y direction for the final framebuffer. + if destination.screen { + const idx = graphics.ProjectionMatrixUniformVariableIndex + g.uniformVars[idx].value[1] *= -1 + g.uniformVars[idx].value[5] *= -1 + g.uniformVars[idx].value[9] *= -1 + g.uniformVars[idx].value[13] *= -1 } var imgs [graphics.ShaderImageCount]textureVariable