package render_ir import "core:mem" import glsl "core:math/linalg/glsl" DEFAULT_RESOURCE_CAP :: 64 DEFAULT_PASS_CAP :: 16 DEFAULT_PIPELINE_CAP :: 32 DEFAULT_SHADER_CAP :: 32 DEFAULT_DESCRIPTOR_LAYOUT_CAP :: 32 DEFAULT_DESCRIPTOR_SET_CAP :: 64 DEFAULT_COMMAND_CAP :: 256 MAX_DESCRIPTOR_BINDINGS :: 16 MAX_DESCRIPTOR_SETS :: 8 MAX_COMPUTE_BINDINGS :: MAX_DESCRIPTOR_BINDINGS MAX_RENDER_TARGETS :: 8 Resource_Handle :: distinct u32 Pass_Handle :: distinct u32 Pipeline_Handle :: distinct u32 Shader_Handle :: distinct u32 Descriptor_Layout_Handle :: distinct u32 Descriptor_Set_Handle :: distinct u32 Target_Handle :: distinct u32 Layer_Handle :: distinct u32 Clip_Handle :: distinct u32 Mask_Handle :: distinct u32 Material_Handle :: distinct u32 Geometry_Handle :: distinct u32 Command_Handle :: distinct u32 INVALID_RESOURCE :: Resource_Handle(0) INVALID_PASS :: Pass_Handle(0) INVALID_PIPELINE :: Pipeline_Handle(0) INVALID_SHADER :: Shader_Handle(0) INVALID_DESCRIPTOR_LAYOUT :: Descriptor_Layout_Handle(0) INVALID_DESCRIPTOR_SET :: Descriptor_Set_Handle(0) INVALID_TARGET :: Target_Handle(0) INVALID_LAYER :: Layer_Handle(0) INVALID_CLIP :: Clip_Handle(0) INVALID_MASK :: Mask_Handle(0) INVALID_MATERIAL :: Material_Handle(0) INVALID_GEOMETRY :: Geometry_Handle(0) INVALID_COMMAND :: Command_Handle(0) Resource_Kind :: enum { Buffer, Texture, Sampler, } // Render_IR keeps its own backend-neutral vocabulary; lowering maps to backend enums by switch. Resource_Lifetime :: enum { Persistent, Transient, Imported, } Buffer_Usage :: enum { Vertex, Index, Uniform, Storage, Indirect_Argument, Transfer_Src, Transfer_Dst, } Buffer_Usage_Flags :: bit_set[Buffer_Usage] Memory_Property :: enum { Device_Local, Host_Visible, Host_Coherent, } Memory_Property_Flags :: bit_set[Memory_Property] Format :: enum { Undefined, R8G8B8A8_SRGB, R8G8B8A8_UNORM, B8G8R8A8_SRGB, D32_SFLOAT, D32_SFLOAT_S8_UINT, D24_UNORM_S8_UINT, } Texture_Usage :: enum { Sampled, Color_Attachment, Depth_Stencil_Attachment, Transfer_Dst, Transfer_Src, } Texture_Usage_Flags :: bit_set[Texture_Usage] Filter :: enum { Nearest, Linear, } Address_Mode :: enum { Repeat, Clamp_To_Edge, Clamp_To_Border, } Compare_Op :: enum { Never, Less, Less_Or_Equal, Always, } Descriptor_Type :: enum { Combined_Image_Sampler, Uniform_Buffer, Storage_Buffer, } Pass_Kind :: enum { Compute, Render, Transfer, Present, } Render_Target_Kind :: enum { Color, Depth, } Attachment_Load_Op :: enum { Load, Clear, Dont_Care, } Attachment_Store_Op :: enum { Store, Dont_Care, } Pipeline_Kind :: enum { Graphics, Compute, } Layout_Variant :: enum { PNU, PN, PNUC, PNUT, PNUCT, } Topology :: enum { Triangle_List, Triangle_Strip, Line_List, Point_List, } Cull_Mode :: enum { None, Front, Back, Front_And_Back, } Front_Face :: enum { Counter_Clockwise, Clockwise, } Blend_Mode :: enum { Alpha, Additive, Premultiplied_Alpha, } Draw_Flag :: enum { Allow_Cull, Transparent, Writes_Depth, Reads_Depth, } Draw_Flags :: bit_set[Draw_Flag] Shader_Stage :: enum { Vertex, Fragment, Compute, } Shader_Stage_Flags :: bit_set[Shader_Stage] Shader_Source_Kind :: enum { Source, File, Bytecode, } Shader_Language :: enum { Luma, } Command_Kind :: enum { Draw, Dispatch, } Draw_Kind :: enum { Raw, Mesh, Indexed_Mesh, Vertex_Stream, Quad_Stream, Line_Stream, Point_Stream, Glyph_Quad_Stream, Indirect, External, } Geometry_Kind :: enum { None, Raw, Mesh, Indexed_Mesh, Vertex_Stream, Quad_Stream, Line_Stream, Point_Stream, Glyph_Quad_Stream, Indirect, } Geometry_View :: struct { kind: Geometry_Kind, resource: Resource_Handle, vertex_count: u32, index_count: u32, first_vertex: u32, first_index: u32, } Instance_View :: struct { resource: Resource_Handle, offset: u64, count: u32, stride: u32, indirect: Resource_Handle, } Material_Desc :: struct { name: string, pipeline: Pipeline_Handle, descriptor_set: Descriptor_Set_Handle, texture_id: u32, normal_map_texture_id: u32, base_color: [4]f32, double_sided: bool, metallic: f32, roughness: f32, reflectance: f32, emissive: [4]f32, blend_mode: Blend_Mode, enable_depth_test: bool, enable_depth_write: bool, } Draw_Packet :: struct { target: Target_Handle, layer: Layer_Handle, sort_key: u64, sequence: u64, bounds: Bounds3, clip: Clip_Handle, transform: glsl.mat4x4, material: Material_Handle, geometry: Geometry_View, instances: Instance_View, flags: Draw_Flags, order: Order_Mode, } Buffer_Desc :: struct { size: u64, usage: Buffer_Usage_Flags, memory: Memory_Property_Flags, } Texture_Desc :: struct { width: u32, height: u32, format: Format, usage: Texture_Usage_Flags, } Sampler_Desc :: struct { mag_filter: Filter, min_filter: Filter, address_mode_u: Address_Mode, address_mode_v: Address_Mode, enable_aniso: bool, enable_compare: bool, compare_op: Compare_Op, mipmap_mode: Filter, } Resource_Desc :: struct { kind: Resource_Kind, lifetime: Resource_Lifetime, name: string, buffer: Buffer_Desc, texture: Texture_Desc, sampler: Sampler_Desc, } Pass_Desc :: struct { kind: Pass_Kind, name: string, // Attachment format is source-of-truth for render-pass compatibility. // Pipeline formats must match the target pass during future graph lowering. color_targets: [MAX_RENDER_TARGETS]Render_Target_Desc, color_target_count: u8, depth_target: Render_Target_Desc, has_depth_target: bool, viewport: Viewport_Desc, has_viewport: bool, scissor: Scissor_Desc, has_scissor: bool, } Render_Target_Desc :: struct { resource: Resource_Handle, kind: Render_Target_Kind, format: Format, load_op: Attachment_Load_Op, store_op: Attachment_Store_Op, clear_color: [4]f32, // used when kind == Color and load_op == Clear clear_depth: f32, // used when kind == Depth and load_op == Clear } Viewport_Desc :: struct { x, y: f32, width, height: f32, min_depth, max_depth: f32, } Scissor_Desc :: struct { x, y: i32, width, height: u32, } Bounds3 :: struct { min: glsl.vec3, max: glsl.vec3, } Target_Kind :: enum { Present, Offscreen, } Target_Desc :: struct { kind: Target_Kind, name: string, width, height: u32, pass: Pass_Handle, color_format: Format, depth_format: Format, color_resource: Resource_Handle, depth_resource: Resource_Handle, clear_color: [4]f32, clear_depth: f32, } Order_Mode :: enum { Strict, Layered, Sortable, Depth, } Layer_Desc :: struct { target: Target_Handle, name: string, sort_base: i32, order: Order_Mode, } Clip_Kind :: enum { None, Rect, Mask, } Mask_Source_Kind :: enum { None, Geometry_Coverage, } Mask_Desc :: struct { target: Target_Handle, name: string, source_kind: Mask_Source_Kind, layout_variant: Layout_Variant, coverage_resource: Resource_Handle, index_resource: Resource_Handle, vertex_count: u32, index_count: u32, } Clip_Desc :: struct { target: Target_Handle, kind: Clip_Kind, rect: Scissor_Desc, mask: Mask_Handle, } Pipeline_Desc :: struct { kind: Pipeline_Kind, name: string, descriptor_layouts: [MAX_DESCRIPTOR_SETS]Descriptor_Layout_Handle, descriptor_layout_count: u8, graphics: Graphics_Pipeline_State, compute: Compute_Pipeline_State, } Shader_Desc :: struct { stage: Shader_Stage, source_kind: Shader_Source_Kind, language: Shader_Language, name: string, source: string, path: string, } // First-pass render target identity. Lowering maps these formats to backend render-pass state. Graphics_Pipeline_State :: struct { vertex_shader: Shader_Handle, fragment_shader: Shader_Handle, layout_variant: Layout_Variant, has_instance_layout: bool, instance_layout_variant: Layout_Variant, topology: Topology, cull_mode: Cull_Mode, front_face: Front_Face, enable_blending: bool, blend_mode: Blend_Mode, enable_depth_test: bool, enable_depth_write: bool, color_format: Format, depth_format: Format, push_constant_size: u32, push_constant_stages: Shader_Stage_Flags, } Compute_Pipeline_State :: struct { shader: Shader_Handle, push_constant_size: u32, push_constant_stages: Shader_Stage_Flags, } Descriptor_Binding_Desc :: struct { binding: u32, type: Descriptor_Type, count: u32, stages: Shader_Stage_Flags, } Descriptor_Set_Layout_Desc :: struct { name: string, bindings: [MAX_DESCRIPTOR_BINDINGS]Descriptor_Binding_Desc, binding_count: u8, } Descriptor_Resource_Binding :: struct { binding: u32, type: Descriptor_Type, resource: Resource_Handle, sampler: Resource_Handle, size: u64, } Descriptor_Set_Desc :: struct { name: string, layout: Descriptor_Layout_Handle, bindings: [MAX_DESCRIPTOR_BINDINGS]Descriptor_Resource_Binding, binding_count: u8, } Draw_Command :: struct { kind: Draw_Kind, pass: Pass_Handle, pipeline: Pipeline_Handle, vertex_count: u32, instance_count: u32, packet: Draw_Packet, } Dispatch_Command :: struct { pass: Pass_Handle, pipeline: Pipeline_Handle, target: Target_Handle, layer: Layer_Handle, sort_key: u64, sequence: u64, groups: [3]u32, shader_id: u32, storage_bindings: [MAX_COMPUTE_BINDINGS]Descriptor_Resource_Binding, storage_binding_count: u8, push_constants: []u8, barrier_after: bool, } // Command handles are 1-based indexes into Frame_IR.commands. // Command_Record.index is a 0-based index into the kind-specific payload array. Command_Record :: struct { kind: Command_Kind, index: u32, } Frame_IR :: struct { resources: [dynamic]Resource_Desc, passes: [dynamic]Pass_Desc, pipelines: [dynamic]Pipeline_Desc, shaders: [dynamic]Shader_Desc, descriptor_layouts: [dynamic]Descriptor_Set_Layout_Desc, descriptor_sets: [dynamic]Descriptor_Set_Desc, targets: [dynamic]Target_Desc, layers: [dynamic]Layer_Desc, clips: [dynamic]Clip_Desc, masks: [dynamic]Mask_Desc, materials: [dynamic]Material_Desc, commands: [dynamic]Command_Record, draw_commands: [dynamic]Draw_Command, dispatch_commands: [dynamic]Dispatch_Command, } Frame_Builder :: struct { ir: ^Frame_IR, default_pass: Pass_Handle, default_pipeline: Pipeline_Handle, } init_frame_ir :: proc( resource_cap: int = DEFAULT_RESOURCE_CAP, pass_cap: int = DEFAULT_PASS_CAP, pipeline_cap: int = DEFAULT_PIPELINE_CAP, shader_cap: int = DEFAULT_SHADER_CAP, descriptor_layout_cap: int = DEFAULT_DESCRIPTOR_LAYOUT_CAP, descriptor_set_cap: int = DEFAULT_DESCRIPTOR_SET_CAP, target_cap: int = DEFAULT_PASS_CAP, layer_cap: int = DEFAULT_PASS_CAP, clip_cap: int = DEFAULT_PASS_CAP, mask_cap: int = DEFAULT_PASS_CAP, material_cap: int = DEFAULT_PIPELINE_CAP, command_cap: int = DEFAULT_COMMAND_CAP, draw_cap: int = DEFAULT_COMMAND_CAP, dispatch_cap: int = DEFAULT_COMMAND_CAP, allocator: mem.Allocator = context.allocator, ) -> Frame_IR { return Frame_IR{ resources = make([dynamic]Resource_Desc, 0, resource_cap, allocator), passes = make([dynamic]Pass_Desc, 0, pass_cap, allocator), pipelines = make([dynamic]Pipeline_Desc, 0, pipeline_cap, allocator), shaders = make([dynamic]Shader_Desc, 0, shader_cap, allocator), descriptor_layouts = make([dynamic]Descriptor_Set_Layout_Desc, 0, descriptor_layout_cap, allocator), descriptor_sets = make([dynamic]Descriptor_Set_Desc, 0, descriptor_set_cap, allocator), targets = make([dynamic]Target_Desc, 0, target_cap, allocator), layers = make([dynamic]Layer_Desc, 0, layer_cap, allocator), clips = make([dynamic]Clip_Desc, 0, clip_cap, allocator), masks = make([dynamic]Mask_Desc, 0, mask_cap, allocator), materials = make([dynamic]Material_Desc, 0, material_cap, allocator), commands = make([dynamic]Command_Record, 0, command_cap, allocator), draw_commands = make([dynamic]Draw_Command, 0, draw_cap, allocator), dispatch_commands = make([dynamic]Dispatch_Command, 0, dispatch_cap, allocator), } } reset_frame_ir :: proc(ir: ^Frame_IR) { destroy_dispatch_payloads(ir) clear(&ir.resources) clear(&ir.passes) clear(&ir.pipelines) clear(&ir.shaders) clear(&ir.descriptor_layouts) clear(&ir.descriptor_sets) clear(&ir.targets) clear(&ir.layers) clear(&ir.clips) clear(&ir.masks) clear(&ir.materials) clear(&ir.commands) clear(&ir.draw_commands) clear(&ir.dispatch_commands) } // Terminal cleanup. Use reset_frame_ir for reuse. destroy_frame_ir :: proc(ir: ^Frame_IR) { destroy_dispatch_payloads(ir) delete(ir.resources) delete(ir.passes) delete(ir.pipelines) delete(ir.shaders) delete(ir.descriptor_layouts) delete(ir.descriptor_sets) delete(ir.targets) delete(ir.layers) delete(ir.clips) delete(ir.masks) delete(ir.materials) delete(ir.commands) delete(ir.draw_commands) delete(ir.dispatch_commands) ir^ = {} } @(private) destroy_dispatch_payloads :: proc(ir: ^Frame_IR) { for &dispatch in ir.dispatch_commands { if len(dispatch.push_constants) > 0 { delete(dispatch.push_constants) dispatch.push_constants = nil } } } add_resource :: proc(ir: ^Frame_IR, desc: Resource_Desc) -> Resource_Handle { append(&ir.resources, desc) return Resource_Handle(len(ir.resources)) } add_buffer :: proc( ir: ^Frame_IR, name: string, size: u64, usage: Buffer_Usage_Flags, memory: Memory_Property_Flags, lifetime: Resource_Lifetime = .Persistent, ) -> Resource_Handle { return add_resource(ir, { kind = .Buffer, lifetime = lifetime, name = name, buffer = { size = size, usage = usage, memory = memory, }, }) } add_texture :: proc( ir: ^Frame_IR, name: string, width, height: u32, format: Format, usage: Texture_Usage_Flags, lifetime: Resource_Lifetime = .Persistent, ) -> Resource_Handle { return add_resource(ir, { kind = .Texture, lifetime = lifetime, name = name, texture = { width = width, height = height, format = format, usage = usage, }, }) } add_sampler :: proc( ir: ^Frame_IR, name: string, desc: Sampler_Desc, lifetime: Resource_Lifetime = .Persistent, ) -> Resource_Handle { return add_resource(ir, { kind = .Sampler, lifetime = lifetime, name = name, sampler = desc, }) } add_pass :: proc(ir: ^Frame_IR, desc: Pass_Desc) -> Pass_Handle { append(&ir.passes, desc) return Pass_Handle(len(ir.passes)) } make_color_target :: proc( resource: Resource_Handle, format: Format, load_op: Attachment_Load_Op = .Clear, store_op: Attachment_Store_Op = .Store, clear_color: [4]f32 = {0, 0, 0, 1}, ) -> Render_Target_Desc { return { resource = resource, kind = .Color, format = format, load_op = load_op, store_op = store_op, clear_color = clear_color, } } make_depth_target :: proc( resource: Resource_Handle, format: Format, load_op: Attachment_Load_Op = .Clear, store_op: Attachment_Store_Op = .Store, clear_depth: f32 = 1, ) -> Render_Target_Desc { return { resource = resource, kind = .Depth, format = format, load_op = load_op, store_op = store_op, clear_depth = clear_depth, } } pass_add_color_target :: proc(pass: ^Pass_Desc, target: Render_Target_Desc) -> bool { if pass.color_target_count >= MAX_RENDER_TARGETS { return false } pass.color_targets[pass.color_target_count] = target pass.color_target_count += 1 return true } pass_set_depth_target :: proc(pass: ^Pass_Desc, target: Render_Target_Desc) { pass.depth_target = target pass.has_depth_target = true } pass_set_viewport :: proc(pass: ^Pass_Desc, viewport: Viewport_Desc) { pass.viewport = viewport pass.has_viewport = true } pass_set_scissor :: proc(pass: ^Pass_Desc, scissor: Scissor_Desc) { pass.scissor = scissor pass.has_scissor = true } add_pipeline :: proc(ir: ^Frame_IR, desc: Pipeline_Desc) -> Pipeline_Handle { append(&ir.pipelines, desc) return Pipeline_Handle(len(ir.pipelines)) } default_graphics_pipeline_state :: proc() -> Graphics_Pipeline_State { return { layout_variant = .PNU, instance_layout_variant = .PNU, topology = .Triangle_List, cull_mode = .Back, front_face = .Counter_Clockwise, enable_blending = true, blend_mode = .Alpha, color_format = .B8G8R8A8_SRGB, } } default_compute_pipeline_state :: proc(shader: Shader_Handle) -> Compute_Pipeline_State { return { shader = shader, } } add_graphics_pipeline :: proc(ir: ^Frame_IR, name: string, state: Graphics_Pipeline_State) -> Pipeline_Handle { return add_pipeline(ir, { kind = .Graphics, name = name, graphics = state, }) } add_compute_pipeline :: proc(ir: ^Frame_IR, name: string, state: Compute_Pipeline_State) -> Pipeline_Handle { return add_pipeline(ir, { kind = .Compute, name = name, compute = state, }) } add_shader :: proc(ir: ^Frame_IR, desc: Shader_Desc) -> Shader_Handle { append(&ir.shaders, desc) return Shader_Handle(len(ir.shaders)) } add_shader_source :: proc(ir: ^Frame_IR, name: string, stage: Shader_Stage, source: string, language: Shader_Language = .Luma) -> Shader_Handle { return add_shader(ir, { stage = stage, source_kind = .Source, language = language, name = name, source = source, }) } add_shader_file :: proc(ir: ^Frame_IR, name: string, stage: Shader_Stage, path: string, language: Shader_Language = .Luma) -> Shader_Handle { return add_shader(ir, { stage = stage, source_kind = .File, language = language, name = name, path = path, }) } make_descriptor_set_layout :: proc(name: string) -> Descriptor_Set_Layout_Desc { return {name = name} } descriptor_layout_add_binding :: proc( layout: ^Descriptor_Set_Layout_Desc, binding: u32, type: Descriptor_Type, count: u32 = 1, stages: Shader_Stage_Flags = {}, ) -> bool { if layout.binding_count >= MAX_DESCRIPTOR_BINDINGS { return false } layout.bindings[layout.binding_count] = { binding = binding, type = type, count = count, stages = stages, } layout.binding_count += 1 return true } add_descriptor_set_layout :: proc(ir: ^Frame_IR, desc: Descriptor_Set_Layout_Desc) -> Descriptor_Layout_Handle { append(&ir.descriptor_layouts, desc) return Descriptor_Layout_Handle(len(ir.descriptor_layouts)) } make_descriptor_set :: proc(name: string, layout: Descriptor_Layout_Handle) -> Descriptor_Set_Desc { return {name = name, layout = layout} } descriptor_set_add_binding :: proc(set: ^Descriptor_Set_Desc, desc: Descriptor_Resource_Binding) -> bool { if set.binding_count >= MAX_DESCRIPTOR_BINDINGS { return false } set.bindings[set.binding_count] = desc set.binding_count += 1 return true } descriptor_set_bind_buffer :: proc( set: ^Descriptor_Set_Desc, binding: u32, type: Descriptor_Type, resource: Resource_Handle, size: u64 = 0, ) -> bool { return descriptor_set_add_binding(set, { binding = binding, type = type, resource = resource, size = size, }) } descriptor_set_bind_texture_sampler :: proc( set: ^Descriptor_Set_Desc, binding: u32, texture: Resource_Handle, sampler: Resource_Handle, ) -> bool { return descriptor_set_add_binding(set, { binding = binding, type = .Combined_Image_Sampler, resource = texture, sampler = sampler, }) } add_descriptor_set :: proc(ir: ^Frame_IR, desc: Descriptor_Set_Desc) -> Descriptor_Set_Handle { append(&ir.descriptor_sets, desc) return Descriptor_Set_Handle(len(ir.descriptor_sets)) } add_target :: proc(ir: ^Frame_IR, desc: Target_Desc) -> Target_Handle { append(&ir.targets, desc) return Target_Handle(len(ir.targets)) } add_layer :: proc(ir: ^Frame_IR, desc: Layer_Desc) -> Layer_Handle { append(&ir.layers, desc) return Layer_Handle(len(ir.layers)) } add_clip :: proc(ir: ^Frame_IR, desc: Clip_Desc) -> Clip_Handle { append(&ir.clips, desc) return Clip_Handle(len(ir.clips)) } add_mask :: proc(ir: ^Frame_IR, desc: Mask_Desc) -> Mask_Handle { append(&ir.masks, desc) return Mask_Handle(len(ir.masks)) } add_material :: proc(ir: ^Frame_IR, desc: Material_Desc) -> Material_Handle { append(&ir.materials, desc) return Material_Handle(len(ir.materials)) } pipeline_add_descriptor_layout :: proc(desc: ^Pipeline_Desc, layout: Descriptor_Layout_Handle) -> bool { if desc.descriptor_layout_count >= MAX_DESCRIPTOR_SETS { return false } desc.descriptor_layouts[desc.descriptor_layout_count] = layout desc.descriptor_layout_count += 1 return true } add_draw :: proc(ir: ^Frame_IR, desc: Draw_Command) -> Command_Handle { payload_index := u32(len(ir.draw_commands)) append(&ir.draw_commands, desc) append(&ir.commands, Command_Record{kind = .Draw, index = payload_index}) return Command_Handle(len(ir.commands)) } add_draw_packet :: proc( ir: ^Frame_IR, pass: Pass_Handle, pipeline: Pipeline_Handle, kind: Draw_Kind, packet: Draw_Packet, vertex_count: u32 = 0, instance_count: u32 = 1, ) -> Command_Handle { p := packet p.sequence = u64(len(ir.commands) + 1) return add_draw(ir, { kind = kind, pass = pass, pipeline = pipeline, vertex_count = vertex_count, instance_count = instance_count, packet = p, }) } init_frame_builder :: proc(ir: ^Frame_IR, default_pass: Pass_Handle, default_pipeline: Pipeline_Handle) -> Frame_Builder { return Frame_Builder{ ir = ir, default_pass = default_pass, default_pipeline = default_pipeline, } } builder_draw_packet :: proc( builder: ^Frame_Builder, kind: Draw_Kind, packet: Draw_Packet, vertex_count: u32 = 0, instance_count: u32 = 1, ) -> Command_Handle { return add_draw_packet( builder.ir, builder.default_pass, builder.default_pipeline, kind, packet, vertex_count, instance_count, ) } add_dispatch :: proc(ir: ^Frame_IR, desc: Dispatch_Command) -> Command_Handle { command := desc command.sequence = u64(len(ir.commands) + 1) if len(desc.push_constants) > 0 { command.push_constants = make([]u8, len(desc.push_constants)) mem.copy(raw_data(command.push_constants), raw_data(desc.push_constants), len(desc.push_constants)) } else { command.push_constants = nil } payload_index := u32(len(ir.dispatch_commands)) append(&ir.dispatch_commands, command) append(&ir.commands, Command_Record{kind = .Dispatch, index = payload_index}) return Command_Handle(len(ir.commands)) } add_compute_dispatch :: proc( ir: ^Frame_IR, pass: Pass_Handle, pipeline: Pipeline_Handle, shader_id: u32, groups: [3]u32, storage_bindings: []Descriptor_Resource_Binding, push_constants: rawptr = nil, push_constants_size: u32 = 0, barrier_after: bool = true, ) -> Command_Handle { push_data: []u8 if push_constants != nil && push_constants_size > 0 { push_data = (cast([^]u8)push_constants)[:int(push_constants_size)] } desc := Dispatch_Command{ pass = pass, pipeline = pipeline, groups = groups, shader_id = shader_id, push_constants = push_data, barrier_after = barrier_after, } count := min(len(storage_bindings), MAX_COMPUTE_BINDINGS) desc.storage_binding_count = u8(count) for i in 0.. (^Resource_Desc, bool) { if handle == INVALID_RESOURCE || int(handle) > len(ir.resources) { return nil, false } return &ir.resources[int(handle) - 1], true } get_pass :: proc(ir: ^Frame_IR, handle: Pass_Handle) -> (^Pass_Desc, bool) { if handle == INVALID_PASS || int(handle) > len(ir.passes) { return nil, false } return &ir.passes[int(handle) - 1], true } get_pipeline :: proc(ir: ^Frame_IR, handle: Pipeline_Handle) -> (^Pipeline_Desc, bool) { if handle == INVALID_PIPELINE || int(handle) > len(ir.pipelines) { return nil, false } return &ir.pipelines[int(handle) - 1], true } get_shader :: proc(ir: ^Frame_IR, handle: Shader_Handle) -> (^Shader_Desc, bool) { if handle == INVALID_SHADER || int(handle) > len(ir.shaders) { return nil, false } return &ir.shaders[int(handle) - 1], true } get_descriptor_set_layout :: proc(ir: ^Frame_IR, handle: Descriptor_Layout_Handle) -> (^Descriptor_Set_Layout_Desc, bool) { if handle == INVALID_DESCRIPTOR_LAYOUT || int(handle) > len(ir.descriptor_layouts) { return nil, false } return &ir.descriptor_layouts[int(handle) - 1], true } get_descriptor_set :: proc(ir: ^Frame_IR, handle: Descriptor_Set_Handle) -> (^Descriptor_Set_Desc, bool) { if handle == INVALID_DESCRIPTOR_SET || int(handle) > len(ir.descriptor_sets) { return nil, false } return &ir.descriptor_sets[int(handle) - 1], true } get_target :: proc(ir: ^Frame_IR, handle: Target_Handle) -> (^Target_Desc, bool) { if handle == INVALID_TARGET || int(handle) > len(ir.targets) { return nil, false } return &ir.targets[int(handle) - 1], true } get_layer :: proc(ir: ^Frame_IR, handle: Layer_Handle) -> (^Layer_Desc, bool) { if handle == INVALID_LAYER || int(handle) > len(ir.layers) { return nil, false } return &ir.layers[int(handle) - 1], true } get_clip :: proc(ir: ^Frame_IR, handle: Clip_Handle) -> (^Clip_Desc, bool) { if handle == INVALID_CLIP || int(handle) > len(ir.clips) { return nil, false } return &ir.clips[int(handle) - 1], true } get_mask :: proc(ir: ^Frame_IR, handle: Mask_Handle) -> (^Mask_Desc, bool) { if handle == INVALID_MASK || int(handle) > len(ir.masks) { return nil, false } return &ir.masks[int(handle) - 1], true } get_material :: proc(ir: ^Frame_IR, handle: Material_Handle) -> (^Material_Desc, bool) { if handle == INVALID_MATERIAL || int(handle) > len(ir.materials) { return nil, false } return &ir.materials[int(handle) - 1], true } find_descriptor_binding :: proc(layout: ^Descriptor_Set_Layout_Desc, binding: u32) -> (^Descriptor_Binding_Desc, bool) { for i in 0.. (^Command_Record, bool) { if handle == INVALID_COMMAND || int(handle) > len(ir.commands) { return nil, false } return &ir.commands[int(handle) - 1], true } get_draw :: proc(ir: ^Frame_IR, handle: Command_Handle) -> (^Draw_Command, bool) { record, ok := get_command_record(ir, handle) if !ok || record.kind != .Draw || int(record.index) >= len(ir.draw_commands) { return nil, false } return &ir.draw_commands[record.index], true } get_dispatch :: proc(ir: ^Frame_IR, handle: Command_Handle) -> (^Dispatch_Command, bool) { record, ok := get_command_record(ir, handle) if !ok || record.kind != .Dispatch || int(record.index) >= len(ir.dispatch_commands) { return nil, false } return &ir.dispatch_commands[record.index], true } resource_count :: proc(ir: ^Frame_IR) -> int { return len(ir.resources) } pass_count :: proc(ir: ^Frame_IR) -> int { return len(ir.passes) } pipeline_count :: proc(ir: ^Frame_IR) -> int { return len(ir.pipelines) } shader_count :: proc(ir: ^Frame_IR) -> int { return len(ir.shaders) } descriptor_layout_count :: proc(ir: ^Frame_IR) -> int { return len(ir.descriptor_layouts) } descriptor_set_count :: proc(ir: ^Frame_IR) -> int { return len(ir.descriptor_sets) } target_count :: proc(ir: ^Frame_IR) -> int { return len(ir.targets) } layer_count :: proc(ir: ^Frame_IR) -> int { return len(ir.layers) } clip_count :: proc(ir: ^Frame_IR) -> int { return len(ir.clips) } mask_count :: proc(ir: ^Frame_IR) -> int { return len(ir.masks) } material_count :: proc(ir: ^Frame_IR) -> int { return len(ir.materials) } command_count :: proc(ir: ^Frame_IR) -> int { return len(ir.commands) } draw_kind_is_packet :: proc(kind: Draw_Kind) -> bool { switch kind { case .Mesh, .Indexed_Mesh, .Vertex_Stream, .Quad_Stream, .Line_Stream, .Point_Stream, .Glyph_Quad_Stream, .Indirect, .External: return true case .Raw: return false } return false } @(private) validate_descriptor_fail :: proc(diagnostics: ^Diagnostic_List, code: Diagnostic_Code, message: string) { if diagnostics != nil { add_error(diagnostics, code, message) } } validate_descriptor_set :: proc(ir: ^Frame_IR, handle: Descriptor_Set_Handle, diagnostics: ^Diagnostic_List = nil) -> bool { set, set_ok := get_descriptor_set(ir, handle) if !set_ok { validate_descriptor_fail(diagnostics, .Invalid_Descriptor, "invalid descriptor set handle") return false } layout, layout_ok := get_descriptor_set_layout(ir, set.layout) if !layout_ok { validate_descriptor_fail(diagnostics, .Invalid_Descriptor, "invalid descriptor set layout handle") return false } ok := true for i in 0..