Update ubo.SetStruct to handle nested structs

This commit is contained in:
bloeys
2024-09-15 12:31:26 +04:00
parent f2b757c606
commit 0e98dc85f5
2 changed files with 70 additions and 23 deletions

View File

@ -120,11 +120,7 @@ func addUniformBufferFieldsToArray(startAlignedOffset uint16, arrayToAddTo *[]Un
subfieldsAlignedOffset := uint16(addUniformBufferFieldsToArray(startAlignedOffset+alignedOffset, arrayToAddTo, f.Subfields)) subfieldsAlignedOffset := uint16(addUniformBufferFieldsToArray(startAlignedOffset+alignedOffset, arrayToAddTo, f.Subfields))
// Pad structs to 16 byte boundary // Pad structs to 16 byte boundary
subfieldsAlignmentError := subfieldsAlignedOffset % 16 padTo16Boundary(&subfieldsAlignedOffset)
if subfieldsAlignmentError != 0 {
subfieldsAlignedOffset += 16 - subfieldsAlignmentError
}
alignedOffset += subfieldsAlignedOffset * f.Count alignedOffset += subfieldsAlignedOffset * f.Count
} else { } else {
@ -135,6 +131,13 @@ func addUniformBufferFieldsToArray(startAlignedOffset uint16, arrayToAddTo *[]Un
return uint32(alignedOffset) return uint32(alignedOffset)
} }
func padTo16Boundary[T uint16 | int | int32](val *T) {
alignmentError := *val % 16
if alignmentError != 0 {
*val += 16 - alignmentError
}
}
func (ub *UniformBuffer) getField(fieldId uint16, fieldType ElementType) UniformBufferField { func (ub *UniformBuffer) getField(fieldId uint16, fieldType ElementType) UniformBufferField {
for i := 0; i < len(ub.Fields); i++ { for i := 0; i < len(ub.Fields); i++ {
@ -203,10 +206,10 @@ func (ub *UniformBuffer) SetMat4(fieldId uint16, val *gglm.Mat4) {
} }
func (ub *UniformBuffer) SetStruct(inputStruct any) { func (ub *UniformBuffer) SetStruct(inputStruct any) {
setStruct(ub.Fields, make([]byte, ub.Size), inputStruct, 1000_000, false) setStruct(ub.Fields, make([]byte, ub.Size), inputStruct, 1000_000, false, 0)
} }
func setStruct(fields []UniformBufferField, buf []byte, inputStruct any, maxFieldsToConsume int, onlyBufWrite bool) (bytesWritten, fieldsConsumed int) { func setStruct(fields []UniformBufferField, buf []byte, inputStruct any, maxFieldsToConsume int, onlyBufWrite bool, writeOffset int) (bytesWritten, fieldsConsumed int) {
if len(fields) == 0 { if len(fields) == 0 {
return return
@ -240,6 +243,7 @@ func setStruct(fields []UniformBufferField, buf []byte, inputStruct any, maxFiel
isArray := kind == reflect.Slice || kind == reflect.Array isArray := kind == reflect.Slice || kind == reflect.Array
if isArray { if isArray {
elementType = valField.Type().Elem() elementType = valField.Type().Elem()
kind = elementType.Kind()
} else { } else {
elementType = valField.Type() elementType = valField.Type()
} }
@ -249,7 +253,7 @@ func setStruct(fields []UniformBufferField, buf []byte, inputStruct any, maxFiel
} }
typeMatches := false typeMatches := false
bytesWritten = int(ubField.AlignedOffset) bytesWritten = int(ubField.AlignedOffset) + writeOffset
switch ubField.Type { switch ubField.Type {
@ -383,16 +387,43 @@ func setStruct(fields []UniformBufferField, buf []byte, inputStruct any, maxFiel
} }
case DataTypeStruct: case DataTypeStruct:
typeMatches = kind == reflect.Struct typeMatches = kind == reflect.Struct
if typeMatches { if typeMatches {
setStructBytesWritten, setStructFieldsConsumed := setStruct(fields[fieldIndex+1:], buf, valField.Interface(), valField.NumField(), true) if isArray {
offset := 0
arrSize := valField.Len()
fieldsToUse := fields[fieldIndex+1:]
for i := 0; i < arrSize; i++ {
setStructBytesWritten, setStructFieldsConsumed := setStruct(fieldsToUse, buf, valField.Index(i).Interface(), elementType.NumField(), true, offset*i)
if offset == 0 {
offset = setStructBytesWritten
padTo16Boundary(&offset)
bytesWritten += offset * arrSize
// Tracking consumed fields is needed because if we have a struct inside another struct
// elementType.NumField() will only give us the fields consumed by the first struct,
// but we need to count all fields of all nested structs inside this one
fieldIndex += setStructFieldsConsumed
fieldsConsumed += setStructFieldsConsumed
}
}
} else {
setStructBytesWritten, setStructFieldsConsumed := setStruct(fields[fieldIndex+1:], buf, valField.Interface(), valField.NumField(), true, writeOffset)
bytesWritten += setStructBytesWritten bytesWritten += setStructBytesWritten
fieldIndex += setStructFieldsConsumed fieldIndex += setStructFieldsConsumed
fieldsConsumed += setStructFieldsConsumed fieldsConsumed += setStructFieldsConsumed
} }
}
default: default:
assert.T(false, "Unknown uniform buffer data type passed. DataType '%d'", ubField.Type) assert.T(false, "Unknown uniform buffer data type passed. DataType '%d'", ubField.Type)
@ -411,7 +442,7 @@ func setStruct(fields []UniformBufferField, buf []byte, inputStruct any, maxFiel
gl.BufferSubData(gl.UNIFORM_BUFFER, 0, bytesWritten, gl.Ptr(&buf[0])) gl.BufferSubData(gl.UNIFORM_BUFFER, 0, bytesWritten, gl.Ptr(&buf[0]))
} }
return bytesWritten - int(fields[0].AlignedOffset), fieldsConsumed return bytesWritten - int(fields[0].AlignedOffset) - writeOffset, fieldsConsumed
} }
func Write32BitIntegerToByteBuf[T uint32 | int32](buf []byte, startIndex *int, val T) { func Write32BitIntegerToByteBuf[T uint32 | int32](buf []byte, startIndex *int, val T) {

30
main.go
View File

@ -677,7 +677,7 @@ func (g *Game) Init() {
// Ubos // Ubos
g.initUbos() g.initUbos()
// testUbos() testUbos()
// Initial camera update // Initial camera update
cam.Update() cam.Update()
@ -873,7 +873,8 @@ func testUbos() {
XX int32 XX int32
Z2 TestUBO3_0 Z2 TestUBO3_0
XX2 int32 XX2 int32
Abcd TestUBO3_X Abcd [2]TestUBO3_X
XX3 int32
} }
ubo3 := buffers.NewUniformBuffer([]buffers.UniformBufferFieldInput{ ubo3 := buffers.NewUniformBuffer([]buffers.UniformBufferFieldInput{
@ -890,13 +891,14 @@ func testUbos() {
{Id: 8, Type: buffers.DataTypeInt32}, // 04 80 {Id: 8, Type: buffers.DataTypeInt32}, // 04 80
}}, }},
{Id: 9, Type: buffers.DataTypeInt32}, // 04 96 {Id: 9, Type: buffers.DataTypeInt32}, // 04 96
{Id: 10, Type: buffers.DataTypeStruct, Subfields: []buffers.UniformBufferFieldInput{ // 00 112 {Id: 10, Type: buffers.DataTypeStruct, Count: 2, Subfields: []buffers.UniformBufferFieldInput{ // 00 112
{Id: 11, Type: buffers.DataTypeStruct, Subfields: []buffers.UniformBufferFieldInput{ // 00 112 {Id: 11, Type: buffers.DataTypeStruct, Subfields: []buffers.UniformBufferFieldInput{ // 00 112
{Id: 12, Type: buffers.DataTypeStruct, Subfields: []buffers.UniformBufferFieldInput{ // 00 112 {Id: 12, Type: buffers.DataTypeStruct, Subfields: []buffers.UniformBufferFieldInput{ // 00 112
{Id: 13, Type: buffers.DataTypeInt32}, // 04 112 {Id: 13, Type: buffers.DataTypeInt32}, // 04 112
}}, }},
}}, }},
}}, }},
{Id: 14, Type: buffers.DataTypeInt32},
}) // 116 }) // 116
ubo3.Bind() ubo3.Bind()
@ -919,13 +921,23 @@ func testUbos() {
X: 8, X: 8,
}, },
XX2: 321, XX2: 321,
Abcd: TestUBO3_X{ Abcd: [2]TestUBO3_X{
{
V: TestUBO3_Y{ V: TestUBO3_Y{
V: TestUBO3_Z{ V: TestUBO3_Z{
V: 9911, V: 9911,
}, },
}, },
}, },
{
V: TestUBO3_Y{
V: TestUBO3_Z{
V: 9922,
},
},
},
},
XX3: 818,
} }
ubo3.SetStruct(s3) ubo3.SetStruct(s3)
@ -937,7 +949,9 @@ func testUbos() {
ubo3Xx := 0 ubo3Xx := 0
ubo3SZ2X := 0 ubo3SZ2X := 0
ubo3Xx2 := 0 ubo3Xx2 := 0
ubo3AbcdV := 0 ubo3AbcdV1 := 0
ubo3AbcdV2 := 0
ubo3Xx3 := 0
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 0, 4, gl.Ptr(&ubo3F32)) gl.GetBufferSubData(gl.UNIFORM_BUFFER, 0, 4, gl.Ptr(&ubo3F32))
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 16, 4, gl.Ptr(&ubo3SF32)) gl.GetBufferSubData(gl.UNIFORM_BUFFER, 16, 4, gl.Ptr(&ubo3SF32))
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 32, 16, gl.Ptr(&ubo3SV3.Data[0])) gl.GetBufferSubData(gl.UNIFORM_BUFFER, 32, 16, gl.Ptr(&ubo3SV3.Data[0]))
@ -946,9 +960,11 @@ func testUbos() {
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 64, 4, gl.Ptr(&ubo3Xx)) gl.GetBufferSubData(gl.UNIFORM_BUFFER, 64, 4, gl.Ptr(&ubo3Xx))
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 80, 4, gl.Ptr(&ubo3SZ2X)) gl.GetBufferSubData(gl.UNIFORM_BUFFER, 80, 4, gl.Ptr(&ubo3SZ2X))
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 96, 4, gl.Ptr(&ubo3Xx2)) gl.GetBufferSubData(gl.UNIFORM_BUFFER, 96, 4, gl.Ptr(&ubo3Xx2))
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 112, 4, gl.Ptr(&ubo3AbcdV)) gl.GetBufferSubData(gl.UNIFORM_BUFFER, 112, 4, gl.Ptr(&ubo3AbcdV1))
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 128, 4, gl.Ptr(&ubo3AbcdV2))
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 144, 4, gl.Ptr(&ubo3Xx3))
fmt.Printf("ubo3_f32=%f\nubo3_s_f32=%f\nubo3_s_v3=%s\nubo3_s_zero_x=%d\nubo3_xx=%d\nubo3_z2_x=%d\nubo3_xx2=%d\nubo3_abcd_v=%d\n", ubo3F32, ubo3SF32, ubo3SV3.String(), ubo3SZeroX, ubo3Xx, ubo3SZ2X, ubo3Xx2, ubo3AbcdV) fmt.Printf("ubo3_f32=%f\nubo3_s_f32=%f\nubo3_s_v3=%s\nubo3_s_zero_x=%d\nubo3_xx=%d\nubo3_z2_x=%d\nubo3_xx2=%d\nubo3_abcd_v1=%d\nubo3_abcd_v2=%d\nubo3_xx3=%d\n", ubo3F32, ubo3SF32, ubo3SV3.String(), ubo3SZeroX, ubo3Xx, ubo3SZ2X, ubo3Xx2, ubo3AbcdV1, ubo3AbcdV2, ubo3Xx3)
} }
func (g *Game) initFbos() { func (g *Game) initFbos() {