Harbor

branch main
showing the latest snapshot on main
planner.odin 5.8 KB · Plain text
gpu/render_ir/planner.odin 0644 Raw
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..<len(plan^) {
		if planned_command_less(command, plan^[i]) {
			insert_at = i
			break
		}
	}
	append_elem := command
	append(plan, append_elem)
	for i := len(plan^) - 1; i > 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
}