From 0e98dc85f56eecdf23de2fb8ddf5688acd6514b1 Mon Sep 17 00:00:00 2001 From: bloeys Date: Sun, 15 Sep 2024 12:31:26 +0400 Subject: [PATCH] Update ubo.SetStruct to handle nested structs --- buffers/uniform_buffer.go | 57 ++++++++++++++++++++++++++++++--------- main.go | 36 ++++++++++++++++++------- 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/buffers/uniform_buffer.go b/buffers/uniform_buffer.go index 8a9b822..00649d5 100755 --- a/buffers/uniform_buffer.go +++ b/buffers/uniform_buffer.go @@ -120,11 +120,7 @@ func addUniformBufferFieldsToArray(startAlignedOffset uint16, arrayToAddTo *[]Un subfieldsAlignedOffset := uint16(addUniformBufferFieldsToArray(startAlignedOffset+alignedOffset, arrayToAddTo, f.Subfields)) // Pad structs to 16 byte boundary - subfieldsAlignmentError := subfieldsAlignedOffset % 16 - if subfieldsAlignmentError != 0 { - subfieldsAlignedOffset += 16 - subfieldsAlignmentError - } - + padTo16Boundary(&subfieldsAlignedOffset) alignedOffset += subfieldsAlignedOffset * f.Count } else { @@ -135,6 +131,13 @@ func addUniformBufferFieldsToArray(startAlignedOffset uint16, arrayToAddTo *[]Un 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 { 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) { - 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 { return @@ -240,6 +243,7 @@ func setStruct(fields []UniformBufferField, buf []byte, inputStruct any, maxFiel isArray := kind == reflect.Slice || kind == reflect.Array if isArray { elementType = valField.Type().Elem() + kind = elementType.Kind() } else { elementType = valField.Type() } @@ -249,7 +253,7 @@ func setStruct(fields []UniformBufferField, buf []byte, inputStruct any, maxFiel } typeMatches := false - bytesWritten = int(ubField.AlignedOffset) + bytesWritten = int(ubField.AlignedOffset) + writeOffset switch ubField.Type { @@ -383,15 +387,42 @@ func setStruct(fields []UniformBufferField, buf []byte, inputStruct any, maxFiel } case DataTypeStruct: + typeMatches = kind == reflect.Struct if typeMatches { - setStructBytesWritten, setStructFieldsConsumed := setStruct(fields[fieldIndex+1:], buf, valField.Interface(), valField.NumField(), true) + if isArray { - bytesWritten += setStructBytesWritten - fieldIndex += setStructFieldsConsumed - fieldsConsumed += setStructFieldsConsumed + 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 + fieldIndex += setStructFieldsConsumed + fieldsConsumed += setStructFieldsConsumed + } } default: @@ -411,7 +442,7 @@ func setStruct(fields []UniformBufferField, buf []byte, inputStruct any, maxFiel 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) { diff --git a/main.go b/main.go index 3446885..44b7a04 100755 --- a/main.go +++ b/main.go @@ -677,7 +677,7 @@ func (g *Game) Init() { // Ubos g.initUbos() - // testUbos() + testUbos() // Initial camera update cam.Update() @@ -873,7 +873,8 @@ func testUbos() { XX int32 Z2 TestUBO3_0 XX2 int32 - Abcd TestUBO3_X + Abcd [2]TestUBO3_X + XX3 int32 } ubo3 := buffers.NewUniformBuffer([]buffers.UniformBufferFieldInput{ @@ -890,13 +891,14 @@ func testUbos() { {Id: 8, Type: buffers.DataTypeInt32}, // 04 80 }}, {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: 12, Type: buffers.DataTypeStruct, Subfields: []buffers.UniformBufferFieldInput{ // 00 112 {Id: 13, Type: buffers.DataTypeInt32}, // 04 112 }}, }}, }}, + {Id: 14, Type: buffers.DataTypeInt32}, }) // 116 ubo3.Bind() @@ -919,13 +921,23 @@ func testUbos() { X: 8, }, XX2: 321, - Abcd: TestUBO3_X{ - V: TestUBO3_Y{ - V: TestUBO3_Z{ - V: 9911, + Abcd: [2]TestUBO3_X{ + { + V: TestUBO3_Y{ + V: TestUBO3_Z{ + V: 9911, + }, + }, + }, + { + V: TestUBO3_Y{ + V: TestUBO3_Z{ + V: 9922, + }, }, }, }, + XX3: 818, } ubo3.SetStruct(s3) @@ -937,7 +949,9 @@ func testUbos() { ubo3Xx := 0 ubo3SZ2X := 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, 16, 4, gl.Ptr(&ubo3SF32)) 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, 80, 4, gl.Ptr(&ubo3SZ2X)) 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() {