Harbor

branch main
showing the latest snapshot on main
types.odin 6.8 KB · Plain text
shader/types.odin 0644 Raw
package shader

import "core:fmt"

// Resolved type representations (used after semantic analysis)

Scalar_Kind :: enum {
	Bool,
	Int,
	Uint,
	Float,
	Half,
}

Resolved_Type :: union {
	Type_Scalar,
	Type_Vector,
	Type_Matrix,
	Type_Struct_Resolved,
	Type_Array_Resolved,
	Type_Sampler,
	Type_Void,
}

Type_Scalar :: struct {
	kind: Scalar_Kind,
}

Type_Vector :: struct {
	elem: Scalar_Kind,
	size: int, // 2, 3, or 4
}

Type_Matrix :: struct {
	elem: Scalar_Kind,
	cols: int,
	rows: int,
}

Type_Struct_Resolved :: struct {
	name:   string,
	fields: []Resolved_Struct_Field,
}

Resolved_Struct_Field :: struct {
	name:       string,
	type:       ^Resolved_Type,
	attributes: []Ast_Attribute,
}

Type_Array_Resolved :: struct {
	elem:         ^Resolved_Type,
	size:         int,  // 0 for runtime-sized
}

Type_Sampler :: struct {
	kind: Sampler_Kind,
}

Sampler_Kind :: enum {
	Sampler2D,
	Sampler3D,
	SamplerCube,
	Sampler2DArray,
	Sampler2DShadow,
}

Type_Void :: struct {}

// Type helpers

type_equals :: proc(a, b: ^Resolved_Type) -> bool {
	if a == nil && b == nil do return true
	if a == nil || b == nil do return false

	a_val := a^
	b_val := b^

	switch av in a_val {
	case Type_Scalar:
		bv, ok := b_val.(Type_Scalar)
		return ok && av.kind == bv.kind
	case Type_Vector:
		bv, ok := b_val.(Type_Vector)
		return ok && av.elem == bv.elem && av.size == bv.size
	case Type_Matrix:
		bv, ok := b_val.(Type_Matrix)
		return ok && av.elem == bv.elem && av.cols == bv.cols && av.rows == bv.rows
	case Type_Struct_Resolved:
		bv, ok := b_val.(Type_Struct_Resolved)
		return ok && av.name == bv.name
	case Type_Array_Resolved:
		bv, ok := b_val.(Type_Array_Resolved)
		return ok && av.size == bv.size && type_equals(av.elem, bv.elem)
	case Type_Sampler:
		bv, ok := b_val.(Type_Sampler)
		return ok && av.kind == bv.kind
	case Type_Void:
		_, ok := b_val.(Type_Void)
		return ok
	}
	return false
}

type_to_string :: proc(t: ^Resolved_Type, allocator := context.allocator) -> string {
	if t == nil do return "void"

	switch v in t^ {
	case Type_Scalar:
		switch v.kind {
		case .Bool:  return "bool"
		case .Int:   return "int"
		case .Uint:  return "uint"
		case .Float: return "float"
		case .Half:  return "half"
		}
	case Type_Vector:
		prefix: string
		switch v.elem {
		case .Float: prefix = "vec"
		case .Int:   prefix = "ivec"
		case .Uint:  prefix = "uvec"
		case .Bool:  prefix = "bvec"
		case .Half:  prefix = "hvec"
		}
		return fmt.aprintf("%s%d", prefix, v.size, allocator = allocator)
	case Type_Matrix:
		if v.cols == v.rows {
			return fmt.aprintf("mat%d", v.cols, allocator = allocator)
		}
		return fmt.aprintf("mat%dx%d", v.cols, v.rows, allocator = allocator)
	case Type_Struct_Resolved:
		return v.name
	case Type_Array_Resolved:
		elem_str := type_to_string(v.elem, allocator)
		if v.size == 0 {
			return fmt.aprintf("[]%s", elem_str, allocator = allocator)
		}
		return fmt.aprintf("[%d]%s", v.size, elem_str, allocator = allocator)
	case Type_Sampler:
		switch v.kind {
		case .Sampler2D:       return "sampler2D"
		case .Sampler3D:       return "sampler3D"
		case .SamplerCube:     return "samplerCube"
		case .Sampler2DArray:  return "sampler2DArray"
		case .Sampler2DShadow: return "sampler2DShadow"
		}
	case Type_Void:
		return "void"
	}
	return "unknown"
}

is_scalar :: proc(t: ^Resolved_Type) -> bool {
	if t == nil do return false
	_, ok := t^.(Type_Scalar)
	return ok
}

is_vector :: proc(t: ^Resolved_Type) -> bool {
	if t == nil do return false
	_, ok := t^.(Type_Vector)
	return ok
}

is_matrix :: proc(t: ^Resolved_Type) -> bool {
	if t == nil do return false
	_, ok := t^.(Type_Matrix)
	return ok
}

