Harbor

branch main
showing the latest snapshot on main
recording_backend.odin 16.6 KB · Plain text
gpu/tests/recording_backend.odin 0644 Raw
package gpu_tests

import bk "../backend"
import compiler "../compiler"
import "core:mem"
import "core:testing"

Recording_Call_Kind :: enum {
	Begin_Frame,
	End_Frame,
	Begin_Render_Pass,
	Begin_Default_Pass,
	End_Render_Pass,
	Set_Viewport,
	Set_Scissor,
	Create_Buffer,
	Destroy_Buffer,
	Create_Graphics_Pipeline,
	Destroy_Graphics_Pipeline,
	Bind_Pipeline,
	Bind_Descriptor,
	Push_Constants,
	Bind_Vertex_Buffer,
	Bind_Index_Buffer,
	Draw,
	Draw_Indexed,
	Draw_Indirect,
	Draw_Indexed_Indirect,
	Create_Compute_Pipeline,
	Destroy_Compute_Pipeline,
	Bind_Compute_Pipeline,
	Dispatch_Compute,
	Compute_Barrier,
	Create_Shader,
	Destroy_Shader,
}

Recording_Call :: struct {
	kind: Recording_Call_Kind,

	frame_index: u32,

	x_i: i32,
	y_i: i32,
	width_u: u32,
	height_u: u32,
	x_f: f32,
	y_f: f32,
	width_f: f32,
	height_f: f32,

	pipeline: bk.Pipeline_Handle,
	descriptor: bk.Descriptor_Handle,
	descriptor_index: u32,
	buffer: bk.Buffer_Handle,
	buffer_slot: u32,
	buffer_offset: u64,
	buffer_stride: u32,
	argument_buffer: bk.Buffer_Handle,
	argument_offset: u64,
	draw_count: u32,
	stride: u32,
	color_count: u32,
	color_formats: [bk.MAX_COLOR_TARGETS]bk.Format,
	color_write_masks: [bk.MAX_COLOR_TARGETS]u8,
	stencil: bk.Stencil_State,
	color_views: [bk.MAX_COLOR_TARGETS]bk.Texture_Handle,
	clear_colors: [bk.MAX_COLOR_TARGETS][4]f32,
	vertex_binding_count: u32,
	vertex_bindings: [16]bk.Vertex_Binding,
	vertex_attribute_count: u32,
	vertex_attributes: [16]bk.Vertex_Attribute,

	push_constant_size: u32,
	push_constant_bytes: [compiler.IMPORTED_PUSH_CONSTANT_CAP]u8,

	vertex_count: u32,
	index_count: u32,
	instance_count: u32,
	first_vertex: u32,
	first_index: u32,
	vertex_offset: i32,
	first_instance: u32,
}

Recording_Buffer :: struct {
	handle: bk.Buffer_Handle,
	data: []u8,
	mapped: bool,
}

Recording_Backend_State :: struct {
	calls: [dynamic]Recording_Call,
	buffers: [dynamic]Recording_Buffer,

	shader_desc: bk.Shader_Module_Desc,
	extent: bk.Extent,

	next_buffer: u64,
	next_pipeline: u64,
	next_shader: u64,
	current_frame_index: u32,
	next_frame_index: u32,

	begin_pass_count: int,
	end_pass_count: int,
	draw_count: int,
	vertex_count: u32,
	indexed_count: u32,
	instance_count: u32,
	bound_vertex_buffer: bk.Buffer_Handle,
	bound_index_buffer: bk.Buffer_Handle,
	bound_pipeline: bk.Pipeline_Handle,
	bound_descriptor: bk.Descriptor_Handle,
	push_constant_size: u32,
}

@(thread_local) recording_active: ^Recording_Backend_State

recording_backend_init :: proc() -> Recording_Backend_State {
	return {
		calls = make([dynamic]Recording_Call, 0, 128),
		buffers = make([dynamic]Recording_Buffer, 0, 16),
		extent = {width = 640, height = 480},
		next_buffer = 12,
		next_pipeline = 31,
		next_shader = 77,
	}
}

