Ubos with nested structs slowly getting there

This commit is contained in:
bloeys
2024-06-06 04:59:40 +04:00
parent 870653019c
commit 0d34e0fe6e
4 changed files with 122 additions and 10 deletions

View File

@ -2,6 +2,7 @@ package buffers
import (
"github.com/bloeys/nmage/assert"
"github.com/bloeys/nmage/logging"
"github.com/go-gl/gl/v4.1-core/gl"
)
@ -28,6 +29,8 @@ const (
DataTypeMat2
DataTypeMat3
DataTypeMat4
DataTypeStruct
)
func (dt ElementType) GLType() uint32 {
@ -54,6 +57,10 @@ func (dt ElementType) GLType() uint32 {
case DataTypeMat4:
return gl.FLOAT
case DataTypeStruct:
logging.ErrLog.Fatalf("ElementType.GLType of DataTypeStruct is not supported")
return 0
default:
assert.T(false, "Unknown data type passed. DataType '%d'", dt)
return 0
@ -85,6 +92,10 @@ func (dt ElementType) CompSize() int32 {
case DataTypeMat4:
return 4
case DataTypeStruct:
logging.ErrLog.Fatalf("ElementType.CompSize of DataTypeStruct is not supported")
return 0
default:
assert.T(false, "Unknown data type passed. DataType '%d'", dt)
return 0
@ -116,6 +127,10 @@ func (dt ElementType) CompCount() int32 {
case DataTypeMat4:
return 4 * 4
case DataTypeStruct:
logging.ErrLog.Fatalf("ElementType.CompCount of DataTypeStruct is not supported")
return 0
default:
assert.T(false, "Unknown data type passed. DataType '%d'", dt)
return 0
@ -148,6 +163,10 @@ func (dt ElementType) Size() int32 {
case DataTypeMat4:
return 4 * 4 * 4
case DataTypeStruct:
logging.ErrLog.Fatalf("ElementType.Size of DataTypeStruct is not supported")
return 0
default:
assert.T(false, "Unknown data type passed. DataType '%d'", dt)
return 0
@ -182,6 +201,10 @@ func (dt ElementType) GlStd140BaseAlignment() uint8 {
case DataTypeMat4:
return (4 * 4) * 4
case DataTypeStruct:
logging.ErrLog.Fatalf("ElementType.GlStd140BaseAlignment of DataTypeStruct is not supported")
return 0
default:
assert.T(false, "Unknown data type passed. DataType '%d'", dt)
return 0
@ -211,6 +234,8 @@ func (dt ElementType) GlStd140AlignmentBoundary() uint16 {
case DataTypeMat3:
fallthrough
case DataTypeMat4:
fallthrough
case DataTypeStruct:
return 16
default:
@ -244,6 +269,9 @@ func (dt ElementType) String() string {
case DataTypeMat4:
return "Mat4"
case DataTypeStruct:
return "Struct"
default:
return "Unknown"
}

View File

@ -16,6 +16,10 @@ type UniformBufferFieldInput struct {
// Count should be set in case this field is an array of type `[Count]Type`.
// Count=0 is valid and is equivalent to Count=1, which means the type is NOT an array, but a single field.
Count uint16
// Subfields is used when type is a struct, in which case it holds the fields of the struct.
// Ids do not have to be unique across structs.
Subfields []UniformBufferFieldInput
}
type UniformBufferField struct {
@ -25,6 +29,10 @@ type UniformBufferField struct {
// Count=0 is valid and is equivalent to Count=1, which means the type is NOT an array, but a single field.
Count uint16
Type ElementType
// Subfields is used when type is a struct, in which case it holds the fields of the struct.
// Ids do not have to be unique across structs.
Subfields []UniformBufferField
}
type UniformBuffer struct {
@ -42,19 +50,23 @@ func (ub *UniformBuffer) UnBind() {
gl.BindBuffer(gl.UNIFORM_BUFFER, 0)
}
func (ub *UniformBuffer) addFields(fields []UniformBufferFieldInput) (totalSize uint32) {
func addUniformBufferFieldsToArray(startAlignedOffset uint16, arrayToAddTo *[]UniformBufferField, fieldsToAdd []UniformBufferFieldInput) (totalSize uint32) {
if len(fields) == 0 {
if len(fieldsToAdd) == 0 {
return 0
}
// This function is recursive so only size the array once
if cap(*arrayToAddTo) == 0 {
*arrayToAddTo = make([]UniformBufferField, 0, len(fieldsToAdd))
}
var alignedOffset uint16 = 0
ub.Fields = make([]UniformBufferField, 0, len(fields))
fieldIdToTypeMap := make(map[uint16]ElementType, len(fields))
fieldIdToTypeMap := make(map[uint16]ElementType, len(fieldsToAdd))
for i := 0; i < len(fields); i++ {
for i := 0; i < len(fieldsToAdd); i++ {
f := fields[i]
f := fieldsToAdd[i]
if f.Count == 0 {
f.Count = 1
}
@ -82,8 +94,8 @@ func (ub *UniformBuffer) addFields(fields []UniformBufferFieldInput) (totalSize
alignedOffset += alignmentBoundary - alignmentError
}
newField := UniformBufferField{Id: f.Id, Type: f.Type, AlignedOffset: alignedOffset, Count: f.Count}
ub.Fields = append(ub.Fields, newField)
newField := UniformBufferField{Id: f.Id, Type: f.Type, AlignedOffset: startAlignedOffset + alignedOffset, Count: f.Count}
*arrayToAddTo = append(*arrayToAddTo, newField)
// Prepare aligned offset for the next field.
//
@ -99,7 +111,21 @@ func (ub *UniformBuffer) addFields(fields []UniformBufferFieldInput) (totalSize
multiplier = 4
}
alignedOffset = newField.AlignedOffset + alignmentBoundary*f.Count*multiplier
if f.Type == DataTypeStruct {
subfieldsAlignedOffset := uint16(addUniformBufferFieldsToArray(alignedOffset, arrayToAddTo, f.Subfields))
// Pad structs to 16 byte boundary
subfieldsAlignmentError := subfieldsAlignedOffset % 16
if subfieldsAlignmentError != 0 {
subfieldsAlignedOffset += 16 - subfieldsAlignmentError
}
alignedOffset += subfieldsAlignedOffset * f.Count
} else {
alignedOffset = newField.AlignedOffset + alignmentBoundary*f.Count*multiplier - startAlignedOffset
}
}
return uint32(alignedOffset)
@ -345,6 +371,10 @@ func (ub *UniformBuffer) SetStruct(inputStruct any) {
}
}
// @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:
default:
assert.T(false, "Unknown uniform buffer data type passed. DataType '%d'", ubField.Type)
}
@ -634,7 +664,7 @@ func NewUniformBuffer(fields []UniformBufferFieldInput) UniformBuffer {
ub := UniformBuffer{}
ub.Size = ub.addFields(fields)
ub.Size = addUniformBufferFieldsToArray(0, &ub.Fields, fields)
gl.GenBuffers(1, &ub.Id)
if ub.Id == 0 {

44
main.go
View File

@ -799,6 +799,50 @@ func testUbos() {
gl.GetBufferSubData(gl.UNIFORM_BUFFER, 32+16*4+16+16*3+16*2+16*2+2*16*2+2*16*3, 2*16*4, gl.Ptr(&mat4Slice[0]))
fmt.Printf("f32=%f; v3=%s; f32Slice=%v; i32=%d; i32Arr=%v; v3Slice=%v; v4Slice=%v; mat2Slice=%v; mat3Slice=%v; mat4Slice=%v\n", x, v.String(), fArr, someInt32, i32Arr, vec3Slice, vec4Slice, mat2Slice, mat3Slice, mat4Slice)
//
// Ubo3
//
type TestUBO3_1 struct {
F32 float32
V3 gglm.Vec3
}
type TestUBO3_2 struct {
F32 float32
S TestUBO3_1
}
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: 3, Type: buffers.DataTypeVec3}, // 16 32
}}, // 32+16 = 48
})
fmt.Printf("\n==UBO3==\nSize=%d\nFields: %+v", ubo3.Size, ubo3.Fields)
ubo3.SetFloat32(0, 11)
ubo3.SetFloat32(2, 22)
ubo3.SetVec3(3, &gglm.Vec3{Data: [3]float32{33, 44, 55}})
// Bind the uniform block and the vertex buffer both to binding slot 2
gl.BindBufferBase(gl.UNIFORM_BUFFER, 2, ubo3.Id)
name := "Test2\x00"
uniformBlockIndex := gl.GetUniformBlockIndex(groundMat.ShaderProg.Id, gl.Str(name))
gl.UniformBlockBinding(groundMat.ShaderProg.Id, uniformBlockIndex, 2)
// s3 := TestUBO3_2{
// F32: 76.1,
// S: TestUBO3_1{
// F32: 89.9,
// V3: gglm.NewVec3(7.1, 7.2, 7.3),
// },
// }
// ubo3.SetStruct(s3)
}
func (g *Game) initFbos() {

View File

@ -70,6 +70,16 @@ out vec3 tangentSpotLightPositions[NUM_SPOT_LIGHTS];
out vec3 tangentSpotLightDirections[NUM_SPOT_LIGHTS];
out vec3 tangentPointLightPositions[NUM_POINT_LIGHTS];
struct Test1 {
float ff;
vec3 v3;
};
layout (std140) uniform Test2 {
float f1;
Test1 s;
};
void main()
{
vertUV0 = vertUV0In;