internal/graphics: use flatten []float32 slice instead of [][]float32

Closes #2479
This commit is contained in:
Hajime Hoshi 2022-12-03 16:11:26 +09:00
parent 9d74784723
commit 0a6813c17f
15 changed files with 225 additions and 294 deletions

View File

@ -252,7 +252,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
useColorM := !colorm.IsIdentity() useColorM := !colorm.IsIdentity()
shader := builtinShader(filter, builtinshader.AddressUnsafe, useColorM) shader := builtinShader(filter, builtinshader.AddressUnsafe, useColorM)
var uniforms [][]uint32 var uniforms []uint32
if useColorM { if useColorM {
var body [16]float32 var body [16]float32
var translation [4]float32 var translation [4]float32
@ -481,7 +481,7 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
useColorM := !colorm.IsIdentity() useColorM := !colorm.IsIdentity()
shader := builtinShader(filter, address, useColorM) shader := builtinShader(filter, address, useColorM)
var uniforms [][]uint32 var uniforms []uint32
if useColorM { if useColorM {
var body [16]float32 var body [16]float32
var translation [4]float32 var translation [4]float32

View File

@ -377,13 +377,13 @@ func (i *Image) processSrc(src *Image) {
// 5: Color G // 5: Color G
// 6: Color B // 6: Color B
// 7: Color Y // 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 [][]uint32, 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() backendsM.Lock()
defer backendsM.Unlock() defer backendsM.Unlock()
i.drawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegion, subimageOffsets, shader, uniforms, evenOdd, false) 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 [][]uint32, 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 { if i.disposed {
panic("atlas: the drawing target image must not be disposed (DrawTriangles)") panic("atlas: the drawing target image must not be disposed (DrawTriangles)")
} }

View File

@ -140,7 +140,7 @@ func (i *Image) WritePixels(pix []byte, x, y, width, height int) {
// DrawTriangles draws the src image with the given vertices. // DrawTriangles draws the src image with the given vertices.
// //
// Copying vertices and indices is the caller's responsibility. // 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 [][]uint32, 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 { for _, src := range srcs {
if i == src { if i == src {
panic("buffered: Image.DrawTriangles: source images must be different from the receiver") panic("buffered: Image.DrawTriangles: source images must be different from the receiver")

View File

@ -71,8 +71,7 @@ type commandQueue struct {
drawTrianglesCommandPool drawTrianglesCommandPool drawTrianglesCommandPool drawTrianglesCommandPool
uint32sBuffer buffer[uint32] uint32sBuffer buffer[uint32]
uint32SlicesBuffer buffer[[]uint32]
} }
// theCommandQueue is the command queue for the current process. // theCommandQueue is the command queue for the current process.
@ -92,7 +91,7 @@ func mustUseDifferentVertexBuffer(nextNumVertexFloats, nextNumIndices int) bool
} }
// EnqueueDrawTrianglesCommand enqueues a drawing-image command. // 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 [][]uint32, 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 { 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)) panic(fmt.Sprintf("graphicscommand: len(indices) must be <= graphics.IndicesCount but not at EnqueueDrawTrianglesCommand: len(indices): %d, graphics.IndicesCount: %d", len(indices), graphics.IndicesCount))
} }
@ -111,7 +110,7 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh
q.tmpNumVertexFloats += len(vertices) q.tmpNumVertexFloats += len(vertices)
q.tmpNumIndices += len(indices) q.tmpNumIndices += len(indices)
uniforms = q.prependPreservedUniforms(uniforms, dst, srcs, offsets, dstRegion, srcRegion) uniforms = q.prependPreservedUniforms(uniforms, shader, dst, srcs, offsets, dstRegion, srcRegion)
// Remove unused uniform variables so that more commands can be merged. // Remove unused uniform variables so that more commands can be merged.
shader.ir.FilterUniformVariables(uniforms) shader.ir.FilterUniformVariables(uniforms)
@ -170,7 +169,6 @@ func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, endFrame bo
}) })
if endFrame { if endFrame {
q.uint32sBuffer.reset() q.uint32sBuffer.reset()
q.uint32SlicesBuffer.reset()
} }
return return
} }
@ -271,7 +269,7 @@ type drawTrianglesCommand struct {
blend graphicsdriver.Blend blend graphicsdriver.Blend
dstRegions []graphicsdriver.DstRegion dstRegions []graphicsdriver.DstRegion
shader *Shader shader *Shader
uniforms [][]uint32 uniforms []uint32
evenOdd bool evenOdd bool
} }
@ -342,7 +340,7 @@ func (c *drawTrianglesCommand) setVertices(vertices []float32) {
// CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged // CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged
// with the drawTrianglesCommand c. // with the drawTrianglesCommand c.
func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageCount]*Image, vertices []float32, blend graphicsdriver.Blend, shader *Shader, uniforms [][]uint32, 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 { if c.shader != shader {
return false return false
} }
@ -350,14 +348,9 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs
return false return false
} }
for i := range c.uniforms { for i := range c.uniforms {
if len(c.uniforms[i]) != len(uniforms[i]) { if c.uniforms[i] != uniforms[i] {
return false return false
} }
for j := range c.uniforms[i] {
if c.uniforms[i][j] != uniforms[i][j] {
return false
}
}
} }
if c.dst != dst { if c.dst != dst {
return false return false
@ -586,39 +579,42 @@ func roundUpPower2(x int) int {
return p2 return p2
} }
func (q *commandQueue) prependPreservedUniforms(uniforms [][]uint32, dst *Image, srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, dstRegion, srcRegion graphicsdriver.Region) [][]uint32 { func (q *commandQueue) prependPreservedUniforms(uniforms []uint32, shader *Shader, dst *Image, srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, dstRegion, srcRegion graphicsdriver.Region) []uint32 {
var n int
for _, typ := range shader.ir.Uniforms[:graphics.PreservedUniformVariablesCount] {
n += typ.Uint32Count()
}
origUniforms := uniforms origUniforms := uniforms
uniforms = q.uint32SlicesBuffer.alloc(len(origUniforms) + graphics.PreservedUniformVariablesCount) uniforms = q.uint32sBuffer.alloc(len(origUniforms) + n)
copy(uniforms[graphics.PreservedUniformVariablesCount:], origUniforms) copy(uniforms[n:], origUniforms)
var idx int
// Set the destination texture size. // Set the destination texture size.
dw, dh := dst.InternalSize() dw, dh := dst.InternalSize()
udstsize := q.uint32sBuffer.alloc(2) uniforms[idx+0] = math.Float32bits(float32(dw))
udstsize[0] = math.Float32bits(float32(dw)) uniforms[idx+1] = math.Float32bits(float32(dh))
udstsize[1] = math.Float32bits(float32(dh)) idx += 2
uniforms[graphics.TextureDestinationSizeUniformVariableIndex] = udstsize
// Set the source texture sizes. // Set the source texture sizes.
usizes := q.uint32sBuffer.alloc(2 * len(srcs))
for i, src := range srcs { for i, src := range srcs {
if src != nil { if src != nil {
w, h := src.InternalSize() w, h := src.InternalSize()
usizes[2*i] = math.Float32bits(float32(w)) uniforms[idx+2*i] = math.Float32bits(float32(w))
usizes[2*i+1] = math.Float32bits(float32(h)) uniforms[idx+2*i+1] = math.Float32bits(float32(h))
} }
} }
uniforms[graphics.TextureSourceSizesUniformVariableIndex] = usizes idx += len(srcs) * 2
// Set the destination region. // Set the destination region.
udstrorig := q.uint32sBuffer.alloc(2) uniforms[idx+0] = math.Float32bits(float32(dstRegion.X) / float32(dw))
udstrorig[0] = math.Float32bits(float32(dstRegion.X) / float32(dw)) uniforms[idx+1] = math.Float32bits(float32(dstRegion.Y) / float32(dh))
udstrorig[1] = math.Float32bits(float32(dstRegion.Y) / float32(dh)) idx += 2
uniforms[graphics.TextureDestinationRegionOriginUniformVariableIndex] = udstrorig
udstrsize := q.uint32sBuffer.alloc(2) uniforms[idx+0] = math.Float32bits(float32(dstRegion.Width) / float32(dw))
udstrsize[0] = math.Float32bits(float32(dstRegion.Width) / float32(dw)) uniforms[idx+1] = math.Float32bits(float32(dstRegion.Height) / float32(dh))
udstrsize[1] = math.Float32bits(float32(dstRegion.Height) / float32(dh)) idx += 2
uniforms[graphics.TextureDestinationRegionSizeUniformVariableIndex] = udstrsize
if srcs[0] != nil { if srcs[0] != nil {
w, h := srcs[0].InternalSize() w, h := srcs[0].InternalSize()
@ -633,42 +629,38 @@ func (q *commandQueue) prependPreservedUniforms(uniforms [][]uint32, dst *Image,
} }
// Set the source offsets. // Set the source offsets.
uoffsets := q.uint32sBuffer.alloc(2 * len(offsets))
for i, offset := range offsets { for i, offset := range offsets {
uoffsets[2*i] = math.Float32bits(offset[0]) uniforms[idx+2*i] = math.Float32bits(offset[0])
uoffsets[2*i+1] = math.Float32bits(offset[1]) uniforms[idx+2*i+1] = math.Float32bits(offset[1])
} }
uniforms[graphics.TextureSourceOffsetsUniformVariableIndex] = uoffsets idx += len(offsets) * 2
// Set the source region of texture0. // Set the source region of texture0.
usrcrorig := q.uint32sBuffer.alloc(2) uniforms[idx+0] = math.Float32bits(float32(srcRegion.X))
usrcrorig[0] = math.Float32bits(float32(srcRegion.X)) uniforms[idx+1] = math.Float32bits(float32(srcRegion.Y))
usrcrorig[1] = math.Float32bits(float32(srcRegion.Y)) idx += 2
uniforms[graphics.TextureSourceRegionOriginUniformVariableIndex] = usrcrorig
usrcrsize := q.uint32sBuffer.alloc(2) uniforms[idx+0] = math.Float32bits(float32(srcRegion.Width))
usrcrsize[0] = math.Float32bits(float32(srcRegion.Width)) uniforms[idx+1] = math.Float32bits(float32(srcRegion.Height))
usrcrsize[1] = math.Float32bits(float32(srcRegion.Height)) idx += 2
uniforms[graphics.TextureSourceRegionSizeUniformVariableIndex] = usrcrsize
umatrix := q.uint32sBuffer.alloc(16) uniforms[idx+0] = math.Float32bits(2 / float32(dw))
umatrix[0] = math.Float32bits(2 / float32(dw)) uniforms[idx+1] = 0
umatrix[1] = 0 uniforms[idx+2] = 0
umatrix[2] = 0 uniforms[idx+3] = 0
umatrix[3] = 0 uniforms[idx+4] = 0
umatrix[4] = 0 uniforms[idx+5] = math.Float32bits(2 / float32(dh))
umatrix[5] = math.Float32bits(2 / float32(dh)) uniforms[idx+6] = 0
umatrix[6] = 0 uniforms[idx+7] = 0
umatrix[7] = 0 uniforms[idx+8] = 0
umatrix[8] = 0 uniforms[idx+9] = 0
umatrix[9] = 0 uniforms[idx+10] = math.Float32bits(1)
umatrix[10] = math.Float32bits(1) uniforms[idx+11] = 0
umatrix[11] = 0 uniforms[idx+12] = math.Float32bits(-1)
umatrix[12] = math.Float32bits(-1) uniforms[idx+13] = math.Float32bits(-1)
umatrix[13] = math.Float32bits(-1) uniforms[idx+14] = 0
umatrix[14] = 0 uniforms[idx+15] = math.Float32bits(1)
umatrix[15] = math.Float32bits(1) idx += 16
uniforms[graphics.ProjectionMatrixUniformVariableIndex] = umatrix
return uniforms return uniforms
} }

View File

@ -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 // 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. // 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 [][]uint32, 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 { for _, src := range srcs {
if src == nil { if src == nil {
continue continue

View File

@ -1188,7 +1188,7 @@ func (g *Graphics) NewShader(program *shaderir.Program) (graphicsdriver.Shader,
return s, nil 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 [][]uint32, 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 { if shaderID == graphicsdriver.InvalidShaderID {
return fmt.Errorf("directx: shader ID is invalid") return fmt.Errorf("directx: shader ID is invalid")
} }
@ -1236,17 +1236,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.Sh
} }
shader := g.shaders[shaderID] shader := g.shaders[shaderID]
adjustedUniforms := shader.adjustUniforms(uniforms, shader)
// 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
// Invert the sign bits as float32 values.
uniforms[idx][1] ^= 1 << 31
uniforms[idx][5] ^= 1 << 31
uniforms[idx][9] ^= 1 << 31
uniforms[idx][13] ^= 1 << 31
flattenUniforms := shader.flattenUniforms(uniforms)
w, h := dst.internalSize() w, h := dst.internalSize()
g.needFlushDrawCommandList = true g.needFlushDrawCommandList = true
@ -1274,7 +1264,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.Sh
Format: _DXGI_FORMAT_R16_UINT, Format: _DXGI_FORMAT_R16_UINT,
}) })
if err := g.pipelineStates.drawTriangles(g.device, g.drawCommandList, g.frameIndex, dst.screen, srcImages, shader, dstRegions, flattenUniforms, blend, indexOffset, evenOdd); err != nil { if err := g.pipelineStates.drawTriangles(g.device, g.drawCommandList, g.frameIndex, dst.screen, srcImages, shader, dstRegions, adjustedUniforms, blend, indexOffset, evenOdd); err != nil {
return err return err
} }
@ -1734,191 +1724,130 @@ func (s *Shader) pipelineState(blend graphicsdriver.Blend, stencilMode stencilMo
return state, nil return state, nil
} }
func (s *Shader) flattenUniforms(uniforms [][]uint32) []uint32 { func (s *Shader) adjustUniforms(uniforms []uint32, shader *Shader) []uint32 {
var fs []uint32 var fs []uint32
for i, u := range uniforms { var idx int
for i, typ := range shader.uniformTypes {
if len(fs) < s.uniformOffsets[i]/4 { if len(fs) < s.uniformOffsets[i]/4 {
fs = append(fs, make([]uint32, s.uniformOffsets[i]/4-len(fs))...) fs = append(fs, make([]uint32, s.uniformOffsets[i]/4-len(fs))...)
} }
t := s.uniformTypes[i] n := typ.Uint32Count()
switch t.Main { switch typ.Main {
case shaderir.Float: case shaderir.Float:
if u != nil { fs = append(fs, uniforms[idx:idx+1]...)
fs = append(fs, u...)
} else {
fs = append(fs, 0)
}
case shaderir.Int: case shaderir.Int:
if u != nil { fs = append(fs, uniforms[idx:idx+1]...)
fs = append(fs, u...)
} else {
fs = append(fs, 0)
}
case shaderir.Vec2, shaderir.IVec2: case shaderir.Vec2, shaderir.IVec2:
if u != nil { fs = append(fs, uniforms[idx:idx+2]...)
fs = append(fs, u...)
} else {
fs = append(fs, 0, 0)
}
case shaderir.Vec3, shaderir.IVec3: case shaderir.Vec3, shaderir.IVec3:
if u != nil { fs = append(fs, uniforms[idx:idx+3]...)
fs = append(fs, u...)
} else {
fs = append(fs, 0, 0, 0)
}
case shaderir.Vec4, shaderir.IVec4: case shaderir.Vec4, shaderir.IVec4:
if u != nil { fs = append(fs, uniforms[idx:idx+4]...)
fs = append(fs, u...)
} else {
fs = append(fs, 0, 0, 0, 0)
}
case shaderir.Mat2: case shaderir.Mat2:
if u != nil { fs = append(fs,
fs = append(fs, uniforms[idx+0], uniforms[idx+2], 0, 0,
u[0], u[2], 0, 0, uniforms[idx+1], uniforms[idx+3],
u[1], u[3], )
)
} else {
fs = append(fs,
0, 0, 0, 0,
0, 0,
)
}
case shaderir.Mat3: case shaderir.Mat3:
if u != nil { fs = append(fs,
fs = append(fs, uniforms[idx+0], uniforms[idx+3], uniforms[idx+6], 0,
u[0], u[3], u[6], 0, uniforms[idx+1], uniforms[idx+4], uniforms[idx+7], 0,
u[1], u[4], u[7], 0, uniforms[idx+2], uniforms[idx+5], uniforms[idx+8],
u[2], u[5], u[8], )
)
} else {
fs = append(fs,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0,
)
}
case shaderir.Mat4: case shaderir.Mat4:
if u != nil { if i == graphics.ProjectionMatrixUniformVariableIndex {
// 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.
// Invert the sign bits as float32 values.
fs = append(fs, fs = append(fs,
u[0], u[4], u[8], u[12], uniforms[idx+0], uniforms[idx+4], uniforms[idx+8], uniforms[idx+12],
u[1], u[5], u[9], u[13], uniforms[idx+1]^(1<<31), uniforms[idx+5]^(1<<31), uniforms[idx+9]^(1<<31), uniforms[idx+13]^(1<<31),
u[2], u[6], u[10], u[14], uniforms[idx+2], uniforms[idx+6], uniforms[idx+10], uniforms[idx+14],
u[3], u[7], u[11], u[15], uniforms[idx+3], uniforms[idx+7], uniforms[idx+11], uniforms[idx+15],
) )
} else { } else {
fs = append(fs, fs = append(fs,
0, 0, 0, 0, uniforms[idx+0], uniforms[idx+4], uniforms[idx+8], uniforms[idx+12],
0, 0, 0, 0, uniforms[idx+1], uniforms[idx+5], uniforms[idx+9], uniforms[idx+13],
0, 0, 0, 0, uniforms[idx+2], uniforms[idx+6], uniforms[idx+10], uniforms[idx+14],
0, 0, 0, 0, uniforms[idx+3], uniforms[idx+7], uniforms[idx+11], uniforms[idx+15],
) )
} }
case shaderir.Array: case shaderir.Array:
// Each element is aligned to the boundary. // Each element is aligned to the boundary.
switch t.Sub[0].Main { switch typ.Sub[0].Main {
case shaderir.Float: case shaderir.Float:
if u != nil { for j := 0; j < typ.Length; j++ {
for j := 0; j < t.Length; j++ { fs = append(fs, uniforms[idx+j])
fs = append(fs, u[j]) if j < typ.Length-1 {
if j < t.Length-1 { fs = append(fs, 0, 0, 0)
fs = append(fs, 0, 0, 0)
}
} }
} else {
fs = append(fs, make([]uint32, (t.Length-1)*4+1)...)
} }
case shaderir.Int: case shaderir.Int:
if u != nil { for j := 0; j < typ.Length; j++ {
for j := 0; j < t.Length; j++ { fs = append(fs, uniforms[idx+j])
fs = append(fs, u[j]) if j < typ.Length-1 {
if j < t.Length-1 { fs = append(fs, 0, 0, 0)
fs = append(fs, 0, 0, 0)
}
} }
} else {
fs = append(fs, make([]uint32, (t.Length-1)*4+1)...)
} }
case shaderir.Vec2, shaderir.IVec2: case shaderir.Vec2, shaderir.IVec2:
if u != nil { for j := 0; j < typ.Length; j++ {
for j := 0; j < t.Length; j++ { fs = append(fs, uniforms[idx+2*j:idx+2*(j+1)]...)
fs = append(fs, u[2*j:2*(j+1)]...) if j < typ.Length-1 {
if j < t.Length-1 { fs = append(fs, 0, 0)
fs = append(fs, 0, 0)
}
} }
} else {
fs = append(fs, make([]uint32, (t.Length-1)*4+2)...)
} }
case shaderir.Vec3, shaderir.IVec3: case shaderir.Vec3, shaderir.IVec3:
if u != nil { for j := 0; j < typ.Length; j++ {
for j := 0; j < t.Length; j++ { fs = append(fs, uniforms[idx+3*j:idx+3*(j+1)]...)
fs = append(fs, u[3*j:3*(j+1)]...) if j < typ.Length-1 {
if j < t.Length-1 { fs = append(fs, 0)
fs = append(fs, 0)
}
} }
} else {
fs = append(fs, make([]uint32, (t.Length-1)*4+3)...)
} }
case shaderir.Vec4, shaderir.IVec4: case shaderir.Vec4, shaderir.IVec4:
if u != nil { fs = append(fs, uniforms[idx:idx+4*typ.Length]...)
fs = append(fs, u...)
} else {
fs = append(fs, make([]uint32, t.Length*4)...)
}
case shaderir.Mat2: case shaderir.Mat2:
if u != nil { for j := 0; j < typ.Length; j++ {
for j := 0; j < t.Length; j++ { u := uniforms[idx+4*j : idx+4*(j+1)]
u1 := u[4*j : 4*(j+1)] fs = append(fs,
fs = append(fs, u[0], u[2], 0, 0,
u1[0], u1[2], 0, 0, u[1], u[3], 0, 0,
u1[1], u1[3], 0, 0, )
) }
} if typ.Length > 0 {
if t.Length > 0 { fs = fs[:len(fs)-2]
fs = fs[:len(fs)-2]
}
} else {
fs = append(fs, make([]uint32, (t.Length-1)*8+6)...)
} }
case shaderir.Mat3: case shaderir.Mat3:
if u != nil { for j := 0; j < typ.Length; j++ {
for j := 0; j < t.Length; j++ { u := uniforms[idx+9*j : idx+9*(j+1)]
u1 := u[9*j : 9*(j+1)] fs = append(fs,
fs = append(fs, u[0], u[3], u[6], 0,
u1[0], u1[3], u1[6], 0, u[1], u[4], u[7], 0,
u1[1], u1[4], u1[7], 0, u[2], u[5], u[8], 0,
u1[2], u1[5], u1[8], 0, )
) }
} if typ.Length > 0 {
if t.Length > 0 { fs = fs[:len(fs)-1]
fs = fs[:len(fs)-1]
}
} else {
fs = append(fs, make([]uint32, (t.Length-1)*12+11)...)
} }
case shaderir.Mat4: case shaderir.Mat4:
if u != nil { for j := 0; j < typ.Length; j++ {
for j := 0; j < t.Length; j++ { u := uniforms[idx+16*j : idx+16*(j+1)]
u1 := u[16*j : 16*(j+1)] fs = append(fs,
fs = append(fs, u[0], u[4], u[8], u[12],
u1[0], u1[4], u1[8], u1[12], u[1], u[5], u[9], u[13],
u1[1], u1[5], u1[9], u1[13], u[2], u[6], u[10], u[14],
u1[2], u1[6], u1[10], u1[14], u[3], u[7], u[11], u[15],
u1[3], u1[7], u1[11], u1[15], )
)
}
} else {
fs = append(fs, make([]uint32, t.Length*16)...)
} }
default: default:
panic(fmt.Sprintf("directx: not implemented type for uniform variables: %s", t.String())) panic(fmt.Sprintf("directx: not implemented type for uniform variables: %s", typ.String()))
} }
default: default:
panic(fmt.Sprintf("directx: not implemented type for uniform variables: %s", t.String())) panic(fmt.Sprintf("directx: not implemented type for uniform variables: %s", typ.String()))
} }
idx += n
} }
return fs return fs
} }