recording_backend_destroy :: proc(rec: ^Recording_Backend_State) {
	if rec == nil {
		return
	}
	for &buffer in rec.buffers {
		if buffer.data != nil {
			delete(buffer.data)
		}
	}
	delete(rec.calls)
	delete(rec.buffers)
	rec^ = {}
	if recording_active == rec {
		recording_active = nil
	}
}

recording_backend_reset :: proc(rec: ^Recording_Backend_State) {
	if rec == nil {
		return
	}
	clear(&rec.calls)
	rec.begin_pass_count = 0
	rec.end_pass_count = 0
	rec.draw_count = 0
	rec.vertex_count = 0
	rec.indexed_count = 0
	rec.instance_count = 0
	rec.bound_vertex_buffer = bk.NULL_BUFFER
	rec.bound_index_buffer = bk.NULL_BUFFER
	rec.bound_pipeline = bk.NULL_PIPELINE
	rec.bound_descriptor = bk.NULL_DESCRIPTOR
	rec.push_constant_size = 0
	rec.shader_desc = {}
}

recording_backend_use :: proc(rec: ^Recording_Backend_State) -> bk.Backend {
	recording_active = rec
	return {
		begin_frame = recording_begin_frame,
		end_frame = recording_end_frame,
		get_extent = recording_get_extent,
		current_frame_index = recording_current_frame_index,

		begin_render_pass = recording_begin_render_pass,
		begin_default_pass = recording_begin_default_pass,
		end_render_pass = recording_end_render_pass,
		set_viewport = recording_set_viewport,
		set_scissor = recording_set_scissor,

		create_buffer = recording_create_buffer,
		create_buffer_staged = recording_create_buffer_staged,
		destroy_buffer = recording_destroy_buffer,
		map_buffer = recording_map_buffer,
		unmap_buffer = recording_unmap_buffer,
		get_buffer_mapped = recording_get_buffer_mapped,

		create_graphics_pipeline = recording_create_graphics_pipeline,
		destroy_graphics_pipeline = recording_destroy_graphics_pipeline,
		bind_graphics_pipeline = recording_bind_graphics_pipeline,
		push_constants = recording_push_constants,
		bind_descriptor_set = recording_bind_descriptor_set,
		bind_vertex_buffer = recording_bind_vertex_buffer,
		bind_vertex_buffer_slot = recording_bind_vertex_buffer_slot,
		bind_index_buffer = recording_bind_index_buffer,
		draw = recording_draw,
		draw_indexed = recording_draw_indexed,
		draw_indirect = recording_draw_indirect,
		draw_indexed_indirect = recording_draw_indexed_indirect,
		create_compute_pipeline = recording_create_compute_pipeline,
		destroy_compute_pipeline = recording_destroy_compute_pipeline,
		bind_compute_pipeline = recording_bind_compute_pipeline,
		dispatch_compute = recording_dispatch_compute,
		compute_barrier = recording_compute_barrier,

		create_shader_module = recording_create_shader_module,
		destroy_shader = recording_destroy_shader,

		destroy_render_pass = recording_destroy_render_pass,
		destroy_framebuffer = recording_destroy_framebuffer,
	}
}

recording_expect_kinds :: proc(
	t: ^testing.T,
	rec: ^Recording_Backend_State,
	expected: []Recording_Call_Kind,
) {
	testing.expect_value(t, len(rec.calls), len(expected))
	if len(rec.calls) != len(expected) {
		return
	}
	for kind, i in expected {
		testing.expect_value(t, rec.calls[i].kind, kind)
	}
}

recording_record :: proc(call: Recording_Call) {
	if recording_active == nil {
		return
	}
	append(&recording_active.calls, call)
}

recording_begin_frame :: proc(clear_color: [4]f32) -> (bk.Frame_Context, bool) {
	_ = clear_color
	rec := recording_active
	if rec == nil {
		return {}, false
	}
	rec.current_frame_index = rec.next_frame_index
	ctx := bk.Frame_Context {frame_index = rec.current_frame_index}
	recording_record({kind = .Begin_Frame, frame_index = ctx.frame_index})
	return ctx, true
}

recording_end_frame :: proc(ctx: bk.Frame_Context) -> bool {
	rec := recording_active
	if rec != nil {
		rec.next_frame_index = (ctx.frame_index + 1) % bk.MAX_FRAMES_IN_FLIGHT
	}
	recording_record({kind = .End_Frame, frame_index = ctx.frame_index})
	return false
}

