Compare commits
6 Commits
51c46ea279
...
master
Author | SHA1 | Date | |
---|---|---|---|
a30a509bcb | |||
|
a3d6a2762e | ||
753793fa89 | |||
|
fcad0ccfc3 | ||
|
b0fe8f0d29 | ||
|
f11f688ea0 |
@@ -1,12 +1,12 @@
|
||||
package main
|
||||
|
||||
import clay "../clay"
|
||||
import "../renderer"
|
||||
import "core:c"
|
||||
import "core:fmt"
|
||||
import "core:log"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import clay "library:clay"
|
||||
import sdl "vendor:sdl3"
|
||||
|
||||
WINDOW_WIDTH :: 1024
|
||||
@@ -24,8 +24,6 @@ body_text := clay.TextElementConfig {
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
defer destroy()
|
||||
|
||||
when ODIN_DEBUG == true {
|
||||
context.logger = log.create_console_logger(lowest = .Debug)
|
||||
|
||||
@@ -153,9 +151,12 @@ main :: proc() {
|
||||
|
||||
last_frame_time = frame_time
|
||||
}
|
||||
|
||||
destroy()
|
||||
}
|
||||
|
||||
destroy :: proc() {
|
||||
free_all(context.temp_allocator)
|
||||
renderer.destroy(device)
|
||||
sdl.ReleaseWindowFromGPUDevice(device, window)
|
||||
sdl.DestroyWindow(window)
|
||||
@@ -165,8 +166,47 @@ destroy :: proc() {
|
||||
update :: proc(cmd_buffer: ^sdl.GPUCommandBuffer, delta_time: u64) -> bool {
|
||||
frame_time := f32(delta_time) / 1000.0
|
||||
input := input()
|
||||
render_cmds := layout()
|
||||
renderer.prepare(device, window, cmd_buffer, &render_cmds, input.mouse_delta, frame_time)
|
||||
mouse_x, mouse_y: f32
|
||||
mouse_flags := sdl.GetMouseState(&mouse_x, &mouse_y)
|
||||
width, height: c.int
|
||||
sdl.GetWindowSize(window, &width, &height)
|
||||
window_bounds := renderer.Rectangle {
|
||||
x = 0.0,
|
||||
y = 0.0,
|
||||
w = f32(width),
|
||||
h = f32(height),
|
||||
}
|
||||
|
||||
layer := renderer.begin_prepare(window_bounds)
|
||||
// ===== Begin processing primitives for GPU upload =====
|
||||
// Everything after begin_prepare() is uploaded in-order. We pass the layer down
|
||||
// until we need a new one, after which we call new_layer()
|
||||
|
||||
// Process primitives on this layer
|
||||
layout(layer)
|
||||
|
||||
// Process clay-specific primitives
|
||||
clay_layer_bounds := renderer.Rectangle {
|
||||
x = f32(width) / 2.0,
|
||||
y = 0.0,
|
||||
w = f32(width) / 2.0,
|
||||
h = f32(height),
|
||||
}
|
||||
// Create a new layer, because these two scenes cannot be renderer in the same batch due to overlap
|
||||
layer = renderer.new_layer(layer, clay_layer_bounds)
|
||||
clay_batch := clay_layout(clay_layer_bounds)
|
||||
renderer.prepare_clay_batch(
|
||||
layer,
|
||||
{mouse_x, mouse_y},
|
||||
mouse_flags,
|
||||
input.mouse_delta,
|
||||
frame_time,
|
||||
&clay_batch,
|
||||
)
|
||||
|
||||
// This uploads the primitive data to the GPU
|
||||
renderer.end_prepare(device, cmd_buffer)
|
||||
|
||||
return input.should_quit
|
||||
}
|
||||
|
||||
@@ -210,7 +250,8 @@ draw :: proc(cmd_buffer: ^sdl.GPUCommandBuffer) {
|
||||
}
|
||||
}
|
||||
|
||||
layout :: proc() -> clay.ClayArray(clay.RenderCommand) {
|
||||
clay_layout :: proc(bounds: renderer.Rectangle) -> renderer.ClayBatch {
|
||||
clay.SetLayoutDimensions(clay.Dimensions{bounds.w, bounds.h})
|
||||
clay.BeginLayout()
|
||||
|
||||
if clay.UI()(
|
||||
@@ -222,7 +263,7 @@ layout :: proc() -> clay.ClayArray(clay.RenderCommand) {
|
||||
childAlignment = {x = .Center, y = .Center},
|
||||
childGap = 32,
|
||||
},
|
||||
backgroundColor = {200.0, 200.0, 200.0, 255.0},
|
||||
backgroundColor = {200.0, 200.0, 200.0, 100.0},
|
||||
},
|
||||
) {
|
||||
if clay.UI()(
|
||||
@@ -244,8 +285,46 @@ layout :: proc() -> clay.ClayArray(clay.RenderCommand) {
|
||||
) {
|
||||
}
|
||||
|
||||
if clay.UI()(
|
||||
{
|
||||
id = clay.ID("RoundedRect2"),
|
||||
backgroundColor = {255.0, 100.0, 100.0, 255.0},
|
||||
cornerRadius = clay.CornerRadius {
|
||||
topLeft = 10,
|
||||
topRight = 20,
|
||||
bottomLeft = 40,
|
||||
bottomRight = 0,
|
||||
},
|
||||
border = clay.BorderElementConfig {
|
||||
color = {0.0, 0.0, 0.0, 255.0},
|
||||
width = clay.BorderAll(5),
|
||||
},
|
||||
layout = {sizing = {clay.SizingFixed(240), clay.SizingFixed(80)}},
|
||||
},
|
||||
) {
|
||||
}
|
||||
|
||||
clay.Text("Test Text", &body_text)
|
||||
}
|
||||
|
||||
return clay.EndLayout()
|
||||
return renderer.ClayBatch{bounds, clay.EndLayout()}
|
||||
}
|
||||
|
||||
layout :: proc(layer: ^renderer.Layer) {
|
||||
bounds := layer.bounds
|
||||
|
||||
test_quad := renderer.quad(
|
||||
pos = {bounds.x + 200, bounds.y + 200},
|
||||
size = {bounds.w / 2.0, bounds.h / 2.0},
|
||||
color = {0.2, 0.2, 0.8, 1},
|
||||
corner_radii = {5, 10, 0, 20},
|
||||
border_color = {0, 0, 0, 1},
|
||||
border_width = 10,
|
||||
)
|
||||
renderer.prepare_quad(layer, test_quad)
|
||||
|
||||
text_ok, text := renderer.text(0, "Raw Text", {bounds.x + 80, bounds.y + 80})
|
||||
if text_ok {
|
||||
renderer.prepare_text(layer, text)
|
||||
}
|
||||
}
|
||||
|
@@ -13,8 +13,8 @@ fi
|
||||
|
||||
# Convert GLSL to SPIRV
|
||||
echo "Converting GLSL shaders to SPIRV..."
|
||||
mkdir -p renderer/res/shaders/compiled
|
||||
cd renderer/res/shaders/raw || exit
|
||||
mkdir -p res/shaders/compiled
|
||||
cd res/shaders/raw || exit
|
||||
glslangValidator -V quad.vert -o ../compiled/quad.vert.spv
|
||||
glslangValidator -V quad.frag -o ../compiled/quad.frag.spv
|
||||
glslangValidator -V text.vert -o ../compiled/text.vert.spv
|
||||
|
@@ -5,8 +5,6 @@ import "core:mem"
|
||||
import "core:os"
|
||||
import sdl "vendor:sdl3"
|
||||
|
||||
tmp_quads: [dynamic]Quad
|
||||
|
||||
QuadPipeline :: struct {
|
||||
instance_buffer: Buffer,
|
||||
num_instances: u32,
|
||||
@@ -22,6 +20,24 @@ Quad :: struct {
|
||||
_: [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")
|
||||
@@ -37,7 +53,6 @@ create_quad_pipeline :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window) -> Qua
|
||||
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),
|
||||
@@ -152,7 +167,7 @@ create_quad_pipeline :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window) -> Qua
|
||||
instance_buffer := create_buffer(
|
||||
device,
|
||||
size_of(Quad) * BUFFER_INIT_SIZE,
|
||||
sdl.GPUBufferUsageFlags { .VERTEX },
|
||||
sdl.GPUBufferUsageFlags{.VERTEX},
|
||||
)
|
||||
|
||||
pipeline := QuadPipeline{instance_buffer, BUFFER_INIT_SIZE, sdl_pipeline}
|
||||
@@ -163,10 +178,11 @@ create_quad_pipeline :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window) -> Qua
|
||||
|
||||
@(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 })
|
||||
resize_buffer(device, &quad_pipeline.instance_buffer, size, sdl.GPUBufferUsageFlags{.VERTEX})
|
||||
|
||||
// Write data
|
||||
i_array := sdl.MapGPUTransferBuffer(device, quad_pipeline.instance_buffer.transfer, false)
|
||||
@@ -193,7 +209,9 @@ draw_quads :: proc(
|
||||
layer: ^Layer,
|
||||
load_op: sdl.GPULoadOp,
|
||||
) {
|
||||
if layer.quad_len == 0 {
|
||||
using global
|
||||
|
||||
if layer.quad_instance_len == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -220,17 +238,12 @@ draw_quads :: proc(
|
||||
|
||||
quad_offset := layer.quad_instance_start
|
||||
|
||||
for &scissor, index in layer.scissors {
|
||||
for &scissor, index in scissors[layer.scissor_start:][:layer.scissor_len] {
|
||||
if scissor.quad_len == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if scissor.bounds.w == 0 || scissor.bounds.h == 0 {
|
||||
sdl.SetGPUScissor(render_pass, sdl.Rect{0, 0, i32(swapchain_w), i32(swapchain_h)})
|
||||
} else {
|
||||
sdl.SetGPUScissor(render_pass, scissor.bounds)
|
||||
}
|
||||
|
||||
sdl.DrawGPUPrimitives(render_pass, 6, scissor.quad_len, 0, quad_offset)
|
||||
quad_offset += scissor.quad_len
|
||||
}
|
||||
@@ -238,6 +251,7 @@ draw_quads :: proc(
|
||||
}
|
||||
|
||||
destroy_quad_pipeline :: proc(device: ^sdl.GPUDevice) {
|
||||
using global
|
||||
destroy_buffer(device, &quad_pipeline.instance_buffer)
|
||||
sdl.ReleaseGPUGraphicsPipeline(device, quad_pipeline.sdl_pipeline)
|
||||
}
|
||||
|
@@ -1,11 +1,11 @@
|
||||
package renderer
|
||||
|
||||
import clay "../clay"
|
||||
import "base:runtime"
|
||||
import "core:c"
|
||||
import "core:log"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
import clay "library:clay"
|
||||
import sdl "vendor:sdl3"
|
||||
import sdl_ttf "vendor:sdl3/ttf"
|
||||
|
||||
@@ -18,24 +18,85 @@ when ODIN_OS == .Darwin {
|
||||
}
|
||||
|
||||
BUFFER_INIT_SIZE: u32 : 256
|
||||
INITIAL_LAYER_SIZE :: 5
|
||||
INITIAL_SCISSOR_SIZE :: 10
|
||||
|
||||
dpi_scaling: f32 = 1.0
|
||||
layers: [dynamic]Layer
|
||||
quad_pipeline: QuadPipeline
|
||||
text_pipeline: TextPipeline
|
||||
odin_context: runtime.Context
|
||||
Global :: struct {
|
||||
dpi_scaling: f32,
|
||||
curr_layer_index: uint,
|
||||
layers: [dynamic]Layer,
|
||||
max_layers: int,
|
||||
scissors: [dynamic]Scissor,
|
||||
max_scissors: int,
|
||||
tmp_text: [dynamic]Text,
|
||||
max_tmp_text: int,
|
||||
tmp_quads: [dynamic]Quad,
|
||||
max_tmp_quads: int,
|
||||
clay_mem: [^]u8,
|
||||
clay_z_index: i16,
|
||||
odin_context: runtime.Context,
|
||||
quad_pipeline: QuadPipeline,
|
||||
text_pipeline: TextPipeline,
|
||||
}
|
||||
|
||||
// TODO every x frames nuke max values in case of edge cases where max gets set very high
|
||||
// Called at the end of every frame
|
||||
resize_global :: proc() {
|
||||
using global
|
||||
|
||||
if len(layers) > max_layers do max_layers = len(layers)
|
||||
shrink(&layers, max_layers)
|
||||
if len(scissors) > max_scissors do max_scissors = len(scissors)
|
||||
shrink(&scissors, max_scissors)
|
||||
if len(tmp_text) > max_tmp_text do max_tmp_text = len(tmp_text)
|
||||
shrink(&tmp_text, max_tmp_text)
|
||||
if len(tmp_quads) > max_tmp_quads do max_tmp_quads = len(tmp_quads)
|
||||
shrink(&tmp_quads, max_tmp_quads)
|
||||
}
|
||||
|
||||
destroy :: proc(device: ^sdl.GPUDevice) {
|
||||
using global
|
||||
delete(layers)
|
||||
delete(scissors)
|
||||
delete(tmp_text)
|
||||
delete(tmp_quads)
|
||||
free(clay_mem)
|
||||
destroy_quad_pipeline(device)
|
||||
destroy_text_pipeline(device)
|
||||
}
|
||||
|
||||
clear_global :: proc() {
|
||||
using global
|
||||
|
||||
curr_layer_index = 0
|
||||
clay_z_index = 0
|
||||
clear(&layers)
|
||||
clear(&scissors)
|
||||
clear(&tmp_text)
|
||||
clear(&tmp_quads)
|
||||
}
|
||||
|
||||
global: Global
|
||||
|
||||
Rectangle :: struct {
|
||||
x: f32,
|
||||
y: f32,
|
||||
w: f32,
|
||||
h: f32,
|
||||
}
|
||||
|
||||
// TODO New layer for each z-index/batch
|
||||
Layer :: struct {
|
||||
bounds: Rectangle,
|
||||
quad_instance_start: u32,
|
||||
quad_len: u32,
|
||||
quad_instance_len: u32,
|
||||
text_instance_start: u32,
|
||||
text_instance_len: u32,
|
||||
text_vertex_start: u32,
|
||||
text_vertex_len: u32,
|
||||
text_index_start: u32,
|
||||
text_index_len: u32,
|
||||
scissors: [dynamic]Scissor,
|
||||
scissor_start: u32,
|
||||
scissor_len: u32,
|
||||
}
|
||||
|
||||
Scissor :: struct {
|
||||
@@ -54,22 +115,29 @@ init :: proc(
|
||||
window_height: f32,
|
||||
ctx: runtime.Context,
|
||||
) {
|
||||
odin_context = ctx
|
||||
dpi_scaling = sdl.GetWindowDisplayScale(window)
|
||||
log.debug("Window DPI scaling:", dpi_scaling)
|
||||
|
||||
min_memory_size: c.size_t = cast(c.size_t)clay.MinMemorySize()
|
||||
memory := make([^]u8, min_memory_size)
|
||||
arena := clay.CreateArenaWithCapacityAndMemory(min_memory_size, memory)
|
||||
|
||||
global = Global {
|
||||
layers = make([dynamic]Layer, 0, INITIAL_LAYER_SIZE),
|
||||
scissors = make([dynamic]Scissor, 0, INITIAL_SCISSOR_SIZE),
|
||||
tmp_quads = make([dynamic]Quad, 0, BUFFER_INIT_SIZE),
|
||||
tmp_text = make([dynamic]Text, 0, BUFFER_INIT_SIZE),
|
||||
odin_context = ctx,
|
||||
dpi_scaling = sdl.GetWindowDisplayScale(window),
|
||||
clay_mem = make([^]u8, min_memory_size),
|
||||
quad_pipeline = create_quad_pipeline(device, window),
|
||||
text_pipeline = create_text_pipeline(device, window),
|
||||
}
|
||||
log.debug("Window DPI scaling:", global.dpi_scaling)
|
||||
arena := clay.CreateArenaWithCapacityAndMemory(min_memory_size, global.clay_mem)
|
||||
|
||||
clay.Initialize(arena, {window_width, window_height}, {handler = clay_error_handler})
|
||||
clay.SetMeasureTextFunction(measure_text, nil)
|
||||
quad_pipeline = create_quad_pipeline(device, window)
|
||||
text_pipeline = create_text_pipeline(device, window)
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
clay_error_handler :: proc "c" (errorData: clay.ErrorData) {
|
||||
context = odin_context
|
||||
context = global.odin_context
|
||||
log.error("Clay error:", errorData.errorType, errorData.errorText)
|
||||
}
|
||||
|
||||
@@ -79,6 +147,7 @@ measure_text :: proc "c" (
|
||||
config: ^clay.TextElementConfig,
|
||||
user_data: rawptr,
|
||||
) -> clay.Dimensions {
|
||||
using global
|
||||
context = odin_context
|
||||
text := string(text.chars[:text.length])
|
||||
c_text := strings.clone_to_cstring(text, context.temp_allocator)
|
||||
@@ -90,47 +159,146 @@ measure_text :: proc "c" (
|
||||
return clay.Dimensions{width = f32(w) / dpi_scaling, height = f32(h) / dpi_scaling}
|
||||
}
|
||||
|
||||
destroy :: proc(device: ^sdl.GPUDevice) {
|
||||
destroy_quad_pipeline(device)
|
||||
destroy_text_pipeline(device)
|
||||
/// Sets up renderer to begin upload to the GPU. Returns starting `Layer` to begin processing primitives for
|
||||
begin_prepare :: proc(bounds: Rectangle) -> ^Layer {
|
||||
using global
|
||||
// Cleanup
|
||||
clear_global()
|
||||
|
||||
// Begin new layer
|
||||
// Start a new scissor
|
||||
scissor := Scissor {
|
||||
bounds = sdl.Rect {
|
||||
x = i32(bounds.x * dpi_scaling),
|
||||
y = i32(bounds.y * dpi_scaling),
|
||||
w = i32(bounds.w * dpi_scaling),
|
||||
h = i32(bounds.h * dpi_scaling),
|
||||
},
|
||||
}
|
||||
append(&scissors, scissor)
|
||||
|
||||
layer := Layer {
|
||||
bounds = bounds,
|
||||
scissor_len = 1,
|
||||
}
|
||||
append(&layers, layer)
|
||||
return &layers[curr_layer_index]
|
||||
}
|
||||
|
||||
/// Creates a new layer
|
||||
new_layer :: proc(prev_layer: ^Layer, bounds: Rectangle) -> ^Layer {
|
||||
using global
|
||||
layer := Layer {
|
||||
bounds = bounds,
|
||||
quad_instance_start = prev_layer.quad_instance_start + prev_layer.quad_instance_len,
|
||||
text_instance_start = prev_layer.text_instance_start + prev_layer.text_instance_len,
|
||||
text_vertex_start = prev_layer.text_vertex_start + prev_layer.text_vertex_len,
|
||||
text_index_start = prev_layer.text_index_start + prev_layer.text_index_len,
|
||||
scissor_start = prev_layer.scissor_start + prev_layer.scissor_len,
|
||||
scissor_len = 1,
|
||||
}
|
||||
append(&layers, layer)
|
||||
curr_layer_index += 1
|
||||
log.debug("Added new layer; curr index", curr_layer_index)
|
||||
|
||||
scissor := Scissor {
|
||||
bounds = sdl.Rect {
|
||||
x = i32(bounds.x * dpi_scaling),
|
||||
y = i32(bounds.y * dpi_scaling),
|
||||
w = i32(bounds.w * dpi_scaling),
|
||||
h = i32(bounds.h * dpi_scaling),
|
||||
},
|
||||
}
|
||||
append(&scissors, scissor)
|
||||
return &layers[curr_layer_index]
|
||||
}
|
||||
|
||||
end_prepare :: proc(device: ^sdl.GPUDevice, cmd_buffer: ^sdl.GPUCommandBuffer) {
|
||||
// Upload primitives to GPU
|
||||
copy_pass := sdl.BeginGPUCopyPass(cmd_buffer)
|
||||
upload_quads(device, copy_pass)
|
||||
upload_text(device, copy_pass)
|
||||
sdl.EndGPUCopyPass(copy_pass)
|
||||
|
||||
// Resize my dynamic arrays
|
||||
resize_global()
|
||||
}
|
||||
|
||||
// ===== Built-in primitive processing =====
|
||||
// TODO scissoring support if I need it for primitives
|
||||
prepare_quad :: proc(layer: ^Layer, quad: Quad) {
|
||||
using global
|
||||
|
||||
append(&tmp_quads, quad)
|
||||
layer.quad_instance_len += 1
|
||||
scissors[layer.scissor_start + layer.scissor_len - 1].quad_len += 1
|
||||
}
|
||||
|
||||
// TODO need to make sure no overlap with clay render command IDs
|
||||
prepare_text :: proc(layer: ^Layer, text: Text) {
|
||||
using global
|
||||
|
||||
data := sdl_ttf.GetGPUTextDrawData(text.ref)
|
||||
if data == nil {
|
||||
log.error("Failed to find GPUTextDrawData for sdl_text")
|
||||
}
|
||||
|
||||
append(&tmp_text, text)
|
||||
layer.text_instance_len += 1
|
||||
layer.text_vertex_len += u32(data.num_vertices)
|
||||
layer.text_index_len += u32(data.num_indices)
|
||||
scissors[layer.scissor_start + layer.scissor_len - 1].text_len += 1
|
||||
}
|
||||
|
||||
// ====== Clay-specific processing ======
|
||||
ClayBatch :: struct {
|
||||
bounds: Rectangle,
|
||||
cmds: clay.ClayArray(clay.RenderCommand),
|
||||
}
|
||||
|
||||
/// Upload data to the GPU
|
||||
prepare :: proc(
|
||||
device: ^sdl.GPUDevice,
|
||||
window: ^sdl.Window,
|
||||
cmd_buffer: ^sdl.GPUCommandBuffer,
|
||||
render_commands: ^clay.ClayArray(clay.RenderCommand),
|
||||
mouse_delta: [2]f32,
|
||||
prepare_clay_batch :: proc(
|
||||
base_layer: ^Layer,
|
||||
mouse_pos: [2]f32,
|
||||
mouse_flags: sdl.MouseButtonFlags,
|
||||
mouse_wheel_delta: [2]f32,
|
||||
frame_time: f32,
|
||||
batch: ^ClayBatch,
|
||||
) {
|
||||
mouse_x, mouse_y: f32
|
||||
mouse_flags := sdl.GetMouseState(&mouse_x, &mouse_y)
|
||||
// Currently MacOS blocks main thread when resizing, this will be fixed with next SDL3 release
|
||||
window_w, window_h: c.int
|
||||
window_size := sdl.GetWindowSize(window, &window_w, &window_h)
|
||||
using global
|
||||
|
||||
// Update clay internals
|
||||
clay.SetPointerState(clay.Vector2{mouse_x, mouse_y}, .LEFT in mouse_flags)
|
||||
clay.UpdateScrollContainers(true, transmute(clay.Vector2)mouse_delta, frame_time)
|
||||
clay.SetLayoutDimensions({f32(window_w), f32(window_h)})
|
||||
clay.SetPointerState(
|
||||
clay.Vector2{mouse_pos.x - base_layer.bounds.x, mouse_pos.y - base_layer.bounds.y},
|
||||
.LEFT in mouse_flags,
|
||||
)
|
||||
clay.UpdateScrollContainers(true, transmute(clay.Vector2)mouse_wheel_delta, frame_time)
|
||||
|
||||
clear(&layers)
|
||||
clear(&tmp_quads)
|
||||
clear(&tmp_text)
|
||||
|
||||
tmp_quads = make([dynamic]Quad, 0, quad_pipeline.num_instances, context.temp_allocator)
|
||||
tmp_text = make([dynamic]Text, 0, 20, context.temp_allocator)
|
||||
|
||||
layer := Layer {
|
||||
scissors = make([dynamic]Scissor, 0, 10, context.temp_allocator),
|
||||
}
|
||||
scissor := Scissor{}
|
||||
layer := base_layer
|
||||
|
||||
// Parse render commands
|
||||
for i in 0 ..< int(render_commands.length) {
|
||||
render_command := clay.RenderCommandArray_Get(render_commands, cast(i32)i)
|
||||
bounds := render_command.boundingBox
|
||||
for i in 0 ..< int(batch.cmds.length) {
|
||||
render_command := clay.RenderCommandArray_Get(&batch.cmds, cast(i32)i)
|
||||
|
||||
// Translate bounding box of the primitive by the layer position
|
||||
bounds := Rectangle {
|
||||
x = render_command.boundingBox.x + layer.bounds.x,
|
||||
y = render_command.boundingBox.y + layer.bounds.y,
|
||||
w = render_command.boundingBox.width,
|
||||
h = render_command.boundingBox.height,
|
||||
}
|
||||
|
||||
if render_command.zIndex > clay_z_index {
|
||||
log.debug(
|
||||
"Higher zIndex found, creating new layer & setting z_index to",
|
||||
render_command.zIndex,
|
||||
)
|
||||
layer = new_layer(layer, bounds)
|
||||
// Update bounds to new layer offset
|
||||
bounds.x = render_command.boundingBox.x + layer.bounds.x
|
||||
bounds.y = render_command.boundingBox.y + layer.bounds.y
|
||||
clay_z_index = render_command.zIndex
|
||||
}
|
||||
|
||||
switch (render_command.commandType) {
|
||||
case clay.RenderCommandType.None:
|
||||
@@ -155,6 +323,9 @@ prepare :: proc(
|
||||
}
|
||||
|
||||
data := sdl_ttf.GetGPUTextDrawData(sdl_text)
|
||||
if data == nil {
|
||||
log.error("Failed to find GPUTextDrawData for sdl_text:", c_text)
|
||||
}
|
||||
|
||||
if sdl_text == nil {
|
||||
log.error("Could not create SDL text:", sdl.GetError())
|
||||
@@ -166,46 +337,57 @@ prepare :: proc(
|
||||
layer.text_instance_len += 1
|
||||
layer.text_vertex_len += u32(data.num_vertices)
|
||||
layer.text_index_len += u32(data.num_indices)
|
||||
scissor.text_len += 1
|
||||
scissors[layer.scissor_start + layer.scissor_len - 1].text_len += 1
|
||||
}
|
||||
case clay.RenderCommandType.Image:
|
||||
case clay.RenderCommandType.ScissorStart:
|
||||
bounds := sdl.Rect {
|
||||
if bounds.w == 0 || bounds.h == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
curr_scissor := &scissors[layer.scissor_start + layer.scissor_len - 1]
|
||||
|
||||
if curr_scissor.quad_len != 0 || curr_scissor.text_len != 0 {
|
||||
// Scissor has some content, need to make a new scissor
|
||||
new := Scissor {
|
||||
quad_start = curr_scissor.quad_start + curr_scissor.quad_len,
|
||||
text_start = curr_scissor.text_start + curr_scissor.text_len,
|
||||
bounds = sdl.Rect {
|
||||
c.int(bounds.x * dpi_scaling),
|
||||
c.int(bounds.y * dpi_scaling),
|
||||
c.int(bounds.width * dpi_scaling),
|
||||
c.int(bounds.height * dpi_scaling),
|
||||
c.int(bounds.w * dpi_scaling),
|
||||
c.int(bounds.h * dpi_scaling),
|
||||
},
|
||||
}
|
||||
append(&scissors, new)
|
||||
layer.scissor_len += 1
|
||||
} else {
|
||||
curr_scissor.bounds = sdl.Rect {
|
||||
c.int(bounds.x * dpi_scaling),
|
||||
c.int(bounds.y * dpi_scaling),
|
||||
c.int(bounds.w * dpi_scaling),
|
||||
c.int(bounds.h * dpi_scaling),
|
||||
}
|
||||
new := new_scissor(&scissor)
|
||||
if scissor.quad_len != 0 || scissor.text_len != 0 {
|
||||
append(&layer.scissors, scissor)
|
||||
}
|
||||
scissor = new
|
||||
scissor.bounds = bounds
|
||||
case clay.RenderCommandType.ScissorEnd:
|
||||
new := new_scissor(&scissor)
|
||||
if scissor.quad_len != 0 || scissor.text_len != 0 {
|
||||
append(&layer.scissors, scissor)
|
||||
}
|
||||
scissor = new
|
||||
case clay.RenderCommandType.Rectangle:
|
||||
render_data := render_command.renderData.rectangle
|
||||
color := f32_color(render_data.backgroundColor)
|
||||
cr := render_data.cornerRadius
|
||||
quad := Quad {
|
||||
position_scale = {bounds.x, bounds.y, bounds.width, bounds.height},
|
||||
position_scale = {bounds.x, bounds.y, bounds.w, bounds.h},
|
||||
corner_radii = {cr.bottomRight, cr.topRight, cr.bottomLeft, cr.topLeft},
|
||||
color = color,
|
||||
}
|
||||
append(&tmp_quads, quad)
|
||||
layer.quad_len += 1
|
||||
scissor.quad_len += 1
|
||||
|
||||
layer.quad_instance_len += 1
|
||||
scissors[layer.scissor_start + layer.scissor_len - 1].quad_len += 1
|
||||
case clay.RenderCommandType.Border:
|
||||
render_data := render_command.renderData.border
|
||||
cr := render_data.cornerRadius
|
||||
//TODO dedicated border pipeline
|
||||
quad := Quad {
|
||||
position_scale = {bounds.x, bounds.y, bounds.width, bounds.height},
|
||||
position_scale = {bounds.x, bounds.y, bounds.w, bounds.h},
|
||||
corner_radii = {cr.bottomRight, cr.topRight, cr.bottomLeft, cr.topLeft},
|
||||
color = f32_color(clay.Color{0.0, 0.0, 0.0, 0.0}),
|
||||
border_color = f32_color(render_data.color),
|
||||
@@ -215,25 +397,16 @@ prepare :: proc(
|
||||
// Technically these should be drawn on top of everything else including children, but
|
||||
// for our use case we can just chuck these in with the quad pipeline
|
||||
append(&tmp_quads, quad)
|
||||
layer.quad_len += 1
|
||||
scissor.quad_len += 1
|
||||
layer.quad_instance_len += 1
|
||||
scissors[layer.scissor_start + layer.scissor_len - 1].quad_len += 1
|
||||
case clay.RenderCommandType.Custom:
|
||||
}
|
||||
}
|
||||
|
||||
//TODO start new layers with z-index changes
|
||||
append(&layer.scissors, scissor)
|
||||
append(&layers, layer)
|
||||
|
||||
// Upload primitives to GPU
|
||||
copy_pass := sdl.BeginGPUCopyPass(cmd_buffer)
|
||||
upload_quads(device, copy_pass)
|
||||
upload_text(device, copy_pass)
|
||||
sdl.EndGPUCopyPass(copy_pass)
|
||||
}
|
||||
|
||||
/// Render primitives
|
||||
draw :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window, cmd_buffer: ^sdl.GPUCommandBuffer) {
|
||||
using global
|
||||
swapchain_texture: ^sdl.GPUTexture
|
||||
w, h: u32
|
||||
if !sdl.WaitAndAcquireGPUSwapchainTexture(cmd_buffer, window, &swapchain_texture, &w, &h) {
|
||||
@@ -247,6 +420,7 @@ draw :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window, cmd_buffer: ^sdl.GPUCo
|
||||
}
|
||||
|
||||
for &layer, index in layers {
|
||||
log.debug("Drawing layer", index)
|
||||
draw_quads(
|
||||
device,
|
||||
window,
|
||||
@@ -258,7 +432,7 @@ draw :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window, cmd_buffer: ^sdl.GPUCo
|
||||
index == 0 ? sdl.GPULoadOp.CLEAR : sdl.GPULoadOp.LOAD,
|
||||
)
|
||||
draw_text(device, window, cmd_buffer, swapchain_texture, w, h, &layer)
|
||||
//TODO draw other primitives in layer
|
||||
//TODO draw other primitives in layer once I add support for them :)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,15 +464,8 @@ Globals :: struct {
|
||||
push_globals :: proc(cmd_buffer: ^sdl.GPUCommandBuffer, w: f32, h: f32) {
|
||||
globals := Globals {
|
||||
ortho_rh(left = 0.0, top = 0.0, right = f32(w), bottom = f32(h), near = -1.0, far = 1.0),
|
||||
dpi_scaling,
|
||||
global.dpi_scaling,
|
||||
}
|
||||
|
||||
sdl.PushGPUVertexUniformData(cmd_buffer, 0, &globals, size_of(Globals))
|
||||
}
|
||||
|
||||
new_scissor :: proc(old: ^Scissor) -> Scissor {
|
||||
return Scissor {
|
||||
quad_start = old.quad_start + old.quad_len,
|
||||
text_start = old.text_start + old.text_len,
|
||||
}
|
||||
}
|
||||
|
@@ -12,8 +12,6 @@ JETBRAINS_MONO_BOLD: u16 : 1
|
||||
NUM_FONTS :: 2
|
||||
MAX_FONT_SIZE :: 120
|
||||
|
||||
tmp_text: [dynamic]Text
|
||||
|
||||
@(private = "file")
|
||||
jetbrains_mono_regular := #load("res/fonts/JetBrainsMono-Regular.ttf")
|
||||
@(private = "file")
|
||||
@@ -31,7 +29,7 @@ TextPipeline :: struct {
|
||||
}
|
||||
|
||||
get_font :: proc(id: u16, size: u16) -> ^sdl_ttf.Font {
|
||||
font := text_pipeline.fonts[id > 1 ? 0 : id][size > 0 ? size : 16]
|
||||
font := global.text_pipeline.fonts[id > 1 ? 0 : id][size > 0 ? size : 16]
|
||||
|
||||
if font == nil {
|
||||
log.debug("Font not found for size", size, "+ adding")
|
||||
@@ -45,8 +43,13 @@ get_font :: proc(id: u16, size: u16) -> ^sdl_ttf.Font {
|
||||
os.exit(1)
|
||||
}
|
||||
font = f
|
||||
_ = sdl_ttf.SetFontSizeDPI(f, f32(size), 72 * i32(dpi_scaling), 72 * i32(dpi_scaling))
|
||||
text_pipeline.fonts[id][size] = f
|
||||
_ = sdl_ttf.SetFontSizeDPI(
|
||||
f,
|
||||
f32(size),
|
||||
72 * i32(global.dpi_scaling),
|
||||
72 * i32(global.dpi_scaling),
|
||||
)
|
||||
global.text_pipeline.fonts[id][size] = f
|
||||
}
|
||||
|
||||
return font
|
||||
@@ -58,6 +61,38 @@ Text :: struct {
|
||||
color: [4]f32,
|
||||
}
|
||||
|
||||
text :: proc(
|
||||
id: u32,
|
||||
txt: cstring,
|
||||
pos: [2]f32,
|
||||
color: [4]f32 = {0.0, 0.0, 0.0, 1.0},
|
||||
font_id: u16 = JETBRAINS_MONO_REGULAR,
|
||||
font_size: u16 = 44,
|
||||
) -> (bool, Text) {
|
||||
using global
|
||||
|
||||
sdl_text := text_pipeline.cache[id]
|
||||
if sdl_text == nil {
|
||||
sdl_text = sdl_ttf.CreateText(text_pipeline.engine, get_font(font_id, font_size), txt, 0)
|
||||
text_pipeline.cache[id] = sdl_text
|
||||
} else {
|
||||
//TODO if IDs are always unique and never change the underlying text
|
||||
// can get rid of this
|
||||
_ = sdl_ttf.SetTextString(sdl_text, txt, 0)
|
||||
}
|
||||
|
||||
if sdl_text == nil {
|
||||
log.error("Could not create SDL text:", sdl.GetError())
|
||||
return false, Text {}
|
||||
} else {
|
||||
return true, Text {
|
||||
sdl_text,
|
||||
pos,
|
||||
color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For upload
|
||||
TextVert :: struct {
|
||||
pos_uv: [4]f32,
|
||||
@@ -241,11 +276,14 @@ create_text_pipeline :: proc(device: ^sdl.GPUDevice, window: ^sdl.Window) -> Tex
|
||||
|
||||
@(private)
|
||||
upload_text :: proc(device: ^sdl.GPUDevice, pass: ^sdl.GPUCopyPass) {
|
||||
using global
|
||||
|
||||
// TODO maybe don't use tmp here
|
||||
vertices := make([dynamic]TextVert, 0, BUFFER_INIT_SIZE, context.temp_allocator)
|
||||
indices := make([dynamic]c.int, 0, BUFFER_INIT_SIZE, context.temp_allocator)
|
||||
instances := make([dynamic][2]f32, 0, BUFFER_INIT_SIZE, context.temp_allocator)
|
||||
|
||||
for &text, index in tmp_text {
|
||||
for &text, index in global.tmp_text {
|
||||
append(&instances, text.position)
|
||||
data := sdl_ttf.GetGPUTextDrawData(text.ref)
|
||||
for data != nil {
|
||||
@@ -344,6 +382,7 @@ draw_text :: proc(
|
||||
swapchain_h: u32,
|
||||
layer: ^Layer,
|
||||
) {
|
||||
using global
|
||||
if layer.text_instance_len == 0 {
|
||||
return
|
||||
}
|
||||
@@ -376,24 +415,19 @@ draw_text :: proc(
|
||||
|
||||
atlas: ^sdl.GPUTexture
|
||||
|
||||
layer_text := tmp_text[layer.text_instance_start:layer.text_instance_start +
|
||||
layer.text_instance_len]
|
||||
index_offset: u32 = layer.text_instance_start
|
||||
layer_text := tmp_text[layer.text_instance_start:][:layer.text_instance_len]
|
||||
index_offset: u32 = layer.text_index_start
|
||||
vertex_offset: i32 = i32(layer.text_vertex_start)
|
||||
instance_offset: u32 = layer.text_instance_start
|
||||
|
||||
for &scissor, index in layer.scissors {
|
||||
for &scissor, index in scissors[layer.scissor_start:][:layer.scissor_len] {
|
||||
if scissor.text_len == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if scissor.bounds.w == 0 || scissor.bounds.h == 0 {
|
||||
sdl.SetGPUScissor(render_pass, sdl.Rect{0, 0, i32(swapchain_w), i32(swapchain_h)})
|
||||
} else {
|
||||
sdl.SetGPUScissor(render_pass, scissor.bounds)
|
||||
}
|
||||
|
||||
for &text in layer_text[scissor.text_start:scissor.text_start + scissor.text_len] {
|
||||
for &text in layer_text[scissor.text_start:][:scissor.text_len] {
|
||||
data := sdl_ttf.GetGPUTextDrawData(text.ref)
|
||||
|
||||
for data != nil {
|
||||
@@ -433,7 +467,10 @@ draw_text :: proc(
|
||||
}
|
||||
|
||||
destroy_text_pipeline :: proc(device: ^sdl.GPUDevice) {
|
||||
using global
|
||||
destroy_buffer(device, &text_pipeline.vertex_buffer)
|
||||
destroy_buffer(device, &text_pipeline.index_buffer)
|
||||
destroy_buffer(device, &text_pipeline.instance_buffer)
|
||||
delete(text_pipeline.cache)
|
||||
sdl.ReleaseGPUGraphicsPipeline(device, text_pipeline.sdl_pipeline)
|
||||
}
|
||||
|
Reference in New Issue
Block a user