258 lines
6.6 KiB
Odin
258 lines
6.6 KiB
Odin
package renderer
|
|
|
|
import "core:log"
|
|
import "core:mem"
|
|
import "core:os"
|
|
import sdl "vendor:sdl3"
|
|
|
|
QuadPipeline :: struct {
|
|
instance_buffer: Buffer,
|
|
num_instances: u32,
|
|
sdl_pipeline: ^sdl.GPUGraphicsPipeline,
|
|
}
|
|
|
|
Quad :: struct {
|
|
position_scale: [4]f32,
|
|
corner_radii: [4]f32,
|
|
color: [4]f32,
|
|
border_color: [4]f32,
|
|
border_width: f32,
|
|
_: [3]f32,
|
|
}
|
|
|
|
quad :: proc(
|
|
pos: [2]f32,
|
|
size: [2]f32,
|
|
color: [4]f32,
|
|
corner_radii := [4]f32{0, 0, 0, 0},
|
|
border_color := [4]f32{0, 0, 0, 0},
|
|
border_width: f32 = 0,
|
|
) -> Quad {
|
|
return Quad {
|
|
{pos.x, pos.y, size.x, size.y},
|
|
corner_radii,
|
|
color,
|
|
border_color,
|
|
border_width,
|
|
{0, 0, 0},
|
|
}
|
|
}
|
|
|
|
@(private)
|
|
create_quad_pipeline :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window) -> QuadPipeline {
|
|
log.debug("Creating quad pipeline")
|
|
when ODIN_OS == .Darwin {
|
|
vert_raw := #load("res/shaders/compiled/quad.vert.metal")
|
|
frag_raw := #load("res/shaders/compiled/quad.frag.metal")
|
|
} else {
|
|
vert_raw := #load("res/shaders/compiled/quad.vert.spv")
|
|
frag_raw := #load("res/shaders/compiled/quad.frag.spv")
|
|
}
|
|
|
|
log.debug("Loaded", len(vert_raw), "vert bytes")
|
|
log.debug("Loaded", len(frag_raw), "frag bytes")
|
|
log.debug("ShaderType:", SHADER_TYPE)
|
|
|
|
vert_info := sdl.GPUShaderCreateInfo {
|
|
code_size = len(vert_raw),
|
|
code = raw_data(vert_raw),
|
|
entrypoint = ENTRY_POINT,
|
|
format = SHADER_TYPE,
|
|
stage = sdl.GPUShaderStage.VERTEX,
|
|
num_uniform_buffers = 1,
|
|
}
|
|
|
|
frag_info := sdl.GPUShaderCreateInfo {
|
|
code_size = len(frag_raw),
|
|
code = raw_data(frag_raw),
|
|
entrypoint = ENTRY_POINT,
|
|
format = SHADER_TYPE,
|
|
stage = sdl.GPUShaderStage.FRAGMENT,
|
|
}
|
|
|
|
vert_shader := sdl.CreateGPUShader(device, vert_info)
|
|
if vert_shader == nil {
|
|
log.error("Could not create vertex shader:", sdl.GetError())
|
|
os.exit(1)
|
|
}
|
|
|
|
frag_shader := sdl.CreateGPUShader(device, frag_info)
|
|
if frag_shader == nil {
|
|
log.error("Could not create fragment shader:", sdl.GetError())
|
|
os.exit(1)
|
|
}
|
|
|
|
vertex_attributes: [5]sdl.GPUVertexAttribute = {
|
|
// position and scale
|
|
sdl.GPUVertexAttribute {
|
|
buffer_slot = 0,
|
|
location = 0,
|
|
format = sdl.GPUVertexElementFormat.FLOAT4,
|
|
offset = 0,
|
|
},
|
|
// corner radii
|
|
sdl.GPUVertexAttribute {
|
|
buffer_slot = 0,
|
|
location = 1,
|
|
format = sdl.GPUVertexElementFormat.FLOAT4,
|
|
offset = size_of(f32) * 4,
|
|
},
|
|
// color
|
|
sdl.GPUVertexAttribute {
|
|
buffer_slot = 0,
|
|
location = 2,
|
|
format = sdl.GPUVertexElementFormat.FLOAT4,
|
|
offset = size_of(f32) * 8,
|
|
},
|
|
// border color
|
|
sdl.GPUVertexAttribute {
|
|
buffer_slot = 0,
|
|
location = 3,
|
|
format = sdl.GPUVertexElementFormat.FLOAT4,
|
|
offset = size_of(f32) * 12,
|
|
},
|
|
// border width
|
|
sdl.GPUVertexAttribute {
|
|
buffer_slot = 0,
|
|
location = 4,
|
|
format = sdl.GPUVertexElementFormat.FLOAT,
|
|
offset = size_of(f32) * 16,
|
|
},
|
|
}
|
|
|
|
pipeline_info := sdl.GPUGraphicsPipelineCreateInfo {
|
|
vertex_shader = vert_shader,
|
|
fragment_shader = frag_shader,
|
|
primitive_type = .TRIANGLELIST,
|
|
target_info = sdl.GPUGraphicsPipelineTargetInfo {
|
|
color_target_descriptions = &sdl.GPUColorTargetDescription {
|
|
format = sdl.GetGPUSwapchainTextureFormat(device, window),
|
|
blend_state = sdl.GPUColorTargetBlendState {
|
|
src_color_blendfactor = .SRC_ALPHA,
|
|
dst_color_blendfactor = .ONE_MINUS_SRC_ALPHA,
|
|
color_blend_op = .ADD,
|
|
src_alpha_blendfactor = .ONE,
|
|
dst_alpha_blendfactor = .ONE_MINUS_SRC_ALPHA,
|
|
alpha_blend_op = .ADD,
|
|
color_write_mask = sdl.GPUColorComponentFlags{.R, .G, .B, .A},
|
|
enable_blend = true,
|
|
enable_color_write_mask = true,
|
|
},
|
|
},
|
|
num_color_targets = 1,
|
|
},
|
|
vertex_input_state = sdl.GPUVertexInputState {
|
|
vertex_buffer_descriptions = &sdl.GPUVertexBufferDescription {
|
|
slot = 0,
|
|
input_rate = sdl.GPUVertexInputRate.INSTANCE,
|
|
instance_step_rate = 1,
|
|
pitch = size_of(Quad),
|
|
},
|
|
num_vertex_buffers = 1,
|
|
vertex_attributes = raw_data(vertex_attributes[:]),
|
|
num_vertex_attributes = 5,
|
|
},
|
|
}
|
|
|
|
sdl_pipeline := sdl.CreateGPUGraphicsPipeline(device, pipeline_info)
|
|
if sdl_pipeline == nil {
|
|
log.error("Failed to create quad graphics pipeline:", sdl.GetError())
|
|
os.exit(1)
|
|
}
|
|
|
|
sdl.ReleaseGPUShader(device, vert_shader)
|
|
sdl.ReleaseGPUShader(device, frag_shader)
|
|
|
|
// Create buffers
|
|
instance_buffer := create_buffer(
|
|
device,
|
|
size_of(Quad) * BUFFER_INIT_SIZE,
|
|
sdl.GPUBufferUsageFlags{.VERTEX},
|
|
)
|
|
|
|
pipeline := QuadPipeline{instance_buffer, BUFFER_INIT_SIZE, sdl_pipeline}
|
|
|
|
log.debug("Done creating quad pipeline")
|
|
return pipeline
|
|
}
|
|
|
|
@(private)
|
|
upload_quads :: proc(device: ^sdl.GPUDevice, pass: ^sdl.GPUCopyPass) {
|
|
using global
|
|
num_quads := u32(len(tmp_quads))
|
|
size := num_quads * size_of(Quad)
|
|
|
|
resize_buffer(device, &quad_pipeline.instance_buffer, size, sdl.GPUBufferUsageFlags{.VERTEX})
|
|
|
|
// Write data
|
|
i_array := sdl.MapGPUTransferBuffer(device, quad_pipeline.instance_buffer.transfer, false)
|
|
mem.copy(i_array, raw_data(tmp_quads), int(size))
|
|
sdl.UnmapGPUTransferBuffer(device, quad_pipeline.instance_buffer.transfer)
|
|
|
|
// Upload
|
|
sdl.UploadToGPUBuffer(
|
|
pass,
|
|
sdl.GPUTransferBufferLocation{transfer_buffer = quad_pipeline.instance_buffer.transfer},
|
|
sdl.GPUBufferRegion{buffer = quad_pipeline.instance_buffer.gpu, offset = 0, size = size},
|
|
false, // TODO figure out what cycling actually does
|
|
)
|
|
}
|
|
|
|
@(private)
|
|
draw_quads :: proc(
|
|
device: ^sdl.GPUDevice,
|
|
window: ^sdl.Window,
|
|
cmd_buffer: ^sdl.GPUCommandBuffer,
|
|
swapchain_texture: ^sdl.GPUTexture,
|
|
swapchain_w: u32,
|
|
swapchain_h: u32,
|
|
layer: ^Layer,
|
|
load_op: sdl.GPULoadOp,
|
|
) {
|
|
using global
|
|
|
|
if layer.quad_len == 0 {
|
|
return
|
|
}
|
|
|
|
render_pass := sdl.BeginGPURenderPass(
|
|
cmd_buffer,
|
|
&sdl.GPUColorTargetInfo {
|
|
texture = swapchain_texture,
|
|
clear_color = sdl.FColor{1.0, 1.0, 1.0, 1.0},
|
|
load_op = load_op,
|
|
store_op = sdl.GPUStoreOp.STORE,
|
|
},
|
|
1,
|
|
nil,
|
|
)
|
|
sdl.BindGPUGraphicsPipeline(render_pass, quad_pipeline.sdl_pipeline)
|
|
|
|
sdl.BindGPUVertexBuffers(
|
|
render_pass,
|
|
0,
|
|
&sdl.GPUBufferBinding{buffer = quad_pipeline.instance_buffer.gpu, offset = 0},
|
|
1,
|
|
)
|
|
push_globals(cmd_buffer, f32(swapchain_w), f32(swapchain_h))
|
|
|
|
quad_offset := layer.quad_instance_start
|
|
|
|
for &scissor, index in scissors[layer.scissor_start:][:layer.scissor_len] {
|
|
if scissor.quad_len == 0 {
|
|
continue
|
|
}
|
|
|
|
sdl.SetGPUScissor(render_pass, scissor.bounds)
|
|
sdl.DrawGPUPrimitives(render_pass, 6, scissor.quad_len, 0, quad_offset)
|
|
quad_offset += scissor.quad_len
|
|
}
|
|
sdl.EndGPURenderPass(render_pass)
|
|
}
|
|
|
|
destroy_quad_pipeline :: proc(device: ^sdl.GPUDevice) {
|
|
using global
|
|
destroy_buffer(device, &quad_pipeline.instance_buffer)
|
|
sdl.ReleaseGPUGraphicsPipeline(device, quad_pipeline.sdl_pipeline)
|
|
}
|