recording_current_frame_index :: proc() -> u32 {
	if recording_active == nil {
		return 0
	}
	return recording_active.current_frame_index
}

recording_begin_render_pass :: proc(ctx: bk.Frame_Context, desc: bk.Render_Pass_Begin_Desc) {
	if recording_active != nil {
		recording_active.begin_pass_count += 1
	}
	recording_record({
		kind = .Begin_Render_Pass,
		frame_index = ctx.frame_index,
		color_count = desc.color_count,
		clear_colors = desc.clear_colors,
	})
}

recording_begin_default_pass :: proc(ctx: bk.Frame_Context, clear_color: [4]f32) {
	_ = clear_color
	if recording_active != nil {
		recording_active.begin_pass_count += 1
	}
	recording_record({kind = .Begin_Default_Pass, frame_index = ctx.frame_index})
}

recording_end_render_pass :: proc(ctx: bk.Frame_Context) {
	if recording_active != nil {
		recording_active.end_pass_count += 1
	}
	recording_record({kind = .End_Render_Pass, frame_index = ctx.frame_index})
}

recording_set_viewport :: proc(ctx: bk.Frame_Context, x, y, w, h: f32) {
	recording_record({kind = .Set_Viewport, frame_index = ctx.frame_index, x_f = x, y_f = y, width_f = w, height_f = h})
}

recording_set_scissor :: proc(ctx: bk.Frame_Context, x, y: i32, w, h: u32) {
	recording_record({kind = .Set_Scissor, frame_index = ctx.frame_index, x_i = x, y_i = y, width_u = w, height_u = h})
}

recording_get_extent :: proc() -> bk.Extent {
	if recording_active == nil {
		return {width = 640, height = 480}
	}
	return recording_active.extent
}

recording_find_buffer :: proc(
	rec: ^Recording_Backend_State,
	handle: bk.Buffer_Handle,
) -> (^Recording_Buffer, bool) {
	if rec == nil {
		return nil, false
	}
	for &buffer in rec.buffers {
		if buffer.handle == handle {
			return &buffer, true
		}
	}
	return nil, false
}

recording_create_buffer :: proc(desc: bk.Buffer_Desc) -> (bk.Buffer_Handle, bool) {
	rec := recording_active
	if rec == nil {
		return bk.NULL_BUFFER, false
	}
	size := int(desc.size)
	if size <= 0 {
		size = 1
	}
	handle := bk.Buffer_Handle(rec.next_buffer)
	rec.next_buffer += 1
	append(&rec.buffers, Recording_Buffer {handle = handle, data = make([]u8, size)})
	recording_record({kind = .Create_Buffer, buffer = handle})
	return handle, true
}

recording_create_buffer_staged :: proc(
	data: rawptr,
	size: int,
	usage: bk.Buffer_Usage_Flags,
) -> (
	bk.Buffer_Handle,
	bool,
) {
	_ = usage
	handle, ok := recording_create_buffer({size = u64(size)})
	if ok && data != nil {
		if buffer, buffer_ok := recording_find_buffer(recording_active, handle); buffer_ok {
			mem.copy(raw_data(buffer.data), data, size)
		}
	}
	return handle, ok
}

recording_destroy_buffer :: proc(handle: bk.Buffer_Handle) {
	rec := recording_active
	if rec != nil {
		for &buffer, i in rec.buffers {
			if buffer.handle == handle {
				if buffer.data != nil {
					delete(buffer.data)
				}
				unordered_remove(&rec.buffers, i)
				break
			}
		}
	}
	recording_record({kind = .Destroy_Buffer, buffer = handle})
}

recording_map_buffer :: proc(handle: bk.Buffer_Handle) -> rawptr {
	buffer, ok := recording_find_buffer(recording_active, handle)
	if !ok {
		return nil
	}
	buffer.mapped = true
	return raw_data(buffer.data)
}

recording_unmap_buffer :: proc(handle: bk.Buffer_Handle) {
	if buffer, ok := recording_find_buffer(recording_active, handle); ok {
		buffer.mapped = false
	}
}

recording_get_buffer_mapped :: proc(handle: bk.Buffer_Handle) -> rawptr {
	buffer, ok := recording_find_buffer(recording_active, handle)
	if !ok || !buffer.mapped {
		return nil
	}
	return raw_data(buffer.data)
}

