Add basic reflection to wgpu struct and vertex attributes
This commit is contained in:
+31
-39
@@ -316,40 +316,48 @@ on_device :: proc "c" (
|
|||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Compile shader
|
// Compile shader
|
||||||
|
|
||||||
shader :: `
|
uniforms_struct := to_wgsl_struct(Camera_Uniform, "Uniforms")
|
||||||
struct Uniforms {
|
defer delete(uniforms_struct)
|
||||||
mvp: mat4x4<f32>,
|
vertex_input_struct := to_wgsl_struct(
|
||||||
};
|
Vertex,
|
||||||
|
"VertexInput",
|
||||||
|
locations = true,
|
||||||
|
)
|
||||||
|
defer delete(vertex_input_struct)
|
||||||
|
|
||||||
|
shader := fmt.tprintf(
|
||||||
|
`
|
||||||
|
%s
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> u: Uniforms;
|
@group(0) @binding(0) var<uniform> u: Uniforms;
|
||||||
@group(0) @binding(1) var t_diffuse: texture_2d<f32>;
|
@group(0) @binding(1) var t_diffuse: texture_2d<f32>;
|
||||||
@group(0) @binding(2) var s_diffuse: sampler;
|
@group(0) @binding(2) var s_diffuse: sampler;
|
||||||
|
|
||||||
struct VertexInput {
|
%s
|
||||||
@location(0) pos: vec3<f32>,
|
|
||||||
@location(1) col: vec3<f32>,
|
|
||||||
@location(2) uv: vec2<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOutput {
|
struct VertexOutput {{
|
||||||
@builtin(position) position: vec4<f32>,
|
@builtin(position) position: vec4<f32>,
|
||||||
@location(0) col: vec3<f32>,
|
@location(0) col: vec3<f32>,
|
||||||
@location(1) uv: vec2<f32>,
|
@location(1) uv: vec2<f32>,
|
||||||
};
|
}};
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn vs_main(in: VertexInput) -> VertexOutput {
|
fn vs_main(in: VertexInput) -> VertexOutput {{
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
out.position = u.mvp * vec4<f32>(in.pos, 1.0);
|
out.position = u.mvp * vec4<f32>(in.pos, 1.0);
|
||||||
out.col = in.col;
|
out.col = in.col;
|
||||||
out.uv = in.uv;
|
out.uv = in.uv;
|
||||||
return out;
|
return out;
|
||||||
}
|
}}
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {{
|
||||||
return textureSample(t_diffuse, s_diffuse, in.uv) * vec4<f32>(in.col, 1.0);
|
return textureSample(t_diffuse, s_diffuse, in.uv) * vec4<f32>(in.col, 1.0);
|
||||||
}`
|
}}
|
||||||
|
`,
|
||||||
|
uniforms_struct,
|
||||||
|
vertex_input_struct,
|
||||||
|
)
|
||||||
|
|
||||||
state.module = wgpu.DeviceCreateShaderModule(
|
state.module = wgpu.DeviceCreateShaderModule(
|
||||||
state.device,
|
state.device,
|
||||||
@@ -415,30 +423,14 @@ on_device :: proc "c" (
|
|||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Buffer shape
|
// Buffer shape
|
||||||
|
|
||||||
vertex_attributes := [?]wgpu.VertexAttribute {
|
vertex_attributes := to_vertex_attributes(Vertex)
|
||||||
{
|
|
||||||
format = .Float32x3,
|
|
||||||
offset = cast(u64)offset_of(Vertex, pos),
|
|
||||||
shaderLocation = 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
format = .Float32x3,
|
|
||||||
offset = cast(u64)offset_of(Vertex, col),
|
|
||||||
shaderLocation = 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
format = .Float32x2,
|
|
||||||
offset = cast(u64)offset_of(Vertex, uv),
|
|
||||||
shaderLocation = 2,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
vertex_layout := wgpu.VertexBufferLayout {
|
vertex_layout := wgpu.VertexBufferLayout {
|
||||||
stepMode = .Vertex,
|
stepMode = .Vertex,
|
||||||
arrayStride = size_of(Vertex),
|
arrayStride = size_of(Vertex),
|
||||||
attributeCount = len(vertex_attributes),
|
attributeCount = len(vertex_attributes),
|
||||||
attributes = raw_data(vertex_attributes[:]),
|
attributes = raw_data(vertex_attributes),
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Render pipeline
|
// Render pipeline
|
||||||
|
|||||||
@@ -0,0 +1,276 @@
|
|||||||
|
package platform
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:reflect"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
import "wgpu:wgpu"
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
// Types
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
// Public functions
|
||||||
|
|
||||||
|
// Generate a WGSL struct declaration from an Odin struct type.
|
||||||
|
// The caller owns the returned string (allocated on context.allocator).
|
||||||
|
//
|
||||||
|
// If locations is true, each member is prefixed with @location(n). This is
|
||||||
|
// required for vertex input structs; uniform structs should use false.
|
||||||
|
to_wgsl_struct :: proc(
|
||||||
|
$T: typeid,
|
||||||
|
name: string,
|
||||||
|
locations := false,
|
||||||
|
) -> string {
|
||||||
|
ti := base_type_info(type_info_of(T))
|
||||||
|
s, ok := ti.variant.(reflect.Type_Info_Struct)
|
||||||
|
if !ok do fmt.panicf("[wgpu_reflect] %s is not a struct", typeid_of(T))
|
||||||
|
|
||||||
|
b: strings.Builder
|
||||||
|
strings.write_string(&b, "struct ")
|
||||||
|
strings.write_string(&b, name)
|
||||||
|
strings.write_string(&b, " {\n")
|
||||||
|
|
||||||
|
names := s.names[:s.field_count]
|
||||||
|
types := s.types[:s.field_count]
|
||||||
|
for i in 0 ..< s.field_count {
|
||||||
|
wgsl_type, type_ok := to_wgsl_type(types[i])
|
||||||
|
if !type_ok {
|
||||||
|
fmt.panicf("[wgpu_reflect] cannot map field %s to WGSL", names[i])
|
||||||
|
}
|
||||||
|
if locations {
|
||||||
|
fmt.sbprintf(
|
||||||
|
&b,
|
||||||
|
"\t@location(%d) %s: %s,\n",
|
||||||
|
i,
|
||||||
|
names[i],
|
||||||
|
wgsl_type,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
fmt.sbprintf(&b, "\t%s: %s,\n", names[i], wgsl_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strings.write_string(&b, "};")
|
||||||
|
return strings.to_string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a []wgpu.VertexAttribute array that matches the fields of struct T.
|
||||||
|
// The caller owns the returned slice (allocated on context.allocator)
|
||||||
|
to_vertex_attributes :: proc($T: typeid) -> []wgpu.VertexAttribute {
|
||||||
|
ti := base_type_info(type_info_of(T))
|
||||||
|
s, ok := ti.variant.(reflect.Type_Info_Struct)
|
||||||
|
if !ok do fmt.panicf("[wgpu_reflect] %s is not a struct", typeid_of(T))
|
||||||
|
|
||||||
|
attrs := make([]wgpu.VertexAttribute, s.field_count)
|
||||||
|
names := s.names[:s.field_count]
|
||||||
|
types := s.types[:s.field_count]
|
||||||
|
offsets := s.offsets[:s.field_count]
|
||||||
|
|
||||||
|
for i in 0 ..< s.field_count {
|
||||||
|
format, format_ok := to_vertex_format(types[i])
|
||||||
|
if !format_ok {
|
||||||
|
fmt.panicf(
|
||||||
|
"[wgpu_reflect] cannot map field %s to a vertex format",
|
||||||
|
names[i],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs[i] = wgpu.VertexAttribute {
|
||||||
|
format = format,
|
||||||
|
offset = cast(u64)offsets[i],
|
||||||
|
shaderLocation = u32(i),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
// Private functions
|
||||||
|
|
||||||
|
// Strips named-type wrappers so we can inspect the underlying type
|
||||||
|
@(private = "file")
|
||||||
|
base_type_info :: proc(type_info: ^reflect.Type_Info) -> ^reflect.Type_Info {
|
||||||
|
#partial switch v in type_info.variant {
|
||||||
|
case reflect.Type_Info_Named:
|
||||||
|
return base_type_info(v.base)
|
||||||
|
}
|
||||||
|
return type_info
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map an Odin type to a WGSL scalar/vector/matrix type name
|
||||||
|
@(private = "file")
|
||||||
|
to_wgsl_type :: proc(type_info: ^reflect.Type_Info) -> (string, bool) {
|
||||||
|
ti := base_type_info(type_info)
|
||||||
|
|
||||||
|
#partial switch v in ti.variant {
|
||||||
|
case reflect.Type_Info_Float:
|
||||||
|
switch ti.size {
|
||||||
|
case 4:
|
||||||
|
return "f32", true
|
||||||
|
case 8:
|
||||||
|
return "f64", true
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Type_Info_Integer:
|
||||||
|
prefix := v.signed ? "i" : "u"
|
||||||
|
switch ti.size {
|
||||||
|
case 1:
|
||||||
|
return fmt.tprintf("%s8", prefix), true
|
||||||
|
case 2:
|
||||||
|
return fmt.tprintf("%s16", prefix), true
|
||||||
|
case 4:
|
||||||
|
return fmt.tprintf("%s32", prefix), true
|
||||||
|
case 8:
|
||||||
|
return fmt.tprintf("%s64", prefix), true
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Type_Info_Array:
|
||||||
|
elem_type, elem_ok := to_wgsl_type(v.elem)
|
||||||
|
if !elem_ok do return "", false
|
||||||
|
switch v.count {
|
||||||
|
case 2:
|
||||||
|
return fmt.tprintf("vec2<%s>", elem_type), true
|
||||||
|
case 3:
|
||||||
|
return fmt.tprintf("vec3<%s>", elem_type), true
|
||||||
|
case 4:
|
||||||
|
return fmt.tprintf("vec4<%s>", elem_type), true
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Type_Info_Matrix:
|
||||||
|
elem_type, elem_ok := to_wgsl_type(v.elem)
|
||||||
|
if !elem_ok do return "", false
|
||||||
|
return fmt.tprintf(
|
||||||
|
"mat%dx%d<%s>",
|
||||||
|
v.column_count,
|
||||||
|
v.row_count,
|
||||||
|
elem_type,
|
||||||
|
),
|
||||||
|
true
|
||||||
|
|
||||||
|
case:
|
||||||
|
fmt.panicf("[wgpu_reflect] unimplemented type translation: %v", ti)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map an Odin type to a wgpu.VertexFormat.
|
||||||
|
// Only handles the scalar/vector numeric formats commonly used as vertex attributes
|
||||||
|
@(private = "file")
|
||||||
|
to_vertex_format :: proc(
|
||||||
|
type_info: ^reflect.Type_Info,
|
||||||
|
) -> (
|
||||||
|
wgpu.VertexFormat,
|
||||||
|
bool,
|
||||||
|
) {
|
||||||
|
ti := base_type_info(type_info)
|
||||||
|
|
||||||
|
#partial switch v in ti.variant {
|
||||||
|
case reflect.Type_Info_Float:
|
||||||
|
if ti.size == 2 do return .Float16, true
|
||||||
|
else if ti.size == 4 do return .Float32, true
|
||||||
|
|
||||||
|
case reflect.Type_Info_Integer:
|
||||||
|
if v.signed {
|
||||||
|
switch ti.size {
|
||||||
|
case 1:
|
||||||
|
return .Sint8, true
|
||||||
|
case 2:
|
||||||
|
return .Sint16, true
|
||||||
|
case 4:
|
||||||
|
return .Sint32, true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch ti.size {
|
||||||
|
case 1:
|
||||||
|
return .Uint8, true
|
||||||
|
case 2:
|
||||||
|
return .Uint16, true
|
||||||
|
case 4:
|
||||||
|
return .Uint32, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Type_Info_Array:
|
||||||
|
elem := base_type_info(v.elem)
|
||||||
|
#partial switch ev in elem.variant {
|
||||||
|
case reflect.Type_Info_Float:
|
||||||
|
if elem.size == 2 {
|
||||||
|
switch v.count {
|
||||||
|
case 2:
|
||||||
|
return .Float16x2, true
|
||||||
|
case 4:
|
||||||
|
return .Float16x4, true
|
||||||
|
}
|
||||||
|
} else if elem.size == 4 {
|
||||||
|
switch v.count {
|
||||||
|
case 2:
|
||||||
|
return .Float32x2, true
|
||||||
|
case 3:
|
||||||
|
return .Float32x3, true
|
||||||
|
case 4:
|
||||||
|
return .Float32x4, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Type_Info_Integer:
|
||||||
|
if ev.signed {
|
||||||
|
switch elem.size {
|
||||||
|
case 1:
|
||||||
|
switch v.count {
|
||||||
|
case 2:
|
||||||
|
return .Sint8x2, true
|
||||||
|
case 4:
|
||||||
|
return .Sint8x4, true
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
switch v.count {
|
||||||
|
case 2:
|
||||||
|
return .Sint16x2, true
|
||||||
|
case 4:
|
||||||
|
return .Sint16x4, true
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
switch v.count {
|
||||||
|
case 2:
|
||||||
|
return .Sint32x2, true
|
||||||
|
case 3:
|
||||||
|
return .Sint32x3, true
|
||||||
|
case 4:
|
||||||
|
return .Sint32x4, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch elem.size {
|
||||||
|
case 1:
|
||||||
|
switch v.count {
|
||||||
|
case 2:
|
||||||
|
return .Uint8x2, true
|
||||||
|
case 4:
|
||||||
|
return .Uint8x4, true
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
switch (v.count) {
|
||||||
|
case 2:
|
||||||
|
return .Uint16x2, true
|
||||||
|
case 4:
|
||||||
|
return .Uint16x4, true
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
switch (v.count) {
|
||||||
|
case 2:
|
||||||
|
return .Uint32x2, true
|
||||||
|
case 3:
|
||||||
|
return .Uint32x3, true
|
||||||
|
case 4:
|
||||||
|
return .Uint32x4, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Float32, false
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user