is_numeric_scalar :: proc(t: ^Resolved_Type) -> bool {
	if t == nil do return false
	s, ok := t^.(Type_Scalar)
	return ok && (s.kind == .Float || s.kind == .Int || s.kind == .Uint || s.kind == .Half)
}

is_float_scalar :: proc(t: ^Resolved_Type) -> bool {
	if t == nil do return false
	s, ok := t^.(Type_Scalar)
	return ok && (s.kind == .Float || s.kind == .Half)
}

is_uint_scalar :: proc(t: ^Resolved_Type) -> bool {
	if t == nil do return false
	s, ok := t^.(Type_Scalar)
	return ok && s.kind == .Uint
}

is_float_type :: proc(t: ^Resolved_Type) -> bool {
	if t == nil do return false
	#partial switch v in t^ {
	case Type_Scalar: return v.kind == .Float || v.kind == .Half
	case Type_Vector: return v.elem == .Float || v.elem == .Half
	case Type_Matrix: return v.elem == .Float || v.elem == .Half
	case: return false
	}
}

vector_elem :: proc(t: ^Resolved_Type) -> (Scalar_Kind, int, bool) {
	if t == nil do return .Float, 0, false
	v, ok := t^.(Type_Vector)
	if !ok do return .Float, 0, false
	return v.elem, v.size, true
}

// Pre-built common types — heap-allocated singletons

make_type :: proc(t: Resolved_Type, allocator := context.allocator) -> ^Resolved_Type {
	ptr := new(Resolved_Type, allocator)
	ptr^ = t
	return ptr
}

// Global type pointers — initialized by @(init)
TYPE_VOID:      ^Resolved_Type
TYPE_BOOL:      ^Resolved_Type
TYPE_INT:       ^Resolved_Type
TYPE_UINT:      ^Resolved_Type
TYPE_FLOAT:     ^Resolved_Type
TYPE_HALF:      ^Resolved_Type
TYPE_VEC2:      ^Resolved_Type
TYPE_VEC3:      ^Resolved_Type
TYPE_VEC4:      ^Resolved_Type
TYPE_IVEC2:     ^Resolved_Type
TYPE_IVEC3:     ^Resolved_Type
TYPE_IVEC4:     ^Resolved_Type
TYPE_UVEC2:     ^Resolved_Type
TYPE_UVEC3:     ^Resolved_Type
TYPE_UVEC4:     ^Resolved_Type
TYPE_BVEC2:     ^Resolved_Type
TYPE_BVEC3:     ^Resolved_Type
TYPE_BVEC4:     ^Resolved_Type
TYPE_MAT2:      ^Resolved_Type
TYPE_MAT3:      ^Resolved_Type
TYPE_MAT4:      ^Resolved_Type
TYPE_SAMPLER2D:        ^Resolved_Type
TYPE_SAMPLER2D_SHADOW: ^Resolved_Type

_types_initialized := false

init_builtin_types :: proc() {
	_types_initialized = true
	TYPE_VOID      = make_type(Type_Void{})
	TYPE_BOOL      = make_type(Type_Scalar{.Bool})
	TYPE_INT       = make_type(Type_Scalar{.Int})
	TYPE_UINT      = make_type(Type_Scalar{.Uint})
	TYPE_FLOAT     = make_type(Type_Scalar{.Float})
	TYPE_HALF      = make_type(Type_Scalar{.Half})
	TYPE_VEC2      = make_type(Type_Vector{.Float, 2})
	TYPE_VEC3      = make_type(Type_Vector{.Float, 3})
	TYPE_VEC4      = make_type(Type_Vector{.Float, 4})
	TYPE_IVEC2     = make_type(Type_Vector{.Int, 2})
	TYPE_IVEC3     = make_type(Type_Vector{.Int, 3})
	TYPE_IVEC4     = make_type(Type_Vector{.Int, 4})
	TYPE_UVEC2     = make_type(Type_Vector{.Uint, 2})
	TYPE_UVEC3     = make_type(Type_Vector{.Uint, 3})
	TYPE_UVEC4     = make_type(Type_Vector{.Uint, 4})
	TYPE_BVEC2     = make_type(Type_Vector{.Bool, 2})
	TYPE_BVEC3     = make_type(Type_Vector{.Bool, 3})
	TYPE_BVEC4     = make_type(Type_Vector{.Bool, 4})
	TYPE_MAT2      = make_type(Type_Matrix{.Float, 2, 2})
	TYPE_MAT3      = make_type(Type_Matrix{.Float, 3, 3})
	TYPE_MAT4      = make_type(Type_Matrix{.Float, 4, 4})
	TYPE_SAMPLER2D        = make_type(Type_Sampler{.Sampler2D})
	TYPE_SAMPLER2D_SHADOW = make_type(Type_Sampler{.Sampler2DShadow})
}