package render_ir_tests import ir "../../render_ir" import "core:testing" make_planner_frame :: proc( ) -> ( frame: ir.Frame_IR, pass: ir.Pass_Handle, pipeline: ir.Pipeline_Handle, target: ir.Target_Handle, world: ir.Layer_Handle, ui: ir.Layer_Handle, mat_a: ir.Material_Handle, mat_b: ir.Material_Handle, ) { frame = ir.init_frame_ir() pass = ir.add_pass(&frame, {kind = .Render, name = "main"}) pipeline = ir.add_graphics_pipeline(&frame, "pipe", ir.default_graphics_pipeline_state()) target = ir.add_target( &frame, { kind = .Present, name = "present", width = 800, height = 600, color_format = .B8G8R8A8_SRGB, }, ) world = ir.add_layer( &frame, {target = target, name = "world", sort_base = 0, order = .Sortable}, ) ui = ir.add_layer(&frame, {target = target, name = "ui", sort_base = 10_000, order = .Strict}) mat_a = ir.add_material(&frame, {name = "a"}) mat_b = ir.add_material(&frame, {name = "b"}) return } @(test) test_planner_preserves_strict_sequence_as_barrier :: proc(t: ^testing.T) { frame, pass, pipeline, target, world, ui, mat_a, _ := make_planner_frame() defer ir.destroy_frame_ir(&frame) _ = ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = target, layer = world, sort_key = 30, material = mat_a, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, order = .Sortable, }, ) strict := ir.add_draw_packet( &frame, pass, pipeline, .Glyph_Quad_Stream, { target = target, layer = ui, sort_key = 0, geometry = {kind = .Glyph_Quad_Stream, vertex_count = 6}, instances = {count = 1}, order = .Strict, }, ) _ = ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = target, layer = world, sort_key = 10, material = mat_a, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, order = .Sortable, }, ) plan := ir.plan_frame_commands(&frame) defer ir.command_plan_destroy(&plan) testing.expect_value(t, len(plan), 3) testing.expect(t, plan[1].handle == strict) testing.expect_value(t, plan[0].sequence, u64(1)) testing.expect_value(t, plan[1].sequence, u64(2)) testing.expect_value(t, plan[2].sequence, u64(3)) } @(test) test_planner_preserves_layered_sequence_as_barrier :: proc(t: ^testing.T) { frame, pass, pipeline, target, world, _, mat_a, _ := make_planner_frame() defer ir.destroy_frame_ir(&frame) layered := ir.add_layer( &frame, {target = target, name = "overlay", sort_base = 500, order = .Layered}, ) _ = ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = target, layer = world, sort_key = 30, material = mat_a, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, order = .Sortable, }, ) layered_cmd := ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = target, layer = layered, sort_key = 0, material = mat_a, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, order = .Layered, }, ) _ = ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = target, layer = world, sort_key = 10, material = mat_a, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, order = .Sortable, }, ) plan := ir.plan_frame_commands(&frame) defer ir.command_plan_destroy(&plan) testing.expect_value(t, len(plan), 3) testing.expect(t, plan[1].handle == layered_cmd) testing.expect_value(t, plan[0].sequence, u64(1)) testing.expect_value(t, plan[1].sequence, u64(2)) testing.expect_value(t, plan[2].sequence, u64(3)) } @(test) test_planner_stable_sorts_sortable_packets :: proc(t: ^testing.T) { frame, pass, pipeline, target, world, _, mat_a, mat_b := make_planner_frame() defer ir.destroy_frame_ir(&frame) first_key_20 := ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = target, layer = world, sort_key = 20, material = mat_b, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, order = .Sortable, }, ) first_key_10 := ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = target, layer = world, sort_key = 10, material = mat_a, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, order = .Sortable, }, ) second_key_10 := ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = target, layer = world, sort_key = 10, material = mat_a, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, order = .Sortable, }, ) plan := ir.plan_frame_commands(&frame) defer ir.command_plan_destroy(&plan) testing.expect_value(t, len(plan), 3) testing.expect(t, plan[0].handle == first_key_10) testing.expect(t, plan[1].handle == second_key_10) testing.expect(t, plan[2].handle == first_key_20) testing.expect_value(t, plan[0].sequence, u64(2)) testing.expect_value(t, plan[1].sequence, u64(3)) } @(test) test_planner_orders_depth_packets_by_sort_key :: proc(t: ^testing.T) { frame, pass, pipeline, target, world, _, mat_a, _ := make_planner_frame() defer ir.destroy_frame_ir(&frame) far := ir.add_draw_packet( &frame, pass, pipeline, .Indexed_Mesh, { target = target, layer = world, sort_key = 200, material = mat_a, geometry = {kind = .Indexed_Mesh, index_count = 36}, instances = {count = 1}, order = .Depth, }, ) near := ir.add_draw_packet( &frame, pass, pipeline, .Indexed_Mesh, { target = target, layer = world, sort_key = 10, material = mat_a, geometry = {kind = .Indexed_Mesh, index_count = 36}, instances = {count = 1}, order = .Depth, }, ) plan := ir.plan_frame_commands(&frame) defer ir.command_plan_destroy(&plan) testing.expect_value(t, len(plan), 2) testing.expect(t, plan[0].handle == near) testing.expect(t, plan[1].handle == far) } @(test) test_planner_is_deterministic_for_same_input :: proc(t: ^testing.T) { frame, pass, pipeline, target, world, _, mat_a, _ := make_planner_frame() defer ir.destroy_frame_ir(&frame) keys := [?]u64{5, 1, 3, 1} for key in keys { _ = ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = target, layer = world, sort_key = key, material = mat_a, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, order = .Sortable, }, ) } a := ir.plan_frame_commands(&frame) defer ir.command_plan_destroy(&a) b := ir.plan_frame_commands(&frame) defer ir.command_plan_destroy(&b) testing.expect_value(t, len(a), len(b)) for i in 0 ..< len(a) { testing.expect(t, a[i].handle == b[i].handle) testing.expect_value(t, a[i].sequence, b[i].sequence) } } @(test) test_planner_treats_dispatch_as_strict_barrier :: proc(t: ^testing.T) { frame, pass, pipeline, target, world, _, mat_a, _ := make_planner_frame() defer ir.destroy_frame_ir(&frame) _ = ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = target, layer = world, sort_key = 30, material = mat_a, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, order = .Sortable, }, ) dispatch := ir.add_dispatch( &frame, { pass = pass, pipeline = pipeline, target = target, layer = world, sort_key = 0, groups = {1, 1, 1}, }, ) _ = ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = target, layer = world, sort_key = 10, material = mat_a, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, order = .Sortable, }, ) plan := ir.plan_frame_commands(&frame) defer ir.command_plan_destroy(&plan) testing.expect_value(t, len(plan), 3) testing.expect(t, plan[1].handle == dispatch) testing.expect_value(t, plan[0].sequence, u64(1)) testing.expect_value(t, plan[1].sequence, u64(2)) testing.expect_value(t, plan[2].sequence, u64(3)) } @(test) test_planner_marks_packets_outside_target_as_culled :: proc(t: ^testing.T) { frame, pass, pipeline, target, world, _, mat_a, _ := make_planner_frame() defer ir.destroy_frame_ir(&frame) cmd := ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = target, layer = world, material = mat_a, bounds = {min = {900, 10, 0}, max = {950, 20, 0}}, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, flags = {.Allow_Cull}, order = .Sortable, }, ) plan := ir.plan_frame_commands(&frame) defer ir.command_plan_destroy(&plan) testing.expect_value(t, len(plan), 1) testing.expect(t, plan[0].handle == cmd) testing.expect(t, plan[0].culled) testing.expect(t, plan[0].cull_reason == .Target_Bounds) } @(test) test_planner_does_not_cull_for_invalid_clip_or_target :: proc(t: ^testing.T) { frame, pass, pipeline, _, world, _, mat_a, _ := make_planner_frame() defer ir.destroy_frame_ir(&frame) _ = ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = ir.INVALID_TARGET, layer = world, clip = ir.Clip_Handle(999), material = mat_a, bounds = {min = {900, 10, 0}, max = {950, 20, 0}}, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, flags = {.Allow_Cull}, order = .Sortable, }, ) draw, ok := ir.get_draw(&frame, ir.Command_Handle(1)) testing.expect(t, ok) _, scissor_ok := ir.packet_effective_scissor(&frame, draw.packet) testing.expect(t, !scissor_ok) plan := ir.plan_frame_commands(&frame) defer ir.command_plan_destroy(&plan) testing.expect(t, !plan[0].culled) testing.expect(t, plan[0].cull_reason == .None) } @(test) test_planner_marks_packets_outside_clip_as_culled_and_exposes_scissor :: proc(t: ^testing.T) { frame, pass, pipeline, target, world, _, mat_a, _ := make_planner_frame() defer ir.destroy_frame_ir(&frame) clip := ir.add_clip( &frame, {target = target, kind = .Rect, rect = {x = 10, y = 10, width = 20, height = 20}}, ) _ = ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = target, layer = world, clip = clip, material = mat_a, bounds = {min = {40, 40, 0}, max = {50, 50, 0}}, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, flags = {.Allow_Cull}, order = .Sortable, }, ) draw, ok := ir.get_draw(&frame, ir.Command_Handle(1)) testing.expect(t, ok) scissor, scissor_ok := ir.packet_effective_scissor(&frame, draw.packet) testing.expect(t, scissor_ok) testing.expect_value(t, scissor.x, i32(10)) testing.expect_value(t, scissor.width, u32(20)) plan := ir.plan_frame_commands(&frame) defer ir.command_plan_destroy(&plan) testing.expect(t, plan[0].culled) testing.expect(t, plan[0].cull_reason == .Clip_Bounds) } @(test) test_planner_does_not_cull_without_allow_cull_flag :: proc(t: ^testing.T) { frame, pass, pipeline, target, world, _, mat_a, _ := make_planner_frame() defer ir.destroy_frame_ir(&frame) _ = ir.add_draw_packet( &frame, pass, pipeline, .Quad_Stream, { target = target, layer = world, material = mat_a, bounds = {min = {900, 10, 0}, max = {950, 20, 0}}, geometry = {kind = .Quad_Stream, vertex_count = 6}, instances = {count = 1}, order = .Sortable, }, ) plan := ir.plan_frame_commands(&frame) defer ir.command_plan_destroy(&plan) testing.expect(t, !plan[0].culled) testing.expect(t, plan[0].cull_reason == .None) }