recording_create_graphics_pipeline :: proc(desc: bk.Pipeline_Desc) -> (bk.Pipeline_Handle, bool) {
	rec := recording_active
	if rec == nil {
		return bk.NULL_PIPELINE, false
	}
	handle := bk.Pipeline_Handle(rec.next_pipeline)
	rec.next_pipeline += 1
	recording_record({
		kind = .Create_Graphics_Pipeline,
		pipeline = handle,
		color_count = desc.color_attachment_count,
		color_formats = desc.color_formats,
		color_write_masks = desc.color_write_masks,
		stencil = desc.stencil,
		vertex_binding_count = u32(len(desc.vertex_bindings)),
		vertex_attribute_count = u32(len(desc.vertex_attributes)),
	})
	call := &rec.calls[len(rec.calls) - 1]
	for binding, i in desc.vertex_bindings {
		if i >= len(call.vertex_bindings) {break}
		call.vertex_bindings[i] = binding
	}
	for attr, i in desc.vertex_attributes {
		if i >= len(call.vertex_attributes) {break}
		call.vertex_attributes[i] = attr
	}
	return handle, true
}

recording_destroy_graphics_pipeline :: proc(handle: bk.Pipeline_Handle) {
	recording_record({kind = .Destroy_Graphics_Pipeline, pipeline = handle})
}

recording_bind_graphics_pipeline :: proc(ctx: bk.Frame_Context, handle: bk.Pipeline_Handle) {
	if recording_active != nil {
		recording_active.bound_pipeline = handle
	}
	recording_record({kind = .Bind_Pipeline, frame_index = ctx.frame_index, pipeline = handle})
}

recording_bind_descriptor_set :: proc(
	ctx: bk.Frame_Context,
	pipeline: bk.Pipeline_Handle,
	set: bk.Descriptor_Handle,
	index: u32,
) {
	if recording_active != nil {
		recording_active.bound_descriptor = set
	}
	recording_record({
		kind = .Bind_Descriptor,
		frame_index = ctx.frame_index,
		pipeline = pipeline,
		descriptor = set,
		descriptor_index = index,
	})
}

recording_push_constants :: proc(
	ctx: bk.Frame_Context,
	pipeline: bk.Pipeline_Handle,
	stages: bk.Shader_Stage_Flags,
	offset, size: u32,
	data: rawptr,
) {
	_ = stages
	_ = offset
	call := Recording_Call {
		kind = .Push_Constants,
		frame_index = ctx.frame_index,
		pipeline = pipeline,
		push_constant_size = size,
	}
	if data != nil && size <= compiler.IMPORTED_PUSH_CONSTANT_CAP {
		mem.copy(&call.push_constant_bytes[0], data, int(size))
	}
	if recording_active != nil {
		recording_active.push_constant_size = size
	}
	recording_record(call)
}

recording_bind_vertex_buffer :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) {
	recording_bind_vertex_buffer_slot(ctx, 0, handle, 0, 0)
}

recording_bind_vertex_buffer_slot :: proc(
	ctx: bk.Frame_Context,
	slot: u32,
	handle: bk.Buffer_Handle,
	offset: u64,
	stride: u32,
) {
	if recording_active != nil {
		if slot == 0 {
			recording_active.bound_vertex_buffer = handle
		}
	}
	recording_record({
		kind = .Bind_Vertex_Buffer,
		frame_index = ctx.frame_index,
		buffer = handle,
		buffer_slot = slot,
		buffer_offset = offset,
		buffer_stride = stride,
	})
}

recording_bind_index_buffer :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) {
	if recording_active != nil {
		recording_active.bound_index_buffer = handle
	}
	recording_record({kind = .Bind_Index_Buffer, frame_index = ctx.frame_index, buffer = handle})
}

recording_draw :: proc(
	ctx: bk.Frame_Context,
	vertex_count, instance_count: u32,
	first_vertex: u32,
	first_instance: u32,
) {
	if recording_active != nil {
		recording_active.draw_count += 1
		recording_active.vertex_count = vertex_count
		recording_active.instance_count = instance_count
	}
	recording_record({
		kind = .Draw,
		frame_index = ctx.frame_index,
		vertex_count = vertex_count,
		instance_count = instance_count,
		first_vertex = first_vertex,
		first_instance = first_instance,
	})
}

