diff --git a/buffers/uniform_buffer.go b/buffers/uniform_buffer.go index a744e4f..41aa3b0 100755 --- a/buffers/uniform_buffer.go +++ b/buffers/uniform_buffer.go @@ -50,6 +50,10 @@ func (ub *UniformBuffer) UnBind() { gl.BindBuffer(gl.UNIFORM_BUFFER, 0) } +func (ub *UniformBuffer) SetBindPoint(bindPointIndex uint32) { + gl.BindBufferBase(gl.UNIFORM_BUFFER, bindPointIndex, ub.Id) +} + func addUniformBufferFieldsToArray(startAlignedOffset uint16, arrayToAddTo *[]UniformBufferField, fieldsToAdd []UniformBufferFieldInput) (totalSize uint32) { if len(fieldsToAdd) == 0 { @@ -113,7 +117,7 @@ func addUniformBufferFieldsToArray(startAlignedOffset uint16, arrayToAddTo *[]Un if f.Type == DataTypeStruct { - subfieldsAlignedOffset := uint16(addUniformBufferFieldsToArray(alignedOffset, arrayToAddTo, f.Subfields)) + subfieldsAlignedOffset := uint16(addUniformBufferFieldsToArray(startAlignedOffset+alignedOffset, arrayToAddTo, f.Subfields)) // Pad structs to 16 byte boundary subfieldsAlignmentError := subfieldsAlignedOffset % 16 @@ -199,6 +203,14 @@ 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) +} + +func setStruct(fields []UniformBufferField, buf []byte, inputStruct any, maxFieldsToConsume int) (bytesWritten, fieldsConsumed int) { + + if len(fields) == 0 { + return + } if inputStruct == nil { logging.ErrLog.Panicf("UniformBuffer.SetStruct called with a value that is nil") @@ -209,16 +221,15 @@ func (ub *UniformBuffer) SetStruct(inputStruct any) { logging.ErrLog.Panicf("UniformBuffer.SetStruct called with a value that is not a struct. Val=%v\n", inputStruct) } - if structVal.NumField() != len(ub.Fields) { - logging.ErrLog.Panicf("struct fields must match uniform buffer fields, but uniform buffer contains %d fields, while the passed struct has %d fields\n", len(ub.Fields), structVal.NumField()) - } + structFieldIndex := 0 + // structFieldCount := structVal.NumField() + for fieldIndex := 0; fieldIndex < len(fields) && fieldIndex < maxFieldsToConsume; fieldIndex++ { - writeIndex := 0 - buf := make([]byte, ub.Size) - for i := 0; i < len(ub.Fields); i++ { + ubField := &fields[fieldIndex] + valField := structVal.Field(structFieldIndex) - ubField := &ub.Fields[i] - valField := structVal.Field(i) + fieldsConsumed++ + structFieldIndex++ kind := valField.Kind() if kind == reflect.Pointer { @@ -238,7 +249,7 @@ func (ub *UniformBuffer) SetStruct(inputStruct any) { } typeMatches := false - writeIndex = int(ubField.AlignedOffset) + bytesWritten = int(ubField.AlignedOffset) switch ubField.Type { @@ -248,9 +259,9 @@ func (ub *UniformBuffer) SetStruct(inputStruct any) { if typeMatches { if isArray { - Write32BitIntegerSliceToByteBufWithAlignment(buf, &writeIndex, 16, valField.Slice(0, valField.Len()).Interface().([]uint32)) + Write32BitIntegerSliceToByteBufWithAlignment(buf, &bytesWritten, 16, valField.Slice(0, valField.Len()).Interface().([]uint32)) } else { - Write32BitIntegerToByteBuf(buf, &writeIndex, uint32(valField.Uint())) + Write32BitIntegerToByteBuf(buf, &bytesWritten, uint32(valField.Uint())) } } @@ -260,9 +271,9 @@ func (ub *UniformBuffer) SetStruct(inputStruct any) { if typeMatches { if isArray { - WriteF32SliceToByteBufWithAlignment(buf, &writeIndex, 16, valField.Slice(0, valField.Len()).Interface().([]float32)) + WriteF32SliceToByteBufWithAlignment(buf, &bytesWritten, 16, valField.Slice(0, valField.Len()).Interface().([]float32)) } else { - WriteF32ToByteBuf(buf, &writeIndex, float32(valField.Float())) + WriteF32ToByteBuf(buf, &bytesWritten, float32(valField.Float())) } } @@ -272,9 +283,9 @@ func (ub *UniformBuffer) SetStruct(inputStruct any) { if typeMatches { if isArray { - Write32BitIntegerSliceToByteBufWithAlignment(buf, &writeIndex, 16, valField.Slice(0, valField.Len()).Interface().([]int32)) + Write32BitIntegerSliceToByteBufWithAlignment(buf, &bytesWritten, 16, valField.Slice(0, valField.Len()).Interface().([]int32)) } else { - Write32BitIntegerToByteBuf(buf, &writeIndex, uint32(valField.Int())) + Write32BitIntegerToByteBuf(buf, &bytesWritten, uint32(valField.Int())) } } @@ -285,10 +296,10 @@ func (ub *UniformBuffer) SetStruct(inputStruct any) { if typeMatches { if isArray { - WriteVec2SliceToByteBufWithAlignment(buf, &writeIndex, 16, valField.Slice(0, valField.Len()).Interface().([]gglm.Vec2)) + WriteVec2SliceToByteBufWithAlignment(buf, &bytesWritten, 16, valField.Slice(0, valField.Len()).Interface().([]gglm.Vec2)) } else { v2 := valField.Interface().(gglm.Vec2) - WriteF32SliceToByteBuf(buf, &writeIndex, v2.Data[:]) + WriteF32SliceToByteBuf(buf, &bytesWritten, v2.Data[:]) } } @@ -299,10 +310,10 @@ func (ub *UniformBuffer) SetStruct(inputStruct any) { if typeMatches { if isArray { - WriteVec3SliceToByteBufWithAlignment(buf, &writeIndex, 16, valField.Slice(0, valField.Len()).Interface().([]gglm.Vec3)) + WriteVec3SliceToByteBufWithAlignment(buf, &bytesWritten, 16, valField.Slice(0, valField.Len()).Interface().([]gglm.Vec3)) } else { v3 := valField.Interface().(gglm.Vec3) - WriteF32SliceToByteBuf(buf, &writeIndex, v3.Data[:]) + WriteF32SliceToByteBuf(buf, &bytesWritten, v3.Data[:]) } } @@ -313,10 +324,10 @@ func (ub *UniformBuffer) SetStruct(inputStruct any) { if typeMatches { if isArray { - WriteVec4SliceToByteBufWithAlignment(buf, &writeIndex, 16, valField.Slice(0, valField.Len()).Interface().([]gglm.Vec4)) + WriteVec4SliceToByteBufWithAlignment(buf, &bytesWritten, 16, valField.Slice(0, valField.Len()).Interface().([]gglm.Vec4)) } else { v3 := valField.Interface().(gglm.Vec4) - WriteF32SliceToByteBuf(buf, &writeIndex, v3.Data[:]) + WriteF32SliceToByteBuf(buf, &bytesWritten, v3.Data[:]) } } @@ -328,11 +339,11 @@ func (ub *UniformBuffer) SetStruct(inputStruct any) { if isArray { m2Arr := valField.Interface().([]gglm.Mat2) - WriteMat2SliceToByteBufWithAlignment(buf, &writeIndex, 16*2, m2Arr) + WriteMat2SliceToByteBufWithAlignment(buf, &bytesWritten, 16*2, m2Arr) } else { m := valField.Interface().(gglm.Mat2) - WriteF32SliceToByteBuf(buf, &writeIndex, m.Data[0][:]) - WriteF32SliceToByteBuf(buf, &writeIndex, m.Data[1][:]) + WriteF32SliceToByteBuf(buf, &bytesWritten, m.Data[0][:]) + WriteF32SliceToByteBuf(buf, &bytesWritten, m.Data[1][:]) } } @@ -344,12 +355,12 @@ func (ub *UniformBuffer) SetStruct(inputStruct any) { if isArray { m3Arr := valField.Interface().([]gglm.Mat3) - WriteMat3SliceToByteBufWithAlignment(buf, &writeIndex, 16*3, m3Arr) + WriteMat3SliceToByteBufWithAlignment(buf, &bytesWritten, 16*3, m3Arr) } else { m := valField.Interface().(gglm.Mat3) - WriteF32SliceToByteBuf(buf, &writeIndex, m.Data[0][:]) - WriteF32SliceToByteBuf(buf, &writeIndex, m.Data[1][:]) - WriteF32SliceToByteBuf(buf, &writeIndex, m.Data[2][:]) + WriteF32SliceToByteBuf(buf, &bytesWritten, m.Data[0][:]) + WriteF32SliceToByteBuf(buf, &bytesWritten, m.Data[1][:]) + WriteF32SliceToByteBuf(buf, &bytesWritten, m.Data[2][:]) } } @@ -361,34 +372,44 @@ func (ub *UniformBuffer) SetStruct(inputStruct any) { if isArray { m4Arr := valField.Interface().([]gglm.Mat4) - WriteMat4SliceToByteBufWithAlignment(buf, &writeIndex, 16*4, m4Arr) + WriteMat4SliceToByteBufWithAlignment(buf, &bytesWritten, 16*4, m4Arr) } else { m := valField.Interface().(gglm.Mat4) - WriteF32SliceToByteBuf(buf, &writeIndex, m.Data[0][:]) - WriteF32SliceToByteBuf(buf, &writeIndex, m.Data[1][:]) - WriteF32SliceToByteBuf(buf, &writeIndex, m.Data[2][:]) - WriteF32SliceToByteBuf(buf, &writeIndex, m.Data[3][:]) + WriteF32SliceToByteBuf(buf, &bytesWritten, m.Data[0][:]) + WriteF32SliceToByteBuf(buf, &bytesWritten, m.Data[1][:]) + WriteF32SliceToByteBuf(buf, &bytesWritten, m.Data[2][:]) + WriteF32SliceToByteBuf(buf, &bytesWritten, m.Data[3][:]) } } - // @TODO: Probably can change it similar to addFields, where we send in a struct AND a field array - // and let the function operate on that instead of the globaly fields array - // case DataTypeStruct: + case DataTypeStruct: + typeMatches = kind == reflect.Struct + + if typeMatches { + + setStructBytesWritten, setStructFieldsConsumed := setStruct(fields[fieldIndex+1:], buf, valField.Interface(), valField.NumField()) + + bytesWritten += setStructBytesWritten + fieldIndex += setStructFieldsConsumed + fieldsConsumed += setStructFieldsConsumed + } default: assert.T(false, "Unknown uniform buffer data type passed. DataType '%d'", ubField.Type) } if !typeMatches { - logging.ErrLog.Panicf("Struct field ordering and types must match uniform buffer fields, but at field index %d got UniformBufferField=%v but a struct field of %s\n", i, ubField, valField.String()) + logging.ErrLog.Panicf("Struct field ordering and types must match uniform buffer fields, but at field index %d got UniformBufferField=%v but a struct field of type %s\n", fieldIndex, ubField, valField.String()) } } - if writeIndex == 0 { - return + if bytesWritten == 0 { + return 0, fieldsConsumed } - gl.BufferSubData(gl.UNIFORM_BUFFER, 0, writeIndex, gl.Ptr(&buf[0])) + gl.BufferSubData(gl.UNIFORM_BUFFER, 0, bytesWritten, gl.Ptr(&buf[0])) + + return bytesWritten - int(fields[0].AlignedOffset), fieldsConsumed } func Write32BitIntegerToByteBuf[T uint32 | int32](buf []byte, startIndex *int, val T) { @@ -673,6 +694,7 @@ func NewUniformBuffer(fields []UniformBufferFieldInput) UniformBuffer { ub.Bind() gl.BufferData(gl.UNIFORM_BUFFER, int(ub.Size), gl.Ptr(nil), gl.STATIC_DRAW) + ub.UnBind() return ub } diff --git a/main.go b/main.go index 4532c21..222f234 100755 --- a/main.go +++ b/main.go @@ -213,8 +213,8 @@ const ( UNSCALED_WINDOW_WIDTH = 1280 UNSCALED_WINDOW_HEIGHT = 720 - PROFILE_CPU = true - PROFILE_MEM = true + PROFILE_CPU = false + PROFILE_MEM = false FRAME_TIME_MS_SAMPLES = 10000 ) @@ -688,15 +688,14 @@ func testUbos() { {Id: 2, Type: buffers.DataTypeFloat32}, // 04 32 {Id: 3, Type: buffers.DataTypeMat2}, // 32 48 }) // Total size: 48+32 = 80 + ubo.Bind() println("!!!!!!!!!!!!! Id:", ubo.Id, "; Size:", ubo.Size) fmt.Printf("%+v\n", ubo.Fields) - ubo.Bind() ubo.SetFloat32(0, 99) ubo.SetFloat32(2, 199) ubo.SetVec3(1, &gglm.Vec3{Data: [3]float32{33, 33, 33}}) - ubo.SetMat2(3, &gglm.Mat2{Data: [2][2]float32{{1, 3}, {2, 4}}}) var v gglm.Vec3 @@ -776,6 +775,7 @@ func testUbos() { {Id: 8, Type: buffers.DataTypeMat3, Count: 2}, {Id: 9, Type: buffers.DataTypeMat4, Count: 2}, }) + ubo2.Bind() ubo2.SetStruct(s2) @@ -803,46 +803,66 @@ func testUbos() { // // Ubo3 // + type TestUBO3_0 struct { + X int32 + } + type TestUBO3_1 struct { - F32 float32 - V3 gglm.Vec3 + F32 float32 + V3 gglm.Vec3 + Zero TestUBO3_0 } type TestUBO3_2 struct { F32 float32 S TestUBO3_1 + XX int32 } ubo3 := buffers.NewUniformBuffer([]buffers.UniformBufferFieldInput{ {Id: 0, Type: buffers.DataTypeFloat32}, // 04 00 {Id: 1, Type: buffers.DataTypeStruct, Subfields: []buffers.UniformBufferFieldInput{ // 00 16 - {Id: 2, Type: buffers.DataTypeFloat32}, // 04 20 + {Id: 2, Type: buffers.DataTypeFloat32}, // 04 16 {Id: 3, Type: buffers.DataTypeVec3}, // 16 32 - }}, // 32+16 = 48 - }) + {Id: 4, Type: buffers.DataTypeStruct, Subfields: []buffers.UniformBufferFieldInput{ // 00 48 + {Id: 5, Type: buffers.DataTypeInt32}, // 04 48 + }}, + }}, + {Id: 6, Type: buffers.DataTypeInt32}, // 04 64 + }) // 68 + ubo3.Bind() - fmt.Printf("\n==UBO3==\nSize=%d\nFields: %+v", ubo3.Size, ubo3.Fields) + ubo3.SetBindPoint(2) + groundMat.SetUniformBlockBindingPoint("Test2", 2) - ubo3.SetFloat32(0, 11) - ubo3.SetFloat32(2, 22) - ubo3.SetVec3(3, &gglm.Vec3{Data: [3]float32{33, 44, 55}}) + fmt.Printf("\n==UBO3==\nSize=%d\nFields: %+v\n\n", ubo3.Size, ubo3.Fields) - // Bind the uniform block and the vertex buffer both to binding slot 2 - gl.BindBufferBase(gl.UNIFORM_BUFFER, 2, ubo3.Id) + s3 := TestUBO3_2{ + F32: 76.1, + S: TestUBO3_1{ + F32: 89.9, + V3: gglm.NewVec3(7.1, 7.2, 7.3), + Zero: TestUBO3_0{ + X: 33, + }, + }, + XX: 41, + } - name := "Test2\x00" - uniformBlockIndex := gl.GetUniformBlockIndex(groundMat.ShaderProg.Id, gl.Str(name)) - gl.UniformBlockBinding(groundMat.ShaderProg.Id, uniformBlockIndex, 2) + ubo3.SetStruct(s3) - // s3 := TestUBO3_2{ - // F32: 76.1, - // S: TestUBO3_1{ - // F32: 89.9, - // V3: gglm.NewVec3(7.1, 7.2, 7.3), - // }, - // } + ubo3F32 := float32(0.0) + ubo3SF32 := float32(0.0) + ubo3SV3 := gglm.Vec3{} + ubo3SZeroX := 0 + ubo3Xx := 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])) + gl.GetBufferSubData(gl.UNIFORM_BUFFER, 48, 4, gl.Ptr(&ubo3SZeroX)) + gl.GetBufferSubData(gl.UNIFORM_BUFFER, 64, 4, gl.Ptr(&ubo3Xx)) - // ubo3.SetStruct(s3) + fmt.Printf("ubo3_f32=%f\nubo3_s_f32=%f\nubo3_s_v3=%s\nubo3_s_zero_x=%d\nubo3_xx=%d\n", ubo3F32, ubo3SF32, ubo3SV3.String(), ubo3SZeroX, ubo3Xx) } func (g *Game) initFbos() { diff --git a/materials/material.go b/materials/material.go index e359834..35a652e 100755 --- a/materials/material.go +++ b/materials/material.go @@ -124,6 +124,21 @@ func (m *Material) UnBind() { gl.UseProgram(0) } +func (m *Material) SetUniformBlockBindingPoint(uniformBlockName string, bindPointIndex uint32) { + + nullStr := gl.Str(uniformBlockName + "\x00") + index := gl.GetUniformBlockIndex(m.ShaderProg.Id, nullStr) + assert.T( + index != gl.INVALID_INDEX, + "SetUniformBlockBindingPoint for material=%s (matId=%d; shaderId=%d) failed because the uniform block=%s wasn't found", + m.Name, + m.Id, + m.ShaderProg.Id, + uniformBlockName, + ) + gl.UniformBlockBinding(m.ShaderProg.Id, index, bindPointIndex) +} + func (m *Material) GetAttribLoc(attribName string) int32 { loc, ok := m.AttribLocs[attribName]