package render_ir import "core:mem" Planned_Command :: struct { handle: Command_Handle, record: Command_Record, target: Target_Handle, layer: Layer_Handle, layer_sort_base: i32, order: Order_Mode, sort_key: u64, sequence: u64, material: Material_Handle, culled: bool, cull_reason: Cull_Reason, } Cull_Reason :: enum { None, Target_Bounds, Clip_Bounds, } command_plan_destroy :: proc(plan: ^[dynamic]Planned_Command) { delete(plan^) plan^ = nil } planned_from_draw :: proc(frame: ^Frame_IR, handle: Command_Handle, record: Command_Record, draw: ^Draw_Command) -> Planned_Command { sequence := u64(handle) packet := draw.packet if packet.sequence != 0 { sequence = packet.sequence } layer_sort_base: i32 if layer, ok := get_layer(frame, packet.layer); ok { layer_sort_base = layer.sort_base } reason := Cull_Reason.None return { handle = handle, record = record, target = packet.target, layer = packet.layer, layer_sort_base = layer_sort_base, order = packet.order, sort_key = packet.sort_key, sequence = sequence, material = packet.material, culled = packet_is_culled(frame, packet, &reason), cull_reason = reason, } } bounds_valid :: proc(bounds: Bounds3) -> bool { return bounds.max.x > bounds.min.x && bounds.max.y > bounds.min.y } bounds_outside_rect :: proc(bounds: Bounds3, x, y, width, height: f32) -> bool { if !bounds_valid(bounds) { return false } return bounds.max.x < x || bounds.max.y < y || bounds.min.x > x + width || bounds.min.y > y + height } packet_effective_scissor :: proc(frame: ^Frame_IR, packet: Draw_Packet) -> (Scissor_Desc, bool) { if packet.clip != INVALID_CLIP { clip, ok := get_clip(frame, packet.clip) if !ok || clip.kind != .Rect { // Mask clips need planned stencil pass materialization before they can produce a scissor. return {}, false } return clip.rect, true } target, ok := get_target(frame, packet.target) if !ok || target.width == 0 || target.height == 0 { return {}, false } return {x = 0, y = 0, width = target.width, height = target.height}, true } packet_is_culled :: proc(frame: ^Frame_IR, packet: Draw_Packet, reason: ^Cull_Reason = nil) -> bool { if .Allow_Cull not_in packet.flags || !bounds_valid(packet.bounds) { return false } target, target_ok := get_target(frame, packet.target) if target_ok && target.width > 0 && target.height > 0 { if bounds_outside_rect(packet.bounds, 0, 0, f32(target.width), f32(target.height)) { if reason != nil { reason^ = .Target_Bounds } return true } } if packet.clip != INVALID_CLIP { clip, clip_ok := get_clip(frame, packet.clip) if clip_ok && clip.kind == .Rect { rect := clip.rect if bounds_outside_rect(packet.bounds, f32(rect.x), f32(rect.y), f32(rect.width), f32(rect.height)) { if reason != nil { reason^ = .Clip_Bounds } return true } } } return false } planned_from_dispatch :: proc(frame: ^Frame_IR, handle: Command_Handle, record: Command_Record, dispatch: ^Dispatch_Command) -> Planned_Command { sequence := u64(handle) if dispatch.sequence != 0 { sequence = dispatch.sequence } layer_sort_base: i32 if layer, ok := get_layer(frame, dispatch.layer); ok { layer_sort_base = layer.sort_base } return { handle = handle, record = record, target = dispatch.target, layer = dispatch.layer, layer_sort_base = layer_sort_base, order = .Strict, sort_key = dispatch.sort_key, sequence = sequence, } } planned_from_legacy :: proc(handle: Command_Handle, record: Command_Record) -> Planned_Command { return { handle = handle, record = record, order = .Strict, sequence = u64(handle), } } planned_command_less :: proc(a, b: Planned_Command) -> bool { if a.order == .Strict || b.order == .Strict || a.order == .Layered || b.order == .Layered { return a.sequence < b.sequence } if a.target != b.target { return a.target < b.target } if a.layer_sort_base != b.layer_sort_base { return a.layer_sort_base < b.layer_sort_base } if a.layer != b.layer { return a.layer < b.layer } if a.order != b.order { return a.order < b.order } if a.sort_key != b.sort_key { return a.sort_key < b.sort_key } if a.material != b.material { return a.material < b.material } return a.sequence < b.sequence } plan_insert_stable :: proc(plan: ^[dynamic]Planned_Command, command: Planned_Command) { insert_at := len(plan^) for i in 0.. insert_at; i -= 1 { plan^[i] = plan^[i - 1] } plan^[insert_at] = command } plan_flush_segment :: proc(plan: ^[dynamic]Planned_Command, segment: ^[dynamic]Planned_Command) { for command in segment^ { append(plan, command) } clear(segment) } plan_frame_commands :: proc(frame: ^Frame_IR, allocator: mem.Allocator = context.allocator) -> [dynamic]Planned_Command { plan := make([dynamic]Planned_Command, 0, len(frame.commands), allocator) segment := make([dynamic]Planned_Command, 0, len(frame.commands), allocator) defer delete(segment) for record, i in frame.commands { handle := Command_Handle(i + 1) command := planned_from_legacy(handle, record) switch record.kind { case .Draw: draw, ok := get_draw(frame, handle) if ok && draw_kind_is_packet(draw.kind) { command = planned_from_draw(frame, handle, record, draw) } case .Dispatch: dispatch, ok := get_dispatch(frame, handle) if ok { command = planned_from_dispatch(frame, handle, record, dispatch) } } if command.order == .Strict || command.order == .Layered { plan_flush_segment(&plan, &segment) append(&plan, command) } else { plan_insert_stable(&segment, command) } } plan_flush_segment(&plan, &segment) return plan }