recording_draw_indexed :: proc(
	ctx: bk.Frame_Context,
	index_count, instance_count: u32,
	first_index: u32,
	vertex_offset: i32,
	first_instance: u32,
) {
	if recording_active != nil {
		recording_active.draw_count += 1
		recording_active.indexed_count = index_count
		recording_active.instance_count = instance_count
	}
	recording_record({
		kind = .Draw_Indexed,
		frame_index = ctx.frame_index,
		index_count = index_count,
		instance_count = instance_count,
		first_index = first_index,
		vertex_offset = vertex_offset,
		first_instance = first_instance,
	})
}

recording_draw_indirect :: proc(
	ctx: bk.Frame_Context,
	argument_buffer: bk.Buffer_Handle,
	argument_offset: u64,
	draw_count: u32,
	stride: u32,
) {
	if recording_active != nil {
		recording_active.draw_count += int(draw_count)
	}
	recording_record({
		kind = .Draw_Indirect,
		frame_index = ctx.frame_index,
		argument_buffer = argument_buffer,
		argument_offset = argument_offset,
		draw_count = draw_count,
		stride = stride,
	})
}

recording_draw_indexed_indirect :: proc(
	ctx: bk.Frame_Context,
	argument_buffer: bk.Buffer_Handle,
	argument_offset: u64,
	draw_count: u32,
	stride: u32,
) {
	if recording_active != nil {
		recording_active.draw_count += int(draw_count)
	}
	recording_record({
		kind = .Draw_Indexed_Indirect,
		frame_index = ctx.frame_index,
		argument_buffer = argument_buffer,
		argument_offset = argument_offset,
		draw_count = draw_count,
		stride = stride,
	})
}

recording_create_compute_pipeline :: proc(
	shader: bk.Shader_Handle,
	num_buffers: u32,
	push_constant_size: u32,
) -> (
	bk.Pipeline_Handle,
	bool,
) {
	_ = shader
	_ = num_buffers
	_ = push_constant_size
	rec := recording_active
	if rec == nil {
		return bk.NULL_PIPELINE, false
	}
	handle := bk.Pipeline_Handle(rec.next_pipeline)
	rec.next_pipeline += 1
	recording_record({kind = .Create_Compute_Pipeline, pipeline = handle})
	return handle, true
}

recording_destroy_compute_pipeline :: proc(handle: bk.Pipeline_Handle) {
	recording_record({kind = .Destroy_Compute_Pipeline, pipeline = handle})
}

recording_bind_compute_pipeline :: proc(ctx: bk.Frame_Context, handle: bk.Pipeline_Handle) {
	if recording_active != nil {
		recording_active.bound_pipeline = handle
	}
	recording_record({kind = .Bind_Compute_Pipeline, frame_index = ctx.frame_index, pipeline = handle})
}

recording_dispatch_compute :: proc(ctx: bk.Frame_Context, groups_x, groups_y, groups_z: u32) {
	recording_record({
		kind = .Dispatch_Compute,
		frame_index = ctx.frame_index,
		width_u = groups_x,
		height_u = groups_y,
		vertex_count = groups_z,
	})
}

recording_compute_barrier :: proc(ctx: bk.Frame_Context) {
	recording_record({kind = .Compute_Barrier, frame_index = ctx.frame_index})
}

recording_create_shader_module :: proc(desc: bk.Shader_Module_Desc) -> (bk.Shader_Handle, bool) {
	rec := recording_active
	if rec == nil {
		return bk.NULL_SHADER, false
	}
	rec.shader_desc = desc
	handle := bk.Shader_Handle(rec.next_shader)
	rec.next_shader += 1
	recording_record({kind = .Create_Shader})
	return handle, true
}

recording_destroy_shader :: proc(handle: bk.Shader_Handle) {
	_ = handle
	recording_record({kind = .Destroy_Shader})
}

recording_destroy_render_pass :: proc(handle: bk.Render_Pass_Handle) {
	_ = handle
}

recording_destroy_framebuffer :: proc(handle: bk.Framebuffer_Handle) {
	_ = handle
}