View File

@ -55,7 +55,7 @@ type Graphics interface {
NewShader(program *shaderir.Program) (Shader, error) NewShader(program *shaderir.Program) (Shader, error)
// DrawTriangles draws an image onto another image with the given parameters. // 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 [][]uint32, evenOdd bool) error DrawTriangles(dst ImageID, srcs [graphics.ShaderImageCount]ImageID, shader ShaderID, dstRegions []DstRegion, indexOffset int, blend Blend, uniforms []uint32, evenOdd bool) error
} }
type Image interface { type Image interface {

View File

@ -544,7 +544,7 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
return nil 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 [][]uint32, 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 { if shaderID == graphicsdriver.InvalidShaderID {
return fmt.Errorf("metal: shader ID is invalid") return fmt.Errorf("metal: shader ID is invalid")
} }
@ -560,33 +560,35 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
srcs[i] = g.images[srcID] srcs[i] = g.images[srcID]
} }
uniformVars := make([][]uint32, len(uniforms)) uniformVars := make([][]uint32, len(g.shaders[shaderID].ir.Uniforms))
// Set the additional uniform variables. // Set the additional uniform variables.
for i, v := range uniforms { var idx int
for i, t := range g.shaders[shaderID].ir.Uniforms {
if i == graphics.ProjectionMatrixUniformVariableIndex { if i == graphics.ProjectionMatrixUniformVariableIndex {
// In Metal, the NDC's Y direction (upward) and the framebuffer's Y direction (downward) don't // 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. // match. Then, the Y direction must be inverted.
// Invert the sign bits as float32 values. // Invert the sign bits as float32 values.
v[1] ^= 1 << 31 uniforms[idx+1] ^= 1 << 31
v[5] ^= 1 << 31 uniforms[idx+5] ^= 1 << 31
v[9] ^= 1 << 31 uniforms[idx+9] ^= 1 << 31
v[13] ^= 1 << 31 uniforms[idx+13] ^= 1 << 31
} }
t := g.shaders[shaderID].ir.Uniforms[i] n := t.Uint32Count()
switch t.Main { switch t.Main {
case shaderir.Vec3, shaderir.IVec3: case shaderir.Vec3, shaderir.IVec3:
// float3 requires 16-byte alignment (#2463). // float3 requires 16-byte alignment (#2463).
v1 := make([]uint32, 4) v1 := make([]uint32, 4)
copy(v1[0:3], v[0:3]) copy(v1[0:3], uniforms[idx:idx+3])
uniformVars[i] = v1 uniformVars[i] = v1
case shaderir.Mat3: case shaderir.Mat3:
// float3x3 requires 16-byte alignment (#2036). // float3x3 requires 16-byte alignment (#2036).
v1 := make([]uint32, 12) v1 := make([]uint32, 12)
copy(v1[0:3], v[0:3]) copy(v1[0:3], uniforms[idx:idx+3])
copy(v1[4:7], v[3:6]) copy(v1[4:7], uniforms[idx+3:idx+6])
copy(v1[8:11], v[6:9]) copy(v1[8:11], uniforms[idx+6:idx+9])
uniformVars[i] = v1 uniformVars[i] = v1
case shaderir.Array: case shaderir.Array:
switch t.Sub[0].Main { switch t.Sub[0].Main {
@ -595,7 +597,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
for j := 0; j < t.Length; j++ { for j := 0; j < t.Length; j++ {
offset0 := j * 3 offset0 := j * 3
offset1 := j * 4 offset1 := j * 4
copy(v1[offset1:offset1+3], v[offset0:offset0+3]) copy(v1[offset1:offset1+3], uniforms[idx+offset0:idx+offset0+3])
} }
uniformVars[i] = v1 uniformVars[i] = v1
case shaderir.Mat3: case shaderir.Mat3:
@ -603,17 +605,19 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
for j := 0; j < t.Length; j++ { for j := 0; j < t.Length; j++ {
offset0 := j * 9 offset0 := j * 9
offset1 := j * 12 offset1 := j * 12
copy(v1[offset1:offset1+3], v[offset0:offset0+3]) copy(v1[offset1:offset1+3], uniforms[idx+offset0:idx+offset0+3])
copy(v1[offset1+4:offset1+7], v[offset0+3:offset0+6]) copy(v1[offset1+4:offset1+7], uniforms[idx+offset0+3:idx+offset0+6])
copy(v1[offset1+8:offset1+11], v[offset0+6:offset0+9]) copy(v1[offset1+8:offset1+11], uniforms[idx+offset0+6:idx+offset0+9])
} }
uniformVars[i] = v1 uniformVars[i] = v1
default: default:
uniformVars[i] = v uniformVars[i] = uniforms[idx : idx+n]
} }
default: default:
uniformVars[i] = v uniformVars[i] = uniforms[idx : idx+n]
} }
idx += n
} }
if err := g.draw(dst, dstRegions, srcs, indexOffset, g.shaders[shaderID], uniformVars, blend, evenOdd); err != nil { if err := g.draw(dst, dstRegions, srcs, indexOffset, g.shaders[shaderID], uniformVars, blend, evenOdd); err != nil {

View File

@ -170,7 +170,7 @@ func (g *Graphics) uniformVariableName(idx int) string {
return name 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 [][]uint32, 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 { if shaderID == graphicsdriver.InvalidShaderID {
return fmt.Errorf("opengl: shader ID is invalid") return fmt.Errorf("opengl: shader ID is invalid")
} }
@ -187,17 +187,20 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
shader := g.shaders[shaderID] shader := g.shaders[shaderID]
program := shader.p program := shader.p
ulen := len(uniforms) ulen := len(shader.ir.Uniforms)
if cap(g.uniformVars) < ulen { if cap(g.uniformVars) < ulen {
g.uniformVars = make([]uniformVariable, ulen) g.uniformVars = make([]uniformVariable, ulen)
} else { } else {
g.uniformVars = g.uniformVars[:ulen] g.uniformVars = g.uniformVars[:ulen]
} }
for i, v := range uniforms { var idx int
for i, typ := range shader.ir.Uniforms {
n := typ.Uint32Count()
g.uniformVars[i].name = g.uniformVariableName(i) g.uniformVars[i].name = g.uniformVariableName(i)
g.uniformVars[i].value = v g.uniformVars[i].value = uniforms[idx : idx+n]
g.uniformVars[i].typ = shader.ir.Uniforms[i] g.uniformVars[i].typ = typ
idx += n
} }
// In OpenGL, the NDC's Y direction is upward, so flip the Y direction for the final framebuffer. // In OpenGL, the NDC's Y direction is upward, so flip the Y direction for the final framebuffer.

View File

@ -65,7 +65,7 @@ func (m *Mipmap) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byt
return m.orig.ReadPixels(graphicsDriver, pixels, x, y, width, height) 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 [][]uint32, 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 { if len(indices) == 0 {
return return
} }

View File

@ -80,7 +80,7 @@ type drawTrianglesHistoryItem struct {
dstRegion graphicsdriver.Region dstRegion graphicsdriver.Region
srcRegion graphicsdriver.Region srcRegion graphicsdriver.Region
shader *Shader shader *Shader
uniforms [][]uint32 uniforms []uint32
evenOdd bool evenOdd bool
} }
@ -365,7 +365,7 @@ func (i *Image) WritePixels(pixels []byte, x, y, width, height int) {
// 5: Color G // 5: Color G
// 6: Color B // 6: Color B
// 7: Color Y // 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 [][]uint32, 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 { if i.priority {
panic("restorable: DrawTriangles cannot be called on a priority image") 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. // 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 [][]uint32, 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() { if i.stale || !i.needsRestoring() {
return return
} }

View File

@ -462,7 +462,7 @@ func (p *Program) reachableUniformVariablesFromBlock(block *Block) []int {
// FilterUniformVariables replaces uniform variables with nil when they are not used. // FilterUniformVariables replaces uniform variables with nil when they are not used.
// By minimizing uniform variables, more commands can be merged in the graphicscommand package. // By minimizing uniform variables, more commands can be merged in the graphicscommand package.
func (p *Program) FilterUniformVariables(uniforms [][]uint32) { func (p *Program) FilterUniformVariables(uniforms []uint32) {
if p.reachableUniforms == nil { if p.reachableUniforms == nil {
p.reachableUniforms = make([]bool, len(p.Uniforms)) p.reachableUniforms = make([]bool, len(p.Uniforms))
for _, i := range p.reachableUniformVariablesFromBlock(p.VertexFunc.Block) { for _, i := range p.reachableUniformVariablesFromBlock(p.VertexFunc.Block) {
@ -472,9 +472,15 @@ func (p *Program) FilterUniformVariables(uniforms [][]uint32) {
p.reachableUniforms[i] = true p.reachableUniforms[i] = true
} }
} }
for i := range uniforms {
var idx int
for i, typ := range p.Uniforms {
n := typ.Uint32Count()
if !p.reachableUniforms[i] { if !p.reachableUniforms[i] {
uniforms[i] = nil for j := 0; j < n; j++ {
uniforms[idx+j] = 0
}
} }
idx += n
} }
} }

View File

@ -81,7 +81,7 @@ func (i *Image) MarkDisposed() {
i.drawCallback = nil 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 [][]uint32, 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 { if i.drawCallback != nil {
i.drawCallback() i.drawCallback()
} }

View File

@ -44,55 +44,52 @@ func (s *Shader) MarkDisposed() {
s.shader = nil s.shader = nil
} }
func (s *Shader) ConvertUniforms(uniforms map[string]any) [][]uint32 { func (s *Shader) ConvertUniforms(uniforms map[string]any) []uint32 {
idxToU32s := make([][]uint32, len(s.uniformNames)) var n int
for idx, name := range s.uniformNames[graphics.PreservedUniformVariablesCount:] { for _, typ := range s.uniformTypes[graphics.PreservedUniformVariablesCount:] {
uv, ok := uniforms[name] n += typ.Uint32Count()
if !ok { }
// TODO: Panic if uniforms include an invalid name
continue
}
v := reflect.ValueOf(uv) us := make([]uint32, n)
t := v.Type() var idx int
switch t.Kind() { for i, name := range s.uniformNames[graphics.PreservedUniformVariablesCount:] {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: typ := s.uniformTypes[graphics.PreservedUniformVariablesCount+i]
idxToU32s[idx] = []uint32{uint32(v.Int())}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: if uv, ok := uniforms[name]; ok {
idxToU32s[idx] = []uint32{uint32(v.Uint())} // TODO: Panic if uniforms include an invalid name
case reflect.Float32, reflect.Float64: v := reflect.ValueOf(uv)
idxToU32s[idx] = []uint32{math.Float32bits(float32(v.Float()))} t := v.Type()
case reflect.Slice, reflect.Array: switch t.Kind() {
u32s := make([]uint32, v.Len())
switch t.Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
for i := range u32s { us[idx] = uint32(v.Int())
u32s[i] = uint32(v.Index(i).Int())
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
for i := range u32s { us[idx] = uint32(v.Uint())
u32s[i] = uint32(v.Index(i).Uint())
}
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
for i := range u32s { us[idx] = math.Float32bits(float32(v.Float()))
u32s[i] = math.Float32bits(float32(v.Index(i).Float())) case reflect.Slice, reflect.Array:
l := v.Len()
switch t.Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
for i := 0; i < l; i++ {
us[idx+i] = uint32(v.Index(i).Int())
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
for i := 0; i < l; i++ {
us[idx+i] = uint32(v.Index(i).Uint())
}
case reflect.Float32, reflect.Float64:
for i := 0; i < l; i++ {
us[idx+i] = math.Float32bits(float32(v.Index(i).Float()))
}
default:
panic(fmt.Sprintf("ebiten: unexpected uniform value type: %s (%s)", name, v.Kind().String()))
} }
default: default:
panic(fmt.Sprintf("ebiten: unexpected uniform value type: %s (%s)", name, v.Kind().String())) panic(fmt.Sprintf("ebiten: unexpected uniform value type: %s (%s)", name, v.Kind().String()))
} }
idxToU32s[idx] = u32s
default:
panic(fmt.Sprintf("ebiten: unexpected uniform value type: %s (%s)", name, v.Kind().String()))
} }
}
us := make([][]uint32, len(s.uniformTypes)-graphics.PreservedUniformVariablesCount) idx += typ.Uint32Count()
for idx, typ := range s.uniformTypes[graphics.PreservedUniformVariablesCount:] {
v := idxToU32s[idx]
if v == nil {
v = make([]uint32, typ.Uint32Count())
}
us[idx] = v
} }
return us return us

View File

@ -52,7 +52,7 @@ func (s *Shader) Dispose() {
s.shader = nil s.shader = nil
} }
func (s *Shader) convertUniforms(uniforms map[string]any) [][]uint32 { func (s *Shader) convertUniforms(uniforms map[string]any) []uint32 {
return s.shader.ConvertUniforms(uniforms) return s.shader.ConvertUniforms(uniforms)
} }