From 419bb4c1e9a928c80e8894ae68f2bf0517ee4935 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 12 Nov 2022 19:02:38 +0900 Subject: [PATCH] all: use uint32 instead of float32 for uniform values This is a preparation for other types of uniform values. Updates #2305 --- image.go | 4 +- internal/atlas/image.go | 4 +- internal/buffered/image.go | 2 +- internal/graphicscommand/command.go | 74 +++++++++---------- internal/graphicscommand/image.go | 2 +- .../directx/graphics_windows.go | 33 +++++---- .../directx/pipeline_windows.go | 6 +- internal/graphicsdriver/graphics.go | 2 +- .../graphicsdriver/metal/graphics_darwin.go | 19 ++--- internal/graphicsdriver/opengl/graphics.go | 11 +-- internal/graphicsdriver/opengl/program.go | 19 +++-- internal/mipmap/mipmap.go | 2 +- internal/restorable/image.go | 6 +- internal/shaderir/program.go | 2 +- internal/ui/image.go | 2 +- internal/ui/shader.go | 19 +++-- shader.go | 2 +- 17 files changed, 111 insertions(+), 98 deletions(-) diff --git a/image.go b/image.go index 5f6879cc3..6ff6b5d85 100644 --- a/image.go +++ b/image.go @@ -247,7 +247,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) { useColorM := !colorm.IsIdentity() shader := builtinShader(filter, builtinshader.AddressUnsafe, useColorM) - var uniforms [][]float32 + var uniforms [][]uint32 if useColorM { var body [16]float32 var translation [4]float32 @@ -476,7 +476,7 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o useColorM := !colorm.IsIdentity() shader := builtinShader(filter, address, useColorM) - var uniforms [][]float32 + var uniforms [][]uint32 if useColorM { var body [16]float32 var translation [4]float32 diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 2e390d132..b9a43694e 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -376,13 +376,13 @@ func (i *Image) processSrc(src *Image) { // 5: Color G // 6: Color B // 7: Color Y -func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool) { +func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]uint32, evenOdd bool) { backendsM.Lock() defer backendsM.Unlock() i.drawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegion, subimageOffsets, shader, uniforms, evenOdd, false) } -func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool, keepOnAtlas bool) { +func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]uint32, evenOdd bool, keepOnAtlas bool) { if i.disposed { panic("atlas: the drawing target image must not be disposed (DrawTriangles)") } diff --git a/internal/buffered/image.go b/internal/buffered/image.go index 81da57b11..b740a968c 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -140,7 +140,7 @@ func (i *Image) WritePixels(pix []byte, x, y, width, height int) { // DrawTriangles draws the src image with the given vertices. // // Copying vertices and indices is the caller's responsibility. -func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool) { +func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]uint32, evenOdd bool) { for _, src := range srcs { if i == src { panic("buffered: Image.DrawTriangles: source images must be different from the receiver") diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index 941187cde..74a280692 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -71,8 +71,8 @@ type commandQueue struct { drawTrianglesCommandPool drawTrianglesCommandPool - float32sBuffer buffer[float32] - float32SlicesBuffer buffer[[]float32] + uint32sBuffer buffer[uint32] + uint32SlicesBuffer buffer[[]uint32] } // theCommandQueue is the command queue for the current process. @@ -92,7 +92,7 @@ func mustUseDifferentVertexBuffer(nextNumVertexFloats, nextNumIndices int) bool } // EnqueueDrawTrianglesCommand enqueues a drawing-image command. -func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) { +func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]uint32, evenOdd bool) { if len(indices) > graphics.IndicesCount { panic(fmt.Sprintf("graphicscommand: len(indices) must be <= graphics.IndicesCount but not at EnqueueDrawTrianglesCommand: len(indices): %d, graphics.IndicesCount: %d", len(indices), graphics.IndicesCount)) } @@ -169,8 +169,8 @@ func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, endFrame bo err = q.flush(graphicsDriver, endFrame) }) if endFrame { - q.float32sBuffer.reset() - q.float32SlicesBuffer.reset() + q.uint32sBuffer.reset() + q.uint32SlicesBuffer.reset() } return } @@ -271,7 +271,7 @@ type drawTrianglesCommand struct { blend graphicsdriver.Blend dstRegions []graphicsdriver.DstRegion shader *Shader - uniforms [][]float32 + uniforms [][]uint32 evenOdd bool } @@ -342,7 +342,7 @@ func (c *drawTrianglesCommand) setVertices(vertices []float32) { // 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, shader *Shader, uniforms [][]float32, evenOdd bool) bool { +func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageCount]*Image, vertices []float32, blend graphicsdriver.Blend, shader *Shader, uniforms [][]uint32, evenOdd bool) bool { if c.shader != shader { return false } @@ -586,38 +586,38 @@ func roundUpPower2(x int) int { return p2 } -func (q *commandQueue) prependPreservedUniforms(uniforms [][]float32, dst *Image, srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, dstRegion, srcRegion graphicsdriver.Region) [][]float32 { +func (q *commandQueue) prependPreservedUniforms(uniforms [][]uint32, dst *Image, srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, dstRegion, srcRegion graphicsdriver.Region) [][]uint32 { origUniforms := uniforms - uniforms = q.float32SlicesBuffer.alloc(len(origUniforms) + graphics.PreservedUniformVariablesCount) + uniforms = q.uint32SlicesBuffer.alloc(len(origUniforms) + graphics.PreservedUniformVariablesCount) copy(uniforms[graphics.PreservedUniformVariablesCount:], origUniforms) // Set the destination texture size. dw, dh := dst.InternalSize() - udstsize := q.float32sBuffer.alloc(2) - udstsize[0] = float32(dw) - udstsize[1] = float32(dh) + udstsize := q.uint32sBuffer.alloc(2) + udstsize[0] = math.Float32bits(float32(dw)) + udstsize[1] = math.Float32bits(float32(dh)) uniforms[graphics.TextureDestinationSizeUniformVariableIndex] = udstsize // Set the source texture sizes. - usizes := q.float32sBuffer.alloc(2 * len(srcs)) + usizes := q.uint32sBuffer.alloc(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) + usizes[2*i] = math.Float32bits(float32(w)) + usizes[2*i+1] = math.Float32bits(float32(h)) } } uniforms[graphics.TextureSourceSizesUniformVariableIndex] = usizes // Set the destination region. - udstrorig := q.float32sBuffer.alloc(2) - udstrorig[0] = float32(dstRegion.X) / float32(dw) - udstrorig[1] = float32(dstRegion.Y) / float32(dh) + udstrorig := q.uint32sBuffer.alloc(2) + udstrorig[0] = math.Float32bits(float32(dstRegion.X) / float32(dw)) + udstrorig[1] = math.Float32bits(float32(dstRegion.Y) / float32(dh)) uniforms[graphics.TextureDestinationRegionOriginUniformVariableIndex] = udstrorig - udstrsize := q.float32sBuffer.alloc(2) - udstrsize[0] = float32(dstRegion.Width) / float32(dw) - udstrsize[1] = float32(dstRegion.Height) / float32(dh) + udstrsize := q.uint32sBuffer.alloc(2) + udstrsize[0] = math.Float32bits(float32(dstRegion.Width) / float32(dw)) + udstrsize[1] = math.Float32bits(float32(dstRegion.Height) / float32(dh)) uniforms[graphics.TextureDestinationRegionSizeUniformVariableIndex] = udstrsize if srcs[0] != nil { @@ -633,41 +633,41 @@ func (q *commandQueue) prependPreservedUniforms(uniforms [][]float32, dst *Image } // Set the source offsets. - uoffsets := q.float32sBuffer.alloc(2 * len(offsets)) + uoffsets := q.uint32sBuffer.alloc(2 * len(offsets)) for i, offset := range offsets { - uoffsets[2*i] = offset[0] - uoffsets[2*i+1] = offset[1] + uoffsets[2*i] = math.Float32bits(offset[0]) + uoffsets[2*i+1] = math.Float32bits(offset[1]) } uniforms[graphics.TextureSourceOffsetsUniformVariableIndex] = uoffsets // Set the source region of texture0. - usrcrorig := q.float32sBuffer.alloc(2) - usrcrorig[0] = float32(srcRegion.X) - usrcrorig[1] = float32(srcRegion.Y) + usrcrorig := q.uint32sBuffer.alloc(2) + usrcrorig[0] = math.Float32bits(float32(srcRegion.X)) + usrcrorig[1] = math.Float32bits(float32(srcRegion.Y)) uniforms[graphics.TextureSourceRegionOriginUniformVariableIndex] = usrcrorig - usrcrsize := q.float32sBuffer.alloc(2) - usrcrsize[0] = float32(srcRegion.Width) - usrcrsize[1] = float32(srcRegion.Height) + usrcrsize := q.uint32sBuffer.alloc(2) + usrcrsize[0] = math.Float32bits(float32(srcRegion.Width)) + usrcrsize[1] = math.Float32bits(float32(srcRegion.Height)) uniforms[graphics.TextureSourceRegionSizeUniformVariableIndex] = usrcrsize - umatrix := q.float32sBuffer.alloc(16) - umatrix[0] = 2 / float32(dw) + umatrix := q.uint32sBuffer.alloc(16) + umatrix[0] = math.Float32bits(2 / float32(dw)) umatrix[1] = 0 umatrix[2] = 0 umatrix[3] = 0 umatrix[4] = 0 - umatrix[5] = 2 / float32(dh) + umatrix[5] = math.Float32bits(2 / float32(dh)) umatrix[6] = 0 umatrix[7] = 0 umatrix[8] = 0 umatrix[9] = 0 - umatrix[10] = 1 + umatrix[10] = math.Float32bits(1) umatrix[11] = 0 - umatrix[12] = -1 - umatrix[13] = -1 + umatrix[12] = math.Float32bits(-1) + umatrix[13] = math.Float32bits(-1) umatrix[14] = 0 - umatrix[15] = 1 + umatrix[15] = math.Float32bits(1) uniforms[graphics.ProjectionMatrixUniformVariableIndex] = umatrix return uniforms diff --git a/internal/graphicscommand/image.go b/internal/graphicscommand/image.go index 4a6f9f784..e30919528 100644 --- a/internal/graphicscommand/image.go +++ b/internal/graphicscommand/image.go @@ -146,7 +146,7 @@ func (i *Image) InternalSize() (int, int) { // // If the source image is not specified, i.e., src is nil and there is no image in the uniform variables, the // elements for the source image are not used. -func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) { +func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]uint32, evenOdd bool) { for _, src := range srcs { if src == nil { continue diff --git a/internal/graphicsdriver/directx/graphics_windows.go b/internal/graphicsdriver/directx/graphics_windows.go index 360f7cd24..68bef475c 100644 --- a/internal/graphicsdriver/directx/graphics_windows.go +++ b/internal/graphicsdriver/directx/graphics_windows.go @@ -1171,7 +1171,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, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms [][]float32, evenOdd bool) error { +func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.ShaderImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms [][]uint32, evenOdd bool) error { if shaderID == graphicsdriver.InvalidShaderID { return fmt.Errorf("directx: shader ID is invalid") } @@ -1223,12 +1223,13 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.Sh // 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 + // Invert the sign bits as float32 values. + uniforms[idx][1] = uniforms[idx][1] ^ (1 << 31) + uniforms[idx][5] = uniforms[idx][5] ^ (1 << 31) + uniforms[idx][9] = uniforms[idx][9] ^ (1 << 31) + uniforms[idx][13] = uniforms[idx][13] ^ (1 << 31) - flattenUniforms := shader.uniformsToFloat32s(uniforms) + flattenUniforms := shader.flattenUniforms(uniforms) w, h := dst.internalSize() g.needFlushDrawCommandList = true @@ -1716,11 +1717,11 @@ func (s *Shader) pipelineState(blend graphicsdriver.Blend, stencilMode stencilMo return state, nil } -func (s *Shader) uniformsToFloat32s(uniforms [][]float32) []float32 { - var fs []float32 +func (s *Shader) flattenUniforms(uniforms [][]uint32) []uint32 { + var fs []uint32 for i, u := range uniforms { if len(fs) < s.uniformOffsets[i]/4 { - fs = append(fs, make([]float32, s.uniformOffsets[i]/4-len(fs))...) + fs = append(fs, make([]uint32, s.uniformOffsets[i]/4-len(fs))...) } t := s.uniformTypes[i] @@ -1803,7 +1804,7 @@ func (s *Shader) uniformsToFloat32s(uniforms [][]float32) []float32 { } } } else { - fs = append(fs, make([]float32, (t.Length-1)*4+1)...) + fs = append(fs, make([]uint32, (t.Length-1)*4+1)...) } case shaderir.Vec2: if u != nil { @@ -1814,7 +1815,7 @@ func (s *Shader) uniformsToFloat32s(uniforms [][]float32) []float32 { } } } else { - fs = append(fs, make([]float32, (t.Length-1)*4+2)...) + fs = append(fs, make([]uint32, (t.Length-1)*4+2)...) } case shaderir.Vec3: if u != nil { @@ -1825,13 +1826,13 @@ func (s *Shader) uniformsToFloat32s(uniforms [][]float32) []float32 { } } } else { - fs = append(fs, make([]float32, (t.Length-1)*4+3)...) + fs = append(fs, make([]uint32, (t.Length-1)*4+3)...) } case shaderir.Vec4: if u != nil { fs = append(fs, u...) } else { - fs = append(fs, make([]float32, t.Length*4)...) + fs = append(fs, make([]uint32, t.Length*4)...) } case shaderir.Mat2: if u != nil { @@ -1846,7 +1847,7 @@ func (s *Shader) uniformsToFloat32s(uniforms [][]float32) []float32 { fs = fs[:len(fs)-2] } } else { - fs = append(fs, make([]float32, (t.Length-1)*8+6)...) + fs = append(fs, make([]uint32, (t.Length-1)*8+6)...) } case shaderir.Mat3: if u != nil { @@ -1862,7 +1863,7 @@ func (s *Shader) uniformsToFloat32s(uniforms [][]float32) []float32 { fs = fs[:len(fs)-1] } } else { - fs = append(fs, make([]float32, (t.Length-1)*12+11)...) + fs = append(fs, make([]uint32, (t.Length-1)*12+11)...) } case shaderir.Mat4: if u != nil { @@ -1876,7 +1877,7 @@ func (s *Shader) uniformsToFloat32s(uniforms [][]float32) []float32 { ) } } else { - fs = append(fs, make([]float32, t.Length*16)...) + fs = append(fs, make([]uint32, t.Length*16)...) } default: panic(fmt.Sprintf("directx: not implemented type for uniform variables: %s", t.String())) diff --git a/internal/graphicsdriver/directx/pipeline_windows.go b/internal/graphicsdriver/directx/pipeline_windows.go index c8a934576..c1ec9f428 100644 --- a/internal/graphicsdriver/directx/pipeline_windows.go +++ b/internal/graphicsdriver/directx/pipeline_windows.go @@ -146,7 +146,7 @@ func (p *pipelineStates) initialize(device *_ID3D12Device) (ferr error) { return nil } -func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D12GraphicsCommandList, frameIndex int, screen bool, srcs [graphics.ShaderImageCount]*Image, shader *Shader, dstRegions []graphicsdriver.DstRegion, uniforms []float32, blend graphicsdriver.Blend, indexOffset int, evenOdd bool) error { +func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D12GraphicsCommandList, frameIndex int, screen bool, srcs [graphics.ShaderImageCount]*Image, shader *Shader, dstRegions []graphicsdriver.DstRegion, uniforms []uint32, blend graphicsdriver.Blend, indexOffset int, evenOdd bool) error { idx := len(p.constantBuffers[frameIndex]) if idx >= numDescriptorsPerFrame { return fmt.Errorf("directx: too many constant buffers") @@ -161,7 +161,7 @@ func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D } const bufferSizeAlignement = 256 - bufferSize := uint32(unsafe.Sizeof(float32(0))) * uint32(len(uniforms)) + bufferSize := uint32(unsafe.Sizeof(uint32(0))) * uint32(len(uniforms)) if bufferSize > 0 { bufferSize = ((bufferSize-1)/bufferSizeAlignement + 1) * bufferSizeAlignement } @@ -228,7 +228,7 @@ func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D } // Update the constant buffer. - copy(unsafe.Slice((*float32)(unsafe.Pointer(m)), len(uniforms)), uniforms) + copy(unsafe.Slice((*uint32)(unsafe.Pointer(m)), len(uniforms)), uniforms) rs, err := p.ensureRootSignature(device) if err != nil { diff --git a/internal/graphicsdriver/graphics.go b/internal/graphicsdriver/graphics.go index 8974027b6..91013e6fe 100644 --- a/internal/graphicsdriver/graphics.go +++ b/internal/graphicsdriver/graphics.go @@ -57,7 +57,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, shader ShaderID, dstRegions []DstRegion, indexOffset int, blend Blend, uniforms [][]float32, evenOdd bool) error + DrawTriangles(dst ImageID, srcs [graphics.ShaderImageCount]ImageID, shader ShaderID, dstRegions []DstRegion, indexOffset int, blend Blend, uniforms [][]uint32, 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 ed1fee5af..7032c97e9 100644 --- a/internal/graphicsdriver/metal/graphics_darwin.go +++ b/internal/graphicsdriver/metal/graphics_darwin.go @@ -421,7 +421,7 @@ func (g *Graphics) flushRenderCommandEncoderIfNeeded() { g.lastDst = nil } -func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs [graphics.ShaderImageCount]*Image, indexOffset int, shader *Shader, uniforms [][]float32, blend graphicsdriver.Blend, evenOdd bool) error { +func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs [graphics.ShaderImageCount]*Image, indexOffset int, shader *Shader, uniforms [][]uint32, blend graphicsdriver.Blend, evenOdd bool) error { // When prepareing a stencil buffer, flush the current render command encoder // to make sure the stencil buffer is cleared when loading. // TODO: What about clearing the stencil buffer by vertices? @@ -544,7 +544,7 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs return nil } -func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms [][]float32, evenOdd bool) error { +func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms [][]uint32, evenOdd bool) error { if shaderID == graphicsdriver.InvalidShaderID { return fmt.Errorf("metal: shader ID is invalid") } @@ -560,24 +560,25 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. srcs[i] = g.images[srcID] } - uniformVars := make([][]float32, len(uniforms)) + uniformVars := make([][]uint32, len(uniforms)) // Set the additional uniform variables. for i, v := range uniforms { 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 + // Invert the sign bits as float32 values. + v[1] = v[1] ^ (1 << 31) + v[5] = v[5] ^ (1 << 31) + v[9] = v[9] ^ (1 << 31) + v[13] = v[13] ^ (1 << 31) } t := g.shaders[shaderID].ir.Uniforms[i] switch t.Main { case shaderir.Mat3: // float3x3 requires 16-byte alignment (#2036). - v1 := make([]float32, 12) + v1 := make([]uint32, 12) copy(v1[0:3], v[0:3]) copy(v1[4:7], v[3:6]) copy(v1[8:11], v[6:9]) @@ -585,7 +586,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. case shaderir.Array: switch t.Sub[0].Main { case shaderir.Mat3: - v1 := make([]float32, t.Length*12) + v1 := make([]uint32, t.Length*12) for j := 0; j < t.Length; j++ { offset0 := j * 9 offset1 := j * 12 diff --git a/internal/graphicsdriver/opengl/graphics.go b/internal/graphicsdriver/opengl/graphics.go index 9bbc982a7..72312d62d 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, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms [][]float32, evenOdd bool) error { +func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms [][]uint32, evenOdd bool) error { if shaderID == graphicsdriver.InvalidShaderID { return fmt.Errorf("opengl: shader ID is invalid") } @@ -213,10 +213,11 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. // In OpenGL, the NDC's Y direction is 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 + // Invert the sign bits as float32 values. + g.uniformVars[idx].value[1] = g.uniformVars[idx].value[1] ^ (1 << 31) + g.uniformVars[idx].value[5] = g.uniformVars[idx].value[5] ^ (1 << 31) + g.uniformVars[idx].value[9] = g.uniformVars[idx].value[9] ^ (1 << 31) + g.uniformVars[idx].value[13] = g.uniformVars[idx].value[13] ^ (1 << 31) } var imgs [graphics.ShaderImageCount]textureVariable diff --git a/internal/graphicsdriver/opengl/program.go b/internal/graphicsdriver/opengl/program.go index 8569e06ae..04bdfaaa7 100644 --- a/internal/graphicsdriver/opengl/program.go +++ b/internal/graphicsdriver/opengl/program.go @@ -17,6 +17,7 @@ package opengl import ( "fmt" "runtime" + "unsafe" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" @@ -122,7 +123,7 @@ type openGLState struct { elementArrayBuffer buffer lastProgram program - lastUniforms map[string][]float32 + lastUniforms map[string][]uint32 lastActiveTexture int } @@ -164,8 +165,8 @@ func (s *openGLState) reset(context *context) error { return nil } -// areSameFloat32Array returns a boolean indicating if a and b are deeply equal. -func areSameFloat32Array(a, b []float32) bool { +// areSameUint32Array returns a boolean indicating if a and b are deeply equal. +func areSameUint32Array(a, b []uint32) bool { if len(a) != len(b) { return false } @@ -179,7 +180,7 @@ func areSameFloat32Array(a, b []float32) bool { type uniformVariable struct { name string - value []float32 + value []uint32 typ shaderir.Type } @@ -230,12 +231,12 @@ func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textu } cached, ok := g.state.lastUniforms[u.name] - if ok && areSameFloat32Array(cached, u.value) { + if ok && areSameUint32Array(cached, u.value) { continue } - g.context.uniformFloats(program, u.name, u.value, u.typ) + g.context.uniformFloats(program, u.name, uint32sToFloat32s(u.value), u.typ) if g.state.lastUniforms == nil { - g.state.lastUniforms = map[string][]float32{} + g.state.lastUniforms = map[string][]uint32{} } g.state.lastUniforms[u.name] = u.value } @@ -279,3 +280,7 @@ loop: return nil } + +func uint32sToFloat32s(s []uint32) []float32 { + return unsafe.Slice((*float32)(unsafe.Pointer(&s[0])), len(s)) +} diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index c33194283..2847ac997 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -65,7 +65,7 @@ func (m *Mipmap) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byt return m.orig.ReadPixels(graphicsDriver, pixels, x, y, width, height) } -func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageCount]*Mipmap, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool, canSkipMipmap bool) { +func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageCount]*Mipmap, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]uint32, evenOdd bool, canSkipMipmap bool) { if len(indices) == 0 { return } diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 5839df15e..c5957d12b 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -80,7 +80,7 @@ type drawTrianglesHistoryItem struct { dstRegion graphicsdriver.Region srcRegion graphicsdriver.Region shader *Shader - uniforms [][]float32 + uniforms [][]uint32 evenOdd bool } @@ -365,7 +365,7 @@ func (i *Image) WritePixels(pixels []byte, x, y, width, height int) { // 5: Color G // 6: Color B // 7: Color Y -func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) { +func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]uint32, evenOdd bool) { if i.priority { panic("restorable: DrawTriangles cannot be called on a priority image") } @@ -403,7 +403,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [g } // appendDrawTrianglesHistory appends a draw-image history item to the image. -func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) { +func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]uint32, evenOdd bool) { if i.stale || !i.needsRestoring() { return } diff --git a/internal/shaderir/program.go b/internal/shaderir/program.go index 371107325..bcd4c63be 100644 --- a/internal/shaderir/program.go +++ b/internal/shaderir/program.go @@ -454,7 +454,7 @@ func (p *Program) reachableUniformVariablesFromBlock(block *Block) []int { return is } -func (p *Program) FilterUniformVariables(uniforms [][]float32) { +func (p *Program) FilterUniformVariables(uniforms [][]uint32) { if p.reachableUniforms == nil { p.reachableUniforms = map[int]struct{}{} for _, i := range p.reachableUniformVariablesFromBlock(p.VertexFunc.Block) { diff --git a/internal/ui/image.go b/internal/ui/image.go index 457d38095..b9f926e11 100644 --- a/internal/ui/image.go +++ b/internal/ui/image.go @@ -75,7 +75,7 @@ func (i *Image) MarkDisposed() { i.drawCallback = nil } -func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool, canSkipMipmap bool, antialias bool) { +func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]uint32, evenOdd bool, canSkipMipmap bool, antialias bool) { if i.drawCallback != nil { i.drawCallback() } diff --git a/internal/ui/shader.go b/internal/ui/shader.go index af3222597..922f9ba16 100644 --- a/internal/ui/shader.go +++ b/internal/ui/shader.go @@ -16,6 +16,7 @@ package ui import ( "fmt" + "math" "strings" "github.com/hajimehoshi/ebiten/v2/internal/mipmap" @@ -44,14 +45,18 @@ func (s *Shader) MarkDisposed() { s.shader = nil } -func (s *Shader) ConvertUniforms(uniforms map[string]any) [][]float32 { - nameToF32s := map[string][]float32{} +func (s *Shader) ConvertUniforms(uniforms map[string]any) [][]uint32 { + nameToU32s := map[string][]uint32{} for name, v := range uniforms { switch v := v.(type) { case float32: - nameToF32s[name] = []float32{v} + nameToU32s[name] = []uint32{math.Float32bits(v)} case []float32: - nameToF32s[name] = v + u32s := make([]uint32, len(v)) + for i := range v { + u32s[i] = math.Float32bits(v[i]) + } + nameToU32s[name] = u32s default: panic(fmt.Sprintf("ebiten: unexpected uniform value type: %s, %T", name, v)) } @@ -72,14 +77,14 @@ func (s *Shader) ConvertUniforms(uniforms map[string]any) [][]float32 { } } - us := make([][]float32, len(s.uniformNameToIndex)) + us := make([][]uint32, len(s.uniformNameToIndex)) for name, idx := range s.uniformNameToIndex { - if v, ok := nameToF32s[name]; ok { + if v, ok := nameToU32s[name]; ok { us[idx] = v continue } t := s.uniformNameToType[name] - us[idx] = make([]float32, t.FloatCount()) + us[idx] = make([]uint32, t.FloatCount()) } // TODO: Panic if uniforms include an invalid name diff --git a/shader.go b/shader.go index 201c305fe..9fc345a18 100644 --- a/shader.go +++ b/shader.go @@ -52,7 +52,7 @@ func (s *Shader) Dispose() { s.shader = nil } -func (s *Shader) convertUniforms(uniforms map[string]any) [][]float32 { +func (s *Shader) convertUniforms(uniforms map[string]any) [][]uint32 { return s.shader.ConvertUniforms(uniforms) }