Initial draw package
This commit is contained in:
688
draw/pipeline_2d_base.odin
Normal file
688
draw/pipeline_2d_base.odin
Normal file
@@ -0,0 +1,688 @@
|
||||
package draw
|
||||
|
||||
import "core:c"
|
||||
import "core:log"
|
||||
import "core:mem"
|
||||
import sdl "vendor:sdl3"
|
||||
|
||||
Vertex :: struct {
|
||||
position: [2]f32,
|
||||
uv: [2]f32,
|
||||
color: Color,
|
||||
}
|
||||
|
||||
TextBatch :: struct {
|
||||
atlas_texture: ^sdl.GPUTexture,
|
||||
vertex_start: u32,
|
||||
vertex_count: u32,
|
||||
index_start: u32,
|
||||
index_count: u32,
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------------------------
|
||||
// ----- SDF primitive types -----------
|
||||
// ----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Shape_Kind :: enum u8 {
|
||||
Solid = 0,
|
||||
RRect = 1,
|
||||
Circle = 2,
|
||||
Ellipse = 3,
|
||||
Segment = 4,
|
||||
Ring_Arc = 5,
|
||||
NGon = 6,
|
||||
}
|
||||
|
||||
Shape_Flag :: enum u8 {
|
||||
Stroke,
|
||||
}
|
||||
|
||||
Shape_Flags :: bit_set[Shape_Flag;u8]
|
||||
|
||||
RRect_Params :: struct {
|
||||
half_size: [2]f32,
|
||||
radii: [4]f32,
|
||||
soft_px: f32,
|
||||
stroke_px: f32,
|
||||
}
|
||||
|
||||
Circle_Params :: struct {
|
||||
radius: f32,
|
||||
soft_px: f32,
|
||||
stroke_px: f32,
|
||||
_: [5]f32,
|
||||
}
|
||||
|
||||
Ellipse_Params :: struct {
|
||||
radii: [2]f32,
|
||||
soft_px: f32,
|
||||
stroke_px: f32,
|
||||
_: [4]f32,
|
||||
}
|
||||
|
||||
Segment_Params :: struct {
|
||||
a: [2]f32,
|
||||
b: [2]f32,
|
||||
width: f32,
|
||||
soft_px: f32,
|
||||
_: [2]f32,
|
||||
}
|
||||
|
||||
Ring_Arc_Params :: struct {
|
||||
inner_radius: f32,
|
||||
outer_radius: f32,
|
||||
start_rad: f32,
|
||||
end_rad: f32,
|
||||
soft_px: f32,
|
||||
_: [3]f32,
|
||||
}
|
||||
|
||||
NGon_Params :: struct {
|
||||
radius: f32,
|
||||
rotation: f32,
|
||||
sides: f32,
|
||||
soft_px: f32,
|
||||
stroke_px: f32,
|
||||
_: [3]f32,
|
||||
}
|
||||
|
||||
Shape_Params :: struct #raw_union {
|
||||
rrect: RRect_Params,
|
||||
circle: Circle_Params,
|
||||
ellipse: Ellipse_Params,
|
||||
segment: Segment_Params,
|
||||
ring_arc: Ring_Arc_Params,
|
||||
ngon: NGon_Params,
|
||||
raw: [8]f32,
|
||||
}
|
||||
|
||||
#assert(size_of(Shape_Params) == 32)
|
||||
|
||||
// GPU layout: 64 bytes, std430-compatible. The shader declares this as a storage buffer struct.
|
||||
Primitive :: struct {
|
||||
bounds: [4]f32, // 0: min_x, min_y, max_x, max_y (world-space, pre-DPI)
|
||||
color: Color, // 16: u8x4, unpacked in shader via unpackUnorm4x8
|
||||
kind_flags: u32, // 20: (kind as u32) | (flags as u32 << 8)
|
||||
_pad: [2]f32, // 24: alignment to vec4 boundary
|
||||
params: Shape_Params, // 32: two vec4s of shape params
|
||||
}
|
||||
|
||||
#assert(size_of(Primitive) == 64)
|
||||
|
||||
pack_kind_flags :: #force_inline proc(kind: Shape_Kind, flags: Shape_Flags) -> u32 {
|
||||
return u32(kind) | (u32(transmute(u8)flags) << 8)
|
||||
}
|
||||
|
||||
Pipeline_2D_Base :: struct {
|
||||
sdl_pipeline: ^sdl.GPUGraphicsPipeline,
|
||||
vertex_buffer: Buffer,
|
||||
index_buffer: Buffer,
|
||||
unit_quad_buffer: ^sdl.GPUBuffer,
|
||||
primitive_buffer: Buffer,
|
||||
white_texture: ^sdl.GPUTexture,
|
||||
sampler: ^sdl.GPUSampler,
|
||||
}
|
||||
|
||||
@(private)
|
||||
create_pipeline_2d_base :: proc(
|
||||
device: ^sdl.GPUDevice,
|
||||
window: ^sdl.Window,
|
||||
sample_count: sdl.GPUSampleCount,
|
||||
) -> (
|
||||
pipeline: Pipeline_2D_Base,
|
||||
ok: bool,
|
||||
) {
|
||||
// On failure, clean up any partially-created resources
|
||||
defer if !ok {
|
||||
if pipeline.sampler != nil do sdl.ReleaseGPUSampler(device, pipeline.sampler)
|
||||
if pipeline.white_texture != nil do sdl.ReleaseGPUTexture(device, pipeline.white_texture)
|
||||
if pipeline.unit_quad_buffer != nil do sdl.ReleaseGPUBuffer(device, pipeline.unit_quad_buffer)
|
||||
if pipeline.primitive_buffer.gpu != nil do destroy_buffer(device, &pipeline.primitive_buffer)
|
||||
if pipeline.index_buffer.gpu != nil do destroy_buffer(device, &pipeline.index_buffer)
|
||||
if pipeline.vertex_buffer.gpu != nil do destroy_buffer(device, &pipeline.vertex_buffer)
|
||||
if pipeline.sdl_pipeline != nil do sdl.ReleaseGPUGraphicsPipeline(device, pipeline.sdl_pipeline)
|
||||
}
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
base_2d_vert_raw := #load("shaders/generated/base_2d.vert.metal")
|
||||
base_2d_frag_raw := #load("shaders/generated/base_2d.frag.metal")
|
||||
} else {
|
||||
base_2d_vert_raw := #load("shaders/generated/base_2d.vert.spv")
|
||||
base_2d_frag_raw := #load("shaders/generated/base_2d.frag.spv")
|
||||
}
|
||||
|
||||
log.debug("Loaded", len(base_2d_vert_raw), "vert bytes")
|
||||
log.debug("Loaded", len(base_2d_frag_raw), "frag bytes")
|
||||
|
||||
vert_info := sdl.GPUShaderCreateInfo {
|
||||
code_size = len(base_2d_vert_raw),
|
||||
code = raw_data(base_2d_vert_raw),
|
||||
entrypoint = ENTRY_POINT,
|
||||
format = SHADER_TYPE,
|
||||
stage = .VERTEX,
|
||||
num_uniform_buffers = 1,
|
||||
num_storage_buffers = 1,
|
||||
}
|
||||
|
||||
frag_info := sdl.GPUShaderCreateInfo {
|
||||
code_size = len(base_2d_frag_raw),
|
||||
code = raw_data(base_2d_frag_raw),
|
||||
entrypoint = ENTRY_POINT,
|
||||
format = SHADER_TYPE,
|
||||
stage = .FRAGMENT,
|
||||
num_samplers = 1,
|
||||
}
|
||||
|
||||
vert_shader := sdl.CreateGPUShader(device, vert_info)
|
||||
if vert_shader == nil {
|
||||
log.errorf("Could not create draw vertex shader: %s", sdl.GetError())
|
||||
return pipeline, false
|
||||
}
|
||||
|
||||
frag_shader := sdl.CreateGPUShader(device, frag_info)
|
||||
if frag_shader == nil {
|
||||
sdl.ReleaseGPUShader(device, vert_shader)
|
||||
log.errorf("Could not create draw fragment shader: %s", sdl.GetError())
|
||||
return pipeline, false
|
||||
}
|
||||
|
||||
vertex_attributes: [3]sdl.GPUVertexAttribute = {
|
||||
// position (GLSL location 0)
|
||||
sdl.GPUVertexAttribute{buffer_slot = 0, location = 0, format = .FLOAT2, offset = 0},
|
||||
// uv (GLSL location 1)
|
||||
sdl.GPUVertexAttribute{buffer_slot = 0, location = 1, format = .FLOAT2, offset = size_of([2]f32)},
|
||||
// color (GLSL location 2, u8x4 normalized to float by GPU)
|
||||
sdl.GPUVertexAttribute{buffer_slot = 0, location = 2, format = .UBYTE4_NORM, offset = size_of([2]f32) * 2},
|
||||
}
|
||||
|
||||
pipeline_info := sdl.GPUGraphicsPipelineCreateInfo {
|
||||
vertex_shader = vert_shader,
|
||||
fragment_shader = frag_shader,
|
||||
primitive_type = .TRIANGLELIST,
|
||||
multisample_state = sdl.GPUMultisampleState{sample_count = sample_count},
|
||||
target_info = sdl.GPUGraphicsPipelineTargetInfo {
|
||||
color_target_descriptions = &sdl.GPUColorTargetDescription {
|
||||
format = sdl.GetGPUSwapchainTextureFormat(device, window),
|
||||
blend_state = sdl.GPUColorTargetBlendState {
|
||||
enable_blend = true,
|
||||
enable_color_write_mask = true,
|
||||
src_color_blendfactor = .SRC_ALPHA,
|
||||
dst_color_blendfactor = .ONE_MINUS_SRC_ALPHA,
|
||||
color_blend_op = .ADD,
|
||||
src_alpha_blendfactor = .SRC_ALPHA,
|
||||
dst_alpha_blendfactor = .ONE_MINUS_SRC_ALPHA,
|
||||
alpha_blend_op = .ADD,
|
||||
color_write_mask = sdl.GPUColorComponentFlags{.R, .G, .B, .A},
|
||||
},
|
||||
},
|
||||
num_color_targets = 1,
|
||||
},
|
||||
vertex_input_state = sdl.GPUVertexInputState {
|
||||
vertex_buffer_descriptions = &sdl.GPUVertexBufferDescription {
|
||||
slot = 0,
|
||||
input_rate = .VERTEX,
|
||||
pitch = size_of(Vertex),
|
||||
},
|
||||
num_vertex_buffers = 1,
|
||||
vertex_attributes = raw_data(vertex_attributes[:]),
|
||||
num_vertex_attributes = 3,
|
||||
},
|
||||
}
|
||||
|
||||
pipeline.sdl_pipeline = sdl.CreateGPUGraphicsPipeline(device, pipeline_info)
|
||||
// Shaders are no longer needed regardless of pipeline creation success
|
||||
sdl.ReleaseGPUShader(device, vert_shader)
|
||||
sdl.ReleaseGPUShader(device, frag_shader)
|
||||
if pipeline.sdl_pipeline == nil {
|
||||
log.errorf("Failed to create draw graphics pipeline: %s", sdl.GetError())
|
||||
return pipeline, false
|
||||
}
|
||||
|
||||
// Create vertex buffer
|
||||
vb_ok: bool
|
||||
pipeline.vertex_buffer, vb_ok = create_buffer(
|
||||
device,
|
||||
size_of(Vertex) * BUFFER_INIT_SIZE,
|
||||
sdl.GPUBufferUsageFlags{.VERTEX},
|
||||
)
|
||||
if !vb_ok do return pipeline, false
|
||||
|
||||
// Create index buffer (used by text)
|
||||
ib_ok: bool
|
||||
pipeline.index_buffer, ib_ok = create_buffer(
|
||||
device,
|
||||
size_of(c.int) * BUFFER_INIT_SIZE,
|
||||
sdl.GPUBufferUsageFlags{.INDEX},
|
||||
)
|
||||
if !ib_ok do return pipeline, false
|
||||
|
||||
// Create primitive storage buffer (used by SDF instanced drawing)
|
||||
pb_ok: bool
|
||||
pipeline.primitive_buffer, pb_ok = create_buffer(
|
||||
device,
|
||||
size_of(Primitive) * BUFFER_INIT_SIZE,
|
||||
sdl.GPUBufferUsageFlags{.GRAPHICS_STORAGE_READ},
|
||||
)
|
||||
if !pb_ok do return pipeline, false
|
||||
|
||||
// Create static 6-vertex unit quad buffer (two triangles, TRIANGLELIST)
|
||||
pipeline.unit_quad_buffer = sdl.CreateGPUBuffer(
|
||||
device,
|
||||
sdl.GPUBufferCreateInfo{usage = {.VERTEX}, size = 6 * size_of(Vertex)},
|
||||
)
|
||||
if pipeline.unit_quad_buffer == nil {
|
||||
log.errorf("Failed to create unit quad buffer: %s", sdl.GetError())
|
||||
return pipeline, false
|
||||
}
|
||||
|
||||
// Create 1x1 white pixel texture
|
||||
pipeline.white_texture = sdl.CreateGPUTexture(
|
||||
device,
|
||||
sdl.GPUTextureCreateInfo {
|
||||
type = .D2,
|
||||
format = .R8G8B8A8_UNORM,
|
||||
usage = {.SAMPLER},
|
||||
width = 1,
|
||||
height = 1,
|
||||
layer_count_or_depth = 1,
|
||||
num_levels = 1,
|
||||
sample_count = ._1,
|
||||
},
|
||||
)
|
||||
if pipeline.white_texture == nil {
|
||||
log.errorf("Failed to create white pixel texture: %s", sdl.GetError())
|
||||
return pipeline, false
|
||||
}
|
||||
|
||||
// Upload white pixel and unit quad data in a single command buffer
|
||||
white_pixel := [4]u8{255, 255, 255, 255}
|
||||
white_transfer := sdl.CreateGPUTransferBuffer(
|
||||
device,
|
||||
sdl.GPUTransferBufferCreateInfo{usage = .UPLOAD, size = size_of(white_pixel)},
|
||||
)
|
||||
if white_transfer == nil {
|
||||
log.errorf("Failed to create white pixel transfer buffer: %s", sdl.GetError())
|
||||
return pipeline, false
|
||||
}
|
||||
defer sdl.ReleaseGPUTransferBuffer(device, white_transfer)
|
||||
|
||||
white_ptr := sdl.MapGPUTransferBuffer(device, white_transfer, false)
|
||||
if white_ptr == nil {
|
||||
log.errorf("Failed to map white pixel transfer buffer: %s", sdl.GetError())
|
||||
return pipeline, false
|
||||
}
|
||||
mem.copy(white_ptr, &white_pixel, size_of(white_pixel))
|
||||
sdl.UnmapGPUTransferBuffer(device, white_transfer)
|
||||
|
||||
quad_verts := [6]Vertex{
|
||||
{position = {0, 0}}, {position = {1, 0}}, {position = {0, 1}},
|
||||
{position = {0, 1}}, {position = {1, 0}}, {position = {1, 1}},
|
||||
}
|
||||
quad_transfer := sdl.CreateGPUTransferBuffer(
|
||||
device,
|
||||
sdl.GPUTransferBufferCreateInfo{usage = .UPLOAD, size = size_of(quad_verts)},
|
||||
)
|
||||
if quad_transfer == nil {
|
||||
log.errorf("Failed to create unit quad transfer buffer: %s", sdl.GetError())
|
||||
return pipeline, false
|
||||
}
|
||||
defer sdl.ReleaseGPUTransferBuffer(device, quad_transfer)
|
||||
|
||||
quad_ptr := sdl.MapGPUTransferBuffer(device, quad_transfer, false)
|
||||
if quad_ptr == nil {
|
||||
log.errorf("Failed to map unit quad transfer buffer: %s", sdl.GetError())
|
||||
return pipeline, false
|
||||
}
|
||||
mem.copy(quad_ptr, &quad_verts, size_of(quad_verts))
|
||||
sdl.UnmapGPUTransferBuffer(device, quad_transfer)
|
||||
|
||||
upload_cmd := sdl.AcquireGPUCommandBuffer(device)
|
||||
if upload_cmd == nil {
|
||||
log.errorf("Failed to acquire command buffer for init upload: %s", sdl.GetError())
|
||||
return pipeline, false
|
||||
}
|
||||
upload_pass := sdl.BeginGPUCopyPass(upload_cmd)
|
||||
|
||||
sdl.UploadToGPUTexture(
|
||||
upload_pass,
|
||||
sdl.GPUTextureTransferInfo{transfer_buffer = white_transfer},
|
||||
sdl.GPUTextureRegion{texture = pipeline.white_texture, w = 1, h = 1, d = 1},
|
||||
false,
|
||||
)
|
||||
|
||||
sdl.UploadToGPUBuffer(
|
||||
upload_pass,
|
||||
sdl.GPUTransferBufferLocation{transfer_buffer = quad_transfer},
|
||||
sdl.GPUBufferRegion{
|
||||
buffer = pipeline.unit_quad_buffer,
|
||||
offset = 0,
|
||||
size = size_of(quad_verts),
|
||||
},
|
||||
false,
|
||||
)
|
||||
|
||||
sdl.EndGPUCopyPass(upload_pass)
|
||||
if !sdl.SubmitGPUCommandBuffer(upload_cmd) {
|
||||
log.errorf("Failed to submit init upload command buffer: %s", sdl.GetError())
|
||||
return pipeline, false
|
||||
}
|
||||
|
||||
log.debug("White pixel texture and unit quad buffer created and uploaded")
|
||||
|
||||
// Create sampler (shared by shapes and text)
|
||||
pipeline.sampler = sdl.CreateGPUSampler(
|
||||
device,
|
||||
sdl.GPUSamplerCreateInfo {
|
||||
min_filter = .LINEAR,
|
||||
mag_filter = .LINEAR,
|
||||
mipmap_mode = .LINEAR,
|
||||
address_mode_u = .CLAMP_TO_EDGE,
|
||||
address_mode_v = .CLAMP_TO_EDGE,
|
||||
address_mode_w = .CLAMP_TO_EDGE,
|
||||
},
|
||||
)
|
||||
if pipeline.sampler == nil {
|
||||
log.errorf("Could not create GPU sampler: %s", sdl.GetError())
|
||||
return pipeline, false
|
||||
}
|
||||
|
||||
log.debug("Done creating unified draw pipeline")
|
||||
return pipeline, true
|
||||
}
|
||||
|
||||
@(private)
|
||||
upload :: proc(device: ^sdl.GPUDevice, pass: ^sdl.GPUCopyPass) {
|
||||
// Upload vertices (shapes then text into one buffer)
|
||||
shape_vert_count := u32(len(GLOB.tmp_shape_verts))
|
||||
text_vert_count := u32(len(GLOB.tmp_text_verts))
|
||||
total_vert_count := shape_vert_count + text_vert_count
|
||||
|
||||
if total_vert_count > 0 {
|
||||
total_vert_size := total_vert_count * size_of(Vertex)
|
||||
shape_vert_size := shape_vert_count * size_of(Vertex)
|
||||
text_vert_size := text_vert_count * size_of(Vertex)
|
||||
|
||||
grow_buffer_if_needed(
|
||||
device,
|
||||
&GLOB.pipeline_2d_base.vertex_buffer,
|
||||
total_vert_size,
|
||||
sdl.GPUBufferUsageFlags{.VERTEX},
|
||||
)
|
||||
|
||||
v_array := sdl.MapGPUTransferBuffer(device, GLOB.pipeline_2d_base.vertex_buffer.transfer, false)
|
||||
if v_array == nil {
|
||||
log.panicf("Failed to map vertex transfer buffer: %s", sdl.GetError())
|
||||
}
|
||||
if shape_vert_size > 0 {
|
||||
mem.copy(v_array, raw_data(GLOB.tmp_shape_verts), int(shape_vert_size))
|
||||
}
|
||||
if text_vert_size > 0 {
|
||||
mem.copy(
|
||||
rawptr(uintptr(v_array) + uintptr(shape_vert_size)),
|
||||
raw_data(GLOB.tmp_text_verts),
|
||||
int(text_vert_size),
|
||||
)
|
||||
}
|
||||
sdl.UnmapGPUTransferBuffer(device, GLOB.pipeline_2d_base.vertex_buffer.transfer)
|
||||
|
||||
sdl.UploadToGPUBuffer(
|
||||
pass,
|
||||
sdl.GPUTransferBufferLocation{transfer_buffer = GLOB.pipeline_2d_base.vertex_buffer.transfer},
|
||||
sdl.GPUBufferRegion{
|
||||
buffer = GLOB.pipeline_2d_base.vertex_buffer.gpu,
|
||||
offset = 0,
|
||||
size = total_vert_size,
|
||||
},
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
// Upload text indices
|
||||
index_count := u32(len(GLOB.tmp_text_indices))
|
||||
if index_count > 0 {
|
||||
index_size := index_count * size_of(c.int)
|
||||
|
||||
grow_buffer_if_needed(
|
||||
device,
|
||||
&GLOB.pipeline_2d_base.index_buffer,
|
||||
index_size,
|
||||
sdl.GPUBufferUsageFlags{.INDEX},
|
||||
)
|
||||
|
||||
i_array := sdl.MapGPUTransferBuffer(device, GLOB.pipeline_2d_base.index_buffer.transfer, false)
|
||||
if i_array == nil {
|
||||
log.panicf("Failed to map index transfer buffer: %s", sdl.GetError())
|
||||
}
|
||||
mem.copy(i_array, raw_data(GLOB.tmp_text_indices), int(index_size))
|
||||
sdl.UnmapGPUTransferBuffer(device, GLOB.pipeline_2d_base.index_buffer.transfer)
|
||||
|
||||
sdl.UploadToGPUBuffer(
|
||||
pass,
|
||||
sdl.GPUTransferBufferLocation{transfer_buffer = GLOB.pipeline_2d_base.index_buffer.transfer},
|
||||
sdl.GPUBufferRegion{
|
||||
buffer = GLOB.pipeline_2d_base.index_buffer.gpu,
|
||||
offset = 0,
|
||||
size = index_size,
|
||||
},
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
// Upload SDF primitives
|
||||
prim_count := u32(len(GLOB.tmp_primitives))
|
||||
if prim_count > 0 {
|
||||
prim_size := prim_count * size_of(Primitive)
|
||||
|
||||
grow_buffer_if_needed(
|
||||
device,
|
||||
&GLOB.pipeline_2d_base.primitive_buffer,
|
||||
prim_size,
|
||||
sdl.GPUBufferUsageFlags{.GRAPHICS_STORAGE_READ},
|
||||
)
|
||||
|
||||
p_array := sdl.MapGPUTransferBuffer(
|
||||
device, GLOB.pipeline_2d_base.primitive_buffer.transfer, false,
|
||||
)
|
||||
if p_array == nil {
|
||||
log.panicf("Failed to map primitive transfer buffer: %s", sdl.GetError())
|
||||
}
|
||||
mem.copy(p_array, raw_data(GLOB.tmp_primitives), int(prim_size))
|
||||
sdl.UnmapGPUTransferBuffer(device, GLOB.pipeline_2d_base.primitive_buffer.transfer)
|
||||
|
||||
sdl.UploadToGPUBuffer(
|
||||
pass,
|
||||
sdl.GPUTransferBufferLocation{
|
||||
transfer_buffer = GLOB.pipeline_2d_base.primitive_buffer.transfer,
|
||||
},
|
||||
sdl.GPUBufferRegion{
|
||||
buffer = GLOB.pipeline_2d_base.primitive_buffer.gpu,
|
||||
offset = 0,
|
||||
size = prim_size,
|
||||
},
|
||||
false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
draw_layer :: proc(
|
||||
device: ^sdl.GPUDevice,
|
||||
window: ^sdl.Window,
|
||||
cmd_buffer: ^sdl.GPUCommandBuffer,
|
||||
render_texture: ^sdl.GPUTexture,
|
||||
swapchain_w: u32,
|
||||
swapchain_h: u32,
|
||||
clear_color: [4]f32,
|
||||
layer: ^Layer,
|
||||
) {
|
||||
if layer.sub_batch_len == 0 {
|
||||
if !GLOB.cleared {
|
||||
pass := sdl.BeginGPURenderPass(
|
||||
cmd_buffer,
|
||||
&sdl.GPUColorTargetInfo {
|
||||
texture = render_texture,
|
||||
clear_color = sdl.FColor {
|
||||
clear_color[0], clear_color[1], clear_color[2], clear_color[3],
|
||||
},
|
||||
load_op = .CLEAR,
|
||||
store_op = .STORE,
|
||||
},
|
||||
1,
|
||||
nil,
|
||||
)
|
||||
sdl.EndGPURenderPass(pass)
|
||||
GLOB.cleared = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
render_pass := sdl.BeginGPURenderPass(
|
||||
cmd_buffer,
|
||||
&sdl.GPUColorTargetInfo {
|
||||
texture = render_texture,
|
||||
clear_color = sdl.FColor {
|
||||
clear_color[0], clear_color[1], clear_color[2], clear_color[3],
|
||||
},
|
||||
load_op = GLOB.cleared ? .LOAD : .CLEAR,
|
||||
store_op = .STORE,
|
||||
},
|
||||
1,
|
||||
nil,
|
||||
)
|
||||
GLOB.cleared = true
|
||||
|
||||
sdl.BindGPUGraphicsPipeline(render_pass, GLOB.pipeline_2d_base.sdl_pipeline)
|
||||
|
||||
// Bind storage buffer (read by vertex shader in SDF mode)
|
||||
sdl.BindGPUVertexStorageBuffers(
|
||||
render_pass,
|
||||
0,
|
||||
([^]^sdl.GPUBuffer)(&GLOB.pipeline_2d_base.primitive_buffer.gpu),
|
||||
1,
|
||||
)
|
||||
|
||||
// Always bind index buffer — harmless if no indexed draws are issued
|
||||
sdl.BindGPUIndexBuffer(
|
||||
render_pass,
|
||||
sdl.GPUBufferBinding{buffer = GLOB.pipeline_2d_base.index_buffer.gpu, offset = 0},
|
||||
._32BIT,
|
||||
)
|
||||
|
||||
// Shorthand aliases for frequently-used pipeline resources
|
||||
main_vbuf := GLOB.pipeline_2d_base.vertex_buffer.gpu
|
||||
unit_quad := GLOB.pipeline_2d_base.unit_quad_buffer
|
||||
white := GLOB.pipeline_2d_base.white_texture
|
||||
sampler := GLOB.pipeline_2d_base.sampler
|
||||
w := f32(swapchain_w)
|
||||
h := f32(swapchain_h)
|
||||
|
||||
// Initial GPU state: tessellated mode, main vertex buffer, no atlas bound yet
|
||||
push_globals(cmd_buffer, w, h, .Tessellated)
|
||||
sdl.BindGPUVertexBuffers(
|
||||
render_pass, 0, &sdl.GPUBufferBinding{buffer = main_vbuf, offset = 0}, 1,
|
||||
)
|
||||
|
||||
current_mode: Draw_Mode = .Tessellated
|
||||
current_vbuf := main_vbuf
|
||||
current_atlas: ^sdl.GPUTexture
|
||||
|
||||
// Text vertices live after shape vertices in the GPU vertex buffer
|
||||
text_vertex_gpu_base := u32(len(GLOB.tmp_shape_verts))
|
||||
|
||||
for &scissor in GLOB.scissors[layer.scissor_start:][:layer.scissor_len] {
|
||||
sdl.SetGPUScissor(render_pass, scissor.bounds)
|
||||
|
||||
for &batch in GLOB.tmp_sub_batches[scissor.sub_batch_start:][:scissor.sub_batch_len] {
|
||||
switch batch.kind {
|
||||
case .Shapes:
|
||||
if current_mode != .Tessellated {
|
||||
push_globals(cmd_buffer, w, h, .Tessellated)
|
||||
current_mode = .Tessellated
|
||||
}
|
||||
if current_vbuf != main_vbuf {
|
||||
sdl.BindGPUVertexBuffers(
|
||||
render_pass, 0,
|
||||
&sdl.GPUBufferBinding{buffer = main_vbuf, offset = 0}, 1,
|
||||
)
|
||||
current_vbuf = main_vbuf
|
||||
}
|
||||
if current_atlas != white {
|
||||
sdl.BindGPUFragmentSamplers(
|
||||
render_pass, 0,
|
||||
&sdl.GPUTextureSamplerBinding{texture = white, sampler = sampler}, 1,
|
||||
)
|
||||
current_atlas = white
|
||||
}
|
||||
sdl.DrawGPUPrimitives(render_pass, batch.count, 1, batch.offset, 0)
|
||||
|
||||
case .Text:
|
||||
if current_mode != .Tessellated {
|
||||
push_globals(cmd_buffer, w, h, .Tessellated)
|
||||
current_mode = .Tessellated
|
||||
}
|
||||
if current_vbuf != main_vbuf {
|
||||
sdl.BindGPUVertexBuffers(
|
||||
render_pass, 0,
|
||||
&sdl.GPUBufferBinding{buffer = main_vbuf, offset = 0}, 1,
|
||||
)
|
||||
current_vbuf = main_vbuf
|
||||
}
|
||||
chunk := &GLOB.tmp_text_batches[batch.offset]
|
||||
if current_atlas != chunk.atlas_texture {
|
||||
sdl.BindGPUFragmentSamplers(
|
||||
render_pass, 0,
|
||||
&sdl.GPUTextureSamplerBinding {
|
||||
texture = chunk.atlas_texture,
|
||||
sampler = sampler,
|
||||
},
|
||||
1,
|
||||
)
|
||||
current_atlas = chunk.atlas_texture
|
||||
}
|
||||
sdl.DrawGPUIndexedPrimitives(
|
||||
render_pass,
|
||||
chunk.index_count,
|
||||
1,
|
||||
chunk.index_start,
|
||||
i32(text_vertex_gpu_base + chunk.vertex_start),
|
||||
0,
|
||||
)
|
||||
|
||||
case .SDF:
|
||||
if current_mode != .SDF {
|
||||
push_globals(cmd_buffer, w, h, .SDF)
|
||||
current_mode = .SDF
|
||||
}
|
||||
if current_vbuf != unit_quad {
|
||||
sdl.BindGPUVertexBuffers(
|
||||
render_pass, 0,
|
||||
&sdl.GPUBufferBinding{buffer = unit_quad, offset = 0}, 1,
|
||||
)
|
||||
current_vbuf = unit_quad
|
||||
}
|
||||
if current_atlas != white {
|
||||
sdl.BindGPUFragmentSamplers(
|
||||
render_pass, 0,
|
||||
&sdl.GPUTextureSamplerBinding{texture = white, sampler = sampler}, 1,
|
||||
)
|
||||
current_atlas = white
|
||||
}
|
||||
sdl.DrawGPUPrimitives(render_pass, 6, batch.count, 0, batch.offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sdl.EndGPURenderPass(render_pass)
|
||||
}
|
||||
|
||||
destroy_pipeline_2d_base :: proc(device: ^sdl.GPUDevice, pipeline: ^Pipeline_2D_Base) {
|
||||
destroy_buffer(device, &pipeline.vertex_buffer)
|
||||
destroy_buffer(device, &pipeline.index_buffer)
|
||||
destroy_buffer(device, &pipeline.primitive_buffer)
|
||||
if pipeline.unit_quad_buffer != nil {
|
||||
sdl.ReleaseGPUBuffer(device, pipeline.unit_quad_buffer)
|
||||
}
|
||||
sdl.ReleaseGPUTexture(device, pipeline.white_texture)
|
||||
sdl.ReleaseGPUSampler(device, pipeline.sampler)
|
||||
sdl.ReleaseGPUGraphicsPipeline(device, pipeline.sdl_pipeline)
|
||||
}
|
||||
Reference in New Issue
Block a user