Compare commits

...

3 Commits

Author SHA1 Message Date
Hajime Hoshi
7d3007a43e internal/graphicsdriver/metal: bug fix: compilation failure 2024-12-07 23:15:13 +09:00
Hajime Hoshi
103b3fe11e internal/graphicsdriver/playstation5: add ebitengine_ProjectionMatrixUniformDwordIndex
This is a necessary information to treat the projection matrix correctly.

This change also updates the header file to avoid duplicated symbols
for constant variables.
2024-12-07 23:04:52 +09:00
Hajime Hoshi
beac278c59 internal/grahics, internal/shadeir: use the term Dword instead of Uint32
This change also renames DWord to Dword.
2024-12-07 22:26:09 +09:00
17 changed files with 104 additions and 86 deletions

View File

@ -29,13 +29,20 @@ const (
ProjectionMatrixUniformVariableIndex = 6
PreservedUniformUint32Count = 2 + // the destination texture size
PreservedUniformDwordCount = 2 + // the destination texture size
2*ShaderSrcImageCount + // the source texture sizes array
2 + // the destination image region origin
2 + // the destination image region size
2*ShaderSrcImageCount + // the source image region origins array
2*ShaderSrcImageCount + // the source image region sizes array
16 // the projection matrix
ProjectionMatrixUniformDwordIndex = 2 +
2*ShaderSrcImageCount +
2 +
2 +
2*ShaderSrcImageCount +
2*ShaderSrcImageCount
)
const (

View File

@ -23,7 +23,7 @@ import (
)
func BenchmarkPrependPreservedUniforms(b *testing.B) {
var uniforms [graphics.PreservedUniformUint32Count]uint32
var uniforms [graphics.PreservedUniformDwordCount]uint32
dst := graphicscommand.NewImage(16, 16, false, "")
src := graphicscommand.NewImage(16, 16, false, "")
dr := image.Rect(0, 0, 16, 16)

View File

@ -347,15 +347,15 @@ func imageRectangleToRectangleF32(r image.Rectangle) rectangleF32 {
func (q *commandQueue) prependPreservedUniforms(uniforms []uint32, shader *Shader, dst *Image, srcs [graphics.ShaderSrcImageCount]*Image, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle) []uint32 {
origUniforms := uniforms
uniforms = q.uint32sBuffer.alloc(len(origUniforms) + graphics.PreservedUniformUint32Count)
copy(uniforms[graphics.PreservedUniformUint32Count:], origUniforms)
uniforms = q.uint32sBuffer.alloc(len(origUniforms) + graphics.PreservedUniformDwordCount)
copy(uniforms[graphics.PreservedUniformDwordCount:], origUniforms)
return prependPreservedUniforms(uniforms, shader, dst, srcs, dstRegion, srcRegions)
}
func prependPreservedUniforms(uniforms []uint32, shader *Shader, dst *Image, srcs [graphics.ShaderSrcImageCount]*Image, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle) []uint32 {
// Set the destination texture size.
// Hard-code indices for BCE optimization.
_ = uniforms[graphics.PreservedUniformUint32Count-1]
_ = uniforms[graphics.PreservedUniformDwordCount-1]
dw, dh := dst.InternalSize()
uniforms[0] = math.Float32bits(float32(dw))
@ -468,8 +468,8 @@ func prependPreservedUniforms(uniforms []uint32, shader *Shader, dst *Image, src
return uniforms
}
// Confirm the concrete value of graphics.PreservedUniformUint32Count.
var _ [0]struct{} = [graphics.PreservedUniformUint32Count - 46]struct{}{}
// Confirm the concrete value of graphics.PreservedUniformDwordCount.
var _ [0]struct{} = [graphics.PreservedUniformDwordCount - 46]struct{}{}
type commandQueuePool struct {
cache []*commandQueue

View File

@ -513,7 +513,7 @@ func (g *graphics11) NewShader(program *shaderir.Program) (graphicsdriver.Shader
graphics: g,
id: g.genNextShaderID(),
uniformTypes: program.Uniforms,
uniformOffsets: hlsl.UniformVariableOffsetsInDWords(program),
uniformOffsets: hlsl.UniformVariableOffsetsInDwords(program),
vertexShaderBlob: vsh,
pixelShaderBlob: psh,
}

View File

@ -1073,7 +1073,7 @@ func (g *graphics12) NewShader(program *shaderir.Program) (graphicsdriver.Shader
graphics: g,
id: g.genNextShaderID(),
uniformTypes: program.Uniforms,
uniformOffsets: hlsl.UniformVariableOffsetsInDWords(program),
uniformOffsets: hlsl.UniformVariableOffsetsInDwords(program),
vertexShader: vsh,
pixelShader: psh,
}

View File

@ -209,7 +209,7 @@ func adjustUniforms(uniformTypes []shaderir.Type, uniformOffsets []int, uniforms
fs = append(fs, make([]uint32, uniformOffsets[i]-len(fs))...)
}
n := typ.Uint32Count()
n := typ.DwordCount()
switch typ.Main {
case shaderir.Float:
fs = append(fs, uniforms[idx:idx+1]...)

View File

@ -882,7 +882,7 @@ func adjustUniformVariablesLayout(uniformTypes []shaderir.Type, uniforms []uint3
var idx int
for i, typ := range uniformTypes {
n := typ.Uint32Count()
n := typ.DwordCount()
switch typ.Main {
case shaderir.Float, shaderir.Int:
values = append(values, uniforms[idx:idx+n]...)

View File

@ -224,7 +224,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
var idx int
for i, typ := range shader.ir.Uniforms {
n := typ.Uint32Count()
n := typ.DwordCount()
g.uniformVars[i].name = g.uniformVariableName(i)
g.uniformVars[i].value = uniforms[idx : idx+n]
g.uniformVars[i].typ = typ

View File

@ -292,7 +292,7 @@ func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textu
if u.value == nil {
continue
}
if got, expected := len(u.value), u.typ.Uint32Count(); got != expected {
if got, expected := len(u.value), u.typ.DwordCount(); got != expected {
// Copy a shaderir.Type value once. Do not pass u.typ directly to fmt.Errorf arguments, or
// the value u would be allocated on heap.
typ := u.typ

View File

@ -30,6 +30,11 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)
//export ebitengine_ProjectionMatrixUniformDwordIndex
func ebitengine_ProjectionMatrixUniformDwordIndex() C.int {
return C.int(graphics.ProjectionMatrixUniformDwordIndex)
}
type playstation5Error struct {
name string
code int

View File

@ -25,6 +25,8 @@
extern "C" {
#endif
int ebitengine_ProjectionMatrixUniformDwordIndex();
typedef struct ebitengine_Error {
const char *message;
int code;
@ -52,23 +54,27 @@ typedef struct ebitengine_DstRegion {
// kBlendFactor* and kBlendOperation* must be synced with
// internal/graphicsdriver/blend.go.
const uint8_t kBlendFactorZero = 0;
const uint8_t kBlendFactorOne = 1;
const uint8_t kBlendFactorSourceColor = 2;
const uint8_t kBlendFactorOneMinusSourceColor = 3;
const uint8_t kBlendFactorSourceAlpha = 4;
const uint8_t kBlendFactorOneMinusSourceAlpha = 5;
const uint8_t kBlendFactorDestinationColor = 6;
const uint8_t kBlendFactorOneMinusDestinationColor = 7;
const uint8_t kBlendFactorDestinationAlpha = 8;
const uint8_t kBlendFactorOneMinusDestinationAlpha = 9;
const uint8_t kBlendFactorSourceAlphaSaturated = 10;
enum {
kBlendFactorZero = 0,
kBlendFactorOne = 1,
kBlendFactorSourceColor = 2,
kBlendFactorOneMinusSourceColor = 3,
kBlendFactorSourceAlpha = 4,
kBlendFactorOneMinusSourceAlpha = 5,
kBlendFactorDestinationColor = 6,
kBlendFactorOneMinusDestinationColor = 7,
kBlendFactorDestinationAlpha = 8,
kBlendFactorOneMinusDestinationAlpha = 9,
kBlendFactorSourceAlphaSaturated = 10,
};
const uint8_t kBlendOperationAdd = 0;
const uint8_t kBlendOperationSubtract = 1;
const uint8_t kBlendOperationReverseSubtract = 2;
const uint8_t kBlendOperationMin = 3;
const uint8_t kBlendOperationMax = 4;
enum {
kBlendOperationAdd = 0,
kBlendOperationSubtract = 1,
kBlendOperationReverseSubtract = 2,
kBlendOperationMin = 3,
kBlendOperationMax = 4,
};
typedef struct ebitengine_Blend {
uint8_t factor_src_rgb;

View File

@ -27,7 +27,7 @@ func BenchmarkFilter(b *testing.B) {
if err != nil {
b.Fatal(err)
}
uniforms := make([]uint32, graphics.PreservedUniformUint32Count)
uniforms := make([]uint32, graphics.PreservedUniformDwordCount)
for i := 0; i < b.N; i++ {
s.FilterUniformVariables(uniforms)
}

View File

@ -81,7 +81,7 @@ float4x4 float4x4FromScalar(float x) {
}`
func Compile(p *shaderir.Program) (vertexShader, pixelShader, prelude string) {
offsets := UniformVariableOffsetsInDWords(p)
offsets := UniformVariableOffsetsInDwords(p)
c := &compileContext{
unit: p.Unit,
@ -119,8 +119,8 @@ func Compile(p *shaderir.Program) (vertexShader, pixelShader, prelude string) {
lines = append(lines, "cbuffer Uniforms : register(b0) {")
for i, t := range p.Uniforms {
// packingoffset is not mandatory, but this is useful to ensure the correct offset is used.
offset := fmt.Sprintf("c%d", offsets[i]/UniformVariableBoundaryInDWords)
switch offsets[i] % UniformVariableBoundaryInDWords {
offset := fmt.Sprintf("c%d", offsets[i]/UniformVariableBoundaryInDwords)
switch offsets[i] % UniformVariableBoundaryInDwords {
case 1:
offset += ".y"
case 2:

View File

@ -20,10 +20,10 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)
const UniformVariableBoundaryInDWords = 4
const UniformVariableBoundaryInDwords = 4
// UniformVariableOffsetsInDWords returns the offsets of the uniform variables in DWROD units in the HLSL layout.
func UniformVariableOffsetsInDWords(program *shaderir.Program) []int {
// UniformVariableOffsetsInDwords returns the offsets of the uniform variables in DWROD units in the HLSL layout.
func UniformVariableOffsetsInDwords(program *shaderir.Program) []int {
// https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules
// https://github.com/microsoft/DirectXShaderCompiler/wiki/Buffer-Packing
@ -31,59 +31,59 @@ func UniformVariableOffsetsInDWords(program *shaderir.Program) []int {
if x == 0 {
return 0
}
return ((x-1)/UniformVariableBoundaryInDWords + 1) * UniformVariableBoundaryInDWords
return ((x-1)/UniformVariableBoundaryInDwords + 1) * UniformVariableBoundaryInDwords
}
var offsetsInDWords []int
var headInDWords int
var offsetsInDwords []int
var headInDwords int
// TODO: Reorder the variables with packoffset.
// See https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-packoffset
for _, u := range program.Uniforms {
switch u.Main {
case shaderir.Float:
offsetsInDWords = append(offsetsInDWords, headInDWords)
headInDWords += 1
offsetsInDwords = append(offsetsInDwords, headInDwords)
headInDwords += 1
case shaderir.Int:
offsetsInDWords = append(offsetsInDWords, headInDWords)
headInDWords += 1
offsetsInDwords = append(offsetsInDwords, headInDwords)
headInDwords += 1
case shaderir.Vec2, shaderir.IVec2:
if headInDWords%UniformVariableBoundaryInDWords >= 3 {
headInDWords = align(headInDWords)
if headInDwords%UniformVariableBoundaryInDwords >= 3 {
headInDwords = align(headInDwords)
}
offsetsInDWords = append(offsetsInDWords, headInDWords)
headInDWords += 2
offsetsInDwords = append(offsetsInDwords, headInDwords)
headInDwords += 2
case shaderir.Vec3, shaderir.IVec3:
if headInDWords%UniformVariableBoundaryInDWords >= 2 {
headInDWords = align(headInDWords)
if headInDwords%UniformVariableBoundaryInDwords >= 2 {
headInDwords = align(headInDwords)
}
offsetsInDWords = append(offsetsInDWords, headInDWords)
headInDWords += 3
offsetsInDwords = append(offsetsInDwords, headInDwords)
headInDwords += 3
case shaderir.Vec4, shaderir.IVec4:
if headInDWords%UniformVariableBoundaryInDWords >= 1 {
headInDWords = align(headInDWords)
if headInDwords%UniformVariableBoundaryInDwords >= 1 {
headInDwords = align(headInDwords)
}
offsetsInDWords = append(offsetsInDWords, headInDWords)
headInDWords += 4
offsetsInDwords = append(offsetsInDwords, headInDwords)
headInDwords += 4
case shaderir.Mat2:
// For matrices, each column is aligned to the boundary.
headInDWords = align(headInDWords)
offsetsInDWords = append(offsetsInDWords, headInDWords)
headInDWords += 6
headInDwords = align(headInDwords)
offsetsInDwords = append(offsetsInDwords, headInDwords)
headInDwords += 6
case shaderir.Mat3:
headInDWords = align(headInDWords)
offsetsInDWords = append(offsetsInDWords, headInDWords)
headInDWords += 11
headInDwords = align(headInDwords)
offsetsInDwords = append(offsetsInDwords, headInDwords)
headInDwords += 11
case shaderir.Mat4:
headInDWords = align(headInDWords)
offsetsInDWords = append(offsetsInDWords, headInDWords)
headInDWords += 16
headInDwords = align(headInDwords)
offsetsInDwords = append(offsetsInDwords, headInDwords)
headInDwords += 16
case shaderir.Array:
// Each array is 16-byte aligned.
// TODO: What if the array has 2 or more dimensions?
headInDWords = align(headInDWords)
offsetsInDWords = append(offsetsInDWords, headInDWords)
n := u.Sub[0].Uint32Count()
headInDwords = align(headInDwords)
offsetsInDwords = append(offsetsInDwords, headInDwords)
n := u.Sub[0].DwordCount()
switch u.Sub[0].Main {
case shaderir.Mat2:
n = 6
@ -92,9 +92,9 @@ func UniformVariableOffsetsInDWords(program *shaderir.Program) []int {
case shaderir.Mat4:
n = 16
}
headInDWords += (u.Length - 1) * align(n)
headInDwords += (u.Length - 1) * align(n)
// The last element is not with a padding.
headInDWords += n
headInDwords += n
case shaderir.Struct:
// TODO: Implement this
panic("hlsl: offset for a struct is not implemented yet")
@ -103,5 +103,5 @@ func UniformVariableOffsetsInDWords(program *shaderir.Program) []int {
}
}
return offsetsInDWords
return offsetsInDwords
}

View File

@ -493,7 +493,7 @@ func (p *Program) FilterUniformVariables(uniforms []uint32) {
p.uniformFactors = make([]uint32, len(uniforms))
var idx int
for i, typ := range p.Uniforms {
c := typ.Uint32Count()
c := typ.DwordCount()
if reachableUniforms[i] {
for i := idx; i < idx+c; i++ {
p.uniformFactors[i] = 1

View File

@ -87,7 +87,7 @@ func (t Type) String() string {
}
}
func (t Type) Uint32Count() int {
func (t Type) DwordCount() int {
switch t.Main {
case Int:
return 1
@ -112,7 +112,7 @@ func (t Type) Uint32Count() int {
case Mat4:
return 16
case Array:
return t.Length * t.Sub[0].Uint32Count()
return t.Length * t.Sub[0].DwordCount()
default: // TODO: Parse a struct correctly
return -1
}

View File

@ -27,9 +27,9 @@ import (
type Shader struct {
shader *atlas.Shader
uniformNames []string
uniformTypes []shaderir.Type
uniformUint32Count int
uniformNames []string
uniformTypes []shaderir.Type
uniformDwordCount int
}
func NewShader(ir *shaderir.Program, name string) *Shader {
@ -45,20 +45,20 @@ func (s *Shader) Deallocate() {
}
func (s *Shader) AppendUniforms(dst []uint32, uniforms map[string]any) []uint32 {
if s.uniformUint32Count == 0 {
if s.uniformDwordCount == 0 {
for _, typ := range s.uniformTypes {
s.uniformUint32Count += typ.Uint32Count()
s.uniformDwordCount += typ.DwordCount()
}
}
origLen := len(dst)
if cap(dst)-len(dst) >= s.uniformUint32Count {
dst = dst[:len(dst)+s.uniformUint32Count]
if cap(dst)-len(dst) >= s.uniformDwordCount {
dst = dst[:len(dst)+s.uniformDwordCount]
for i := origLen; i < len(dst); i++ {
dst[i] = 0
}
} else {
dst = append(dst, make([]uint32, s.uniformUint32Count)...)
dst = append(dst, make([]uint32, s.uniformDwordCount)...)
}
idx := origLen
@ -71,23 +71,23 @@ func (s *Shader) AppendUniforms(dst []uint32, uniforms map[string]any) []uint32
t := v.Type()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if typ.Uint32Count() != 1 {
if typ.DwordCount() != 1 {
panic(fmt.Sprintf("ui: unexpected uniform value for %s (%s)", name, typ.String()))
}
dst[idx] = uint32(v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
if typ.Uint32Count() != 1 {
if typ.DwordCount() != 1 {
panic(fmt.Sprintf("ui: unexpected uniform value for %s (%s)", name, typ.String()))
}
dst[idx] = uint32(v.Uint())
case reflect.Float32, reflect.Float64:
if typ.Uint32Count() != 1 {
if typ.DwordCount() != 1 {
panic(fmt.Sprintf("ui: unexpected uniform value for %s (%s)", name, typ.String()))
}
dst[idx] = math.Float32bits(float32(v.Float()))
case reflect.Slice, reflect.Array:
l := v.Len()
if typ.Uint32Count() != l {
if typ.DwordCount() != l {
panic(fmt.Sprintf("ui: unexpected uniform value for %s (%s)", name, typ.String()))
}
switch t.Elem().Kind() {
@ -111,7 +111,7 @@ func (s *Shader) AppendUniforms(dst []uint32, uniforms map[string]any) []uint32
}
}
idx += typ.Uint32Count()
idx += typ.DwordCount()
}
return dst