Initial pipeline work
This commit is contained in:
+240
-13
@@ -3,9 +3,29 @@ package engine
|
|||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import sdl "vendor:sdl3"
|
import sdl "vendor:sdl3"
|
||||||
|
|
||||||
|
Vertex :: struct {
|
||||||
|
pos: [2]f32, // clip space, -1..1
|
||||||
|
uv: [2]f32, // 0..1 atlas coords (used later in draw_sprite)
|
||||||
|
}
|
||||||
|
|
||||||
|
SPRITE_VERT_COUNT :: 6
|
||||||
|
VERTEX_BUFFER_SIZE :: SPRITE_VERT_COUNT * size_of(Vertex)
|
||||||
|
|
||||||
|
VERT_SPV := #load("../shaders/sprite.vert.spv")
|
||||||
|
FRAG_SPV := #load("../shaders/sprite.frag.spv")
|
||||||
|
|
||||||
App :: struct {
|
App :: struct {
|
||||||
window: ^sdl.Window,
|
window: ^sdl.Window,
|
||||||
renderer: ^sdl.Renderer,
|
device: ^sdl.GPUDevice,
|
||||||
|
pipeline: ^sdl.GPUGraphicsPipeline,
|
||||||
|
sampler: ^sdl.GPUSampler,
|
||||||
|
cmd: ^sdl.GPUCommandBuffer,
|
||||||
|
render_pass: ^sdl.GPURenderPass,
|
||||||
|
swapchain_texture: ^sdl.GPUTexture,
|
||||||
|
swapchain_w: u32,
|
||||||
|
swapchain_h: u32,
|
||||||
|
vertex_buffer: ^sdl.GPUBuffer,
|
||||||
|
transfer_buffer: ^sdl.GPUTransferBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
init :: proc(app: ^App, title: cstring, width, height: i32) -> bool {
|
init :: proc(app: ^App, title: cstring, width, height: i32) -> bool {
|
||||||
@@ -14,8 +34,64 @@ init :: proc(app: ^App, title: cstring, width, height: i32) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sdl.CreateWindowAndRenderer(title, width, height, {}, &app.window, &app.renderer) {
|
app.window = sdl.CreateWindow(title, width, height, {})
|
||||||
fmt.eprintfln("CreateWindowAndRenderer failed: %s", sdl.GetError())
|
|
||||||
|
if app.window == nil {
|
||||||
|
fmt.eprintfln("CreateWindow failed: %s", sdl.GetError())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
app.device = sdl.CreateGPUDevice({.SPIRV}, true, nil)
|
||||||
|
if app.device == nil {
|
||||||
|
fmt.eprintfln("CreateGPUDevice failed: %s", sdl.GetError())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sdl.ClaimWindowForGPUDevice(app.device, app.window) {
|
||||||
|
fmt.eprintfln("ClaimWindowForGPUDevice failed: %s", sdl.GetError())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
app.pipeline = create_sprite_pipeline(app.device, app.window)
|
||||||
|
if app.pipeline == nil {
|
||||||
|
fmt.eprintfln("create_sprite_pipeline failed: %s", sdl.GetError())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
app.sampler = sdl.CreateGPUSampler(
|
||||||
|
app.device,
|
||||||
|
{
|
||||||
|
min_filter = .NEAREST,
|
||||||
|
mag_filter = .NEAREST,
|
||||||
|
mipmap_mode = .NEAREST,
|
||||||
|
address_mode_u = .CLAMP_TO_EDGE,
|
||||||
|
address_mode_v = .CLAMP_TO_EDGE,
|
||||||
|
address_mode_w = .CLAMP_TO_EDGE,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if app.sampler == nil {
|
||||||
|
fmt.eprintfln("CreateGPUSampler failed: %s", sdl.GetError())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
app.vertex_buffer = sdl.CreateGPUBuffer(
|
||||||
|
app.device,
|
||||||
|
{usage = {.VERTEX}, size = VERTEX_BUFFER_SIZE},
|
||||||
|
)
|
||||||
|
|
||||||
|
if app.vertex_buffer == nil {
|
||||||
|
fmt.eprintfln("CreateGPUBuffer failed: %s", sdl.GetError())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
app.transfer_buffer = sdl.CreateGPUTransferBuffer(
|
||||||
|
app.device,
|
||||||
|
{usage = .UPLOAD, size = VERTEX_BUFFER_SIZE},
|
||||||
|
)
|
||||||
|
|
||||||
|
if app.transfer_buffer == nil {
|
||||||
|
fmt.eprintfln("CreateGPUTransferBuffer failed: %s", sdl.GetError())
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,14 +99,43 @@ init :: proc(app: ^App, title: cstring, width, height: i32) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shutdown :: proc(app: ^App) {
|
shutdown :: proc(app: ^App) {
|
||||||
if app.renderer != nil do sdl.DestroyRenderer(app.renderer)
|
if app.device != nil {
|
||||||
if app.window != nil do sdl.DestroyWindow(app.window)
|
ok := sdl.WaitForGPUIdle(app.device)
|
||||||
|
if !ok {
|
||||||
|
fmt.eprintfln("WaitForGPUIdle failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.transfer_buffer != nil {
|
||||||
|
sdl.ReleaseGPUTransferBuffer(app.device, app.transfer_buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.vertex_buffer != nil {
|
||||||
|
sdl.ReleaseGPUBuffer(app.device, app.vertex_buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.sampler != nil {
|
||||||
|
sdl.ReleaseGPUSampler(app.device, app.sampler)
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.pipeline != nil {
|
||||||
|
sdl.ReleaseGPUGraphicsPipeline(app.device, app.pipeline)
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.window != nil {
|
||||||
|
sdl.ReleaseWindowFromGPUDevice(app.device, app.window)
|
||||||
|
}
|
||||||
|
sdl.DestroyGPUDevice(app.device)
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.window != nil {
|
||||||
|
sdl.DestroyWindow(app.window)
|
||||||
|
}
|
||||||
|
|
||||||
sdl.Quit()
|
sdl.Quit()
|
||||||
app^ = {}
|
app^ = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pump_events :: proc() -> bool {
|
events :: proc() -> bool {
|
||||||
event: sdl.Event
|
event: sdl.Event
|
||||||
|
|
||||||
for sdl.PollEvent(&event) {
|
for sdl.PollEvent(&event) {
|
||||||
@@ -42,15 +147,137 @@ pump_events :: proc() -> bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
begin_frame :: proc(app: ^App, r: u8 = 30, g: u8 = 30, b: u8 = 40, a: u8 = 255) {
|
begin_frame :: proc(app: ^App, clear: sdl.FColor = {0.12, 0.12, 0.16, 1}) {
|
||||||
sdl.SetRenderDrawColor(app.renderer, r, g, b, a)
|
app.cmd = sdl.AcquireGPUCommandBuffer(app.device)
|
||||||
sdl.RenderClear(app.renderer)
|
|
||||||
|
if app.cmd == nil {
|
||||||
|
fmt.eprintfln("AcquireGPUCommandBuffer failed: %s", sdl.GetError())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := sdl.WaitAndAcquireGPUSwapchainTexture(
|
||||||
|
app.cmd,
|
||||||
|
app.window,
|
||||||
|
&app.swapchain_texture,
|
||||||
|
&app.swapchain_w,
|
||||||
|
&app.swapchain_h,
|
||||||
|
)
|
||||||
|
|
||||||
|
if !ok || app.swapchain_texture == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
color_info := sdl.GPUColorTargetInfo {
|
||||||
|
texture = app.swapchain_texture,
|
||||||
|
clear_color = clear,
|
||||||
|
load_op = .CLEAR,
|
||||||
|
store_op = .STORE,
|
||||||
|
}
|
||||||
|
|
||||||
|
app.render_pass = sdl.BeginGPURenderPass(app.cmd, &color_info, 1, nil)
|
||||||
|
|
||||||
|
sdl.BindGPUGraphicsPipeline(app.render_pass, app.pipeline)
|
||||||
}
|
}
|
||||||
|
|
||||||
end_frame :: proc(app: ^App) {
|
end_frame :: proc(app: ^App) {
|
||||||
sdl.RenderPresent(app.renderer)
|
if app.render_pass != nil {
|
||||||
|
sdl.EndGPURenderPass(app.render_pass)
|
||||||
|
app.render_pass = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.cmd != nil {
|
||||||
|
ok := sdl.SubmitGPUCommandBuffer(app.cmd)
|
||||||
|
if !ok {
|
||||||
|
fmt.eprintfln("SubmitGPUCommandBuffer failed")
|
||||||
|
}
|
||||||
|
app.cmd = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
app.swapchain_texture = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer :: proc(app: ^App) -> ^sdl.Renderer {
|
load_spirv_shader :: proc(
|
||||||
return app.renderer
|
device: ^sdl.GPUDevice,
|
||||||
|
code: []u8,
|
||||||
|
stage: sdl.GPUShaderStage,
|
||||||
|
num_samplers: u32,
|
||||||
|
) -> ^sdl.GPUShader {
|
||||||
|
return sdl.CreateGPUShader(
|
||||||
|
device,
|
||||||
|
{
|
||||||
|
code_size = len(code),
|
||||||
|
code = raw_data(code),
|
||||||
|
entrypoint = "main",
|
||||||
|
format = {.SPIRV},
|
||||||
|
stage = stage,
|
||||||
|
num_samplers = num_samplers,
|
||||||
|
num_storage_textures = 0,
|
||||||
|
num_storage_buffers = 0,
|
||||||
|
num_uniform_buffers = 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
create_sprite_pipeline :: proc(
|
||||||
|
device: ^sdl.GPUDevice,
|
||||||
|
window: ^sdl.Window,
|
||||||
|
) -> ^sdl.GPUGraphicsPipeline {
|
||||||
|
vert := load_spirv_shader(device, VERT_SPV[:], .VERTEX, 0)
|
||||||
|
if vert == nil do return nil
|
||||||
|
|
||||||
|
frag := load_spirv_shader(device, FRAG_SPV[:], .FRAGMENT, 1)
|
||||||
|
if frag == nil {
|
||||||
|
sdl.ReleaseGPUShader(device, vert)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
swap_format := sdl.GetGPUSwapchainTextureFormat(device, window)
|
||||||
|
|
||||||
|
|
||||||
|
blend := 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,
|
||||||
|
enable_blend = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
color_target := sdl.GPUColorTargetDescription {
|
||||||
|
format = swap_format,
|
||||||
|
blend_state = blend,
|
||||||
|
}
|
||||||
|
|
||||||
|
vb_desc := sdl.GPUVertexBufferDescription {
|
||||||
|
slot = 0,
|
||||||
|
pitch = u32(size_of(Vertex)),
|
||||||
|
input_rate = .VERTEX,
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs := [2]sdl.GPUVertexAttribute {
|
||||||
|
{location = 0, buffer_slot = 0, format = .FLOAT2, offset = 0},
|
||||||
|
{location = 1, buffer_slot = 0, format = .FLOAT2, offset = u32(offset_of(Vertex, uv))},
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline_info := sdl.GPUGraphicsPipelineCreateInfo {
|
||||||
|
vertex_shader = vert,
|
||||||
|
fragment_shader = frag,
|
||||||
|
vertex_input_state = {
|
||||||
|
vertex_buffer_descriptions = &vb_desc,
|
||||||
|
num_vertex_buffers = 1,
|
||||||
|
vertex_attributes = raw_data(attrs[:]),
|
||||||
|
num_vertex_attributes = 2,
|
||||||
|
},
|
||||||
|
primitive_type = .TRIANGLELIST,
|
||||||
|
rasterizer_state = {fill_mode = .FILL},
|
||||||
|
target_info = {color_target_descriptions = &color_target, num_color_targets = 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline := sdl.CreateGPUGraphicsPipeline(device, pipeline_info)
|
||||||
|
|
||||||
|
sdl.ReleaseGPUShader(device, vert)
|
||||||
|
sdl.ReleaseGPUShader(device, frag)
|
||||||
|
|
||||||
|
return pipeline
|
||||||
}
|
}
|
||||||
|
|||||||
+112
-13
@@ -2,6 +2,7 @@ package engine
|
|||||||
|
|
||||||
import "core:encoding/json"
|
import "core:encoding/json"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:mem"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:path/filepath"
|
import "core:path/filepath"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
@@ -30,16 +31,12 @@ Char_Def :: struct {
|
|||||||
|
|
||||||
Character_Data :: struct {
|
Character_Data :: struct {
|
||||||
def: Char_Def,
|
def: Char_Def,
|
||||||
texture: ^sdl.Texture,
|
texture: ^sdl.GPUTexture,
|
||||||
|
width: int,
|
||||||
|
height: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
load_character_data :: proc(
|
load_character_data :: proc(app: ^App, character_json_path: string) -> (Character_Data, bool) {
|
||||||
renderer: ^sdl.Renderer,
|
|
||||||
character_json_path: string,
|
|
||||||
) -> (
|
|
||||||
Character_Data,
|
|
||||||
bool,
|
|
||||||
) {
|
|
||||||
file_data, read_err := os.read_entire_file(character_json_path, context.allocator)
|
file_data, read_err := os.read_entire_file(character_json_path, context.allocator)
|
||||||
if read_err != nil {
|
if read_err != nil {
|
||||||
fmt.eprintfln("failed to read %s: %v", character_json_path, read_err)
|
fmt.eprintfln("failed to read %s: %v", character_json_path, read_err)
|
||||||
@@ -75,18 +72,120 @@ load_character_data :: proc(
|
|||||||
}
|
}
|
||||||
defer sdl.DestroySurface(surface)
|
defer sdl.DestroySurface(surface)
|
||||||
|
|
||||||
out.texture = sdl.CreateTextureFromSurface(renderer, surface)
|
rgba_surface := surface
|
||||||
|
converted_surface: ^sdl.Surface
|
||||||
|
defer {
|
||||||
|
if converted_surface != nil do sdl.DestroySurface(converted_surface)
|
||||||
|
}
|
||||||
|
if surface.format != .ABGR8888 {
|
||||||
|
converted_surface = sdl.ConvertSurface(surface, .ABGR8888)
|
||||||
|
if converted_surface == nil {
|
||||||
|
fmt.eprintfln("ConvertSurface(ABGR8888) failed: %s", sdl.GetError())
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
rgba_surface = converted_surface
|
||||||
|
}
|
||||||
|
|
||||||
|
out.width = int(rgba_surface.w)
|
||||||
|
out.height = int(rgba_surface.h)
|
||||||
|
|
||||||
|
out.texture = sdl.CreateGPUTexture(
|
||||||
|
app.device,
|
||||||
|
{
|
||||||
|
type = .D2,
|
||||||
|
format = .R8G8B8A8_UNORM,
|
||||||
|
usage = {.SAMPLER},
|
||||||
|
width = u32(out.width),
|
||||||
|
height = u32(out.height),
|
||||||
|
layer_count_or_depth = 1,
|
||||||
|
num_levels = 1,
|
||||||
|
sample_count = ._1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
if out.texture == nil {
|
if out.texture == nil {
|
||||||
fmt.eprintfln("CreateTextureFromSurface failed: %s", sdl.GetError())
|
fmt.eprintfln("CreateGPUTexture failed: %s", sdl.GetError())
|
||||||
return {}, false
|
return {}, false
|
||||||
}
|
}
|
||||||
sdl.SetTextureScaleMode(out.texture, .NEAREST)
|
|
||||||
|
upload_size := int(rgba_surface.pitch) * int(rgba_surface.h)
|
||||||
|
tbuf := sdl.CreateGPUTransferBuffer(app.device, {usage = .UPLOAD, size = u32(upload_size)})
|
||||||
|
|
||||||
|
if tbuf == nil {
|
||||||
|
fmt.eprintfln("CreateGPUTransferBuffer failed: %s", sdl.GetError())
|
||||||
|
sdl.ReleaseGPUTexture(app.device, out.texture)
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
map_ptr := sdl.MapGPUTransferBuffer(app.device, tbuf, false)
|
||||||
|
|
||||||
|
if map_ptr == nil {
|
||||||
|
fmt.eprintfln("MapGPUTransferBuffer failed: %s", sdl.GetError())
|
||||||
|
sdl.ReleaseGPUTransferBuffer(app.device, tbuf)
|
||||||
|
sdl.ReleaseGPUTexture(app.device, out.texture)
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
mem.copy(map_ptr, rgba_surface.pixels, upload_size)
|
||||||
|
sdl.UnmapGPUTransferBuffer(app.device, tbuf)
|
||||||
|
|
||||||
|
cmd := sdl.AcquireGPUCommandBuffer(app.device)
|
||||||
|
if cmd == nil {
|
||||||
|
fmt.eprintfln("AcquireGPUCommandBuffer failed: %s", sdl.GetError())
|
||||||
|
sdl.ReleaseGPUTransferBuffer(app.device, tbuf)
|
||||||
|
sdl.ReleaseGPUTexture(app.device, out.texture)
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_pass := sdl.BeginGPUCopyPass(cmd)
|
||||||
|
|
||||||
|
transfer := sdl.GPUTextureTransferInfo {
|
||||||
|
transfer_buffer = tbuf,
|
||||||
|
offset = 0,
|
||||||
|
pixels_per_row = u32(rgba_surface.pitch / 4), // RGBA8888 => 4 bytes/pixel
|
||||||
|
rows_per_layer = u32(rgba_surface.h),
|
||||||
|
}
|
||||||
|
region := sdl.GPUTextureRegion {
|
||||||
|
texture = out.texture,
|
||||||
|
mip_level = 0,
|
||||||
|
layer = 0,
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
z = 0,
|
||||||
|
w = u32(rgba_surface.w),
|
||||||
|
h = u32(rgba_surface.h),
|
||||||
|
d = 1,
|
||||||
|
}
|
||||||
|
sdl.UploadToGPUTexture(copy_pass, transfer, region, false)
|
||||||
|
sdl.EndGPUCopyPass(copy_pass)
|
||||||
|
fence := sdl.SubmitGPUCommandBufferAndAcquireFence(cmd)
|
||||||
|
if fence == nil {
|
||||||
|
fmt.eprintfln("SubmitGPUCommandBufferAndAcquireFence (texture upload) failed: %s", sdl.GetError())
|
||||||
|
sdl.ReleaseGPUTransferBuffer(app.device, tbuf)
|
||||||
|
sdl.ReleaseGPUTexture(app.device, out.texture)
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
defer sdl.ReleaseGPUFence(app.device, fence)
|
||||||
|
|
||||||
|
if !sdl.WaitForGPUFences(app.device, true, &fence, 1) {
|
||||||
|
fmt.eprintfln("WaitForGPUFences (texture upload) failed: %s", sdl.GetError())
|
||||||
|
sdl.ReleaseGPUTransferBuffer(app.device, tbuf)
|
||||||
|
sdl.ReleaseGPUTexture(app.device, out.texture)
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
sdl.ReleaseGPUTransferBuffer(app.device, tbuf)
|
||||||
|
|
||||||
return out, true
|
return out, true
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy_character_data :: proc(data: ^Character_Data) {
|
destroy_character_data :: proc(app: ^App, data: ^Character_Data) {
|
||||||
if data.texture != nil {
|
if data.texture != nil {
|
||||||
sdl.DestroyTexture(data.texture)
|
// Ensure no in-flight draw command is still sampling this texture.
|
||||||
|
if app != nil && app.device != nil {
|
||||||
|
_ = sdl.WaitForGPUIdle(app.device)
|
||||||
|
}
|
||||||
|
sdl.ReleaseGPUTexture(app.device, data.texture)
|
||||||
}
|
}
|
||||||
data^ = {}
|
data^ = {}
|
||||||
}
|
}
|
||||||
|
|||||||
+95
-15
@@ -1,5 +1,6 @@
|
|||||||
package engine
|
package engine
|
||||||
|
|
||||||
|
import "core:mem"
|
||||||
import sdl "vendor:sdl3"
|
import sdl "vendor:sdl3"
|
||||||
|
|
||||||
Vec2 :: [2]f32
|
Vec2 :: [2]f32
|
||||||
@@ -26,26 +27,105 @@ update_sprite :: proc(sprite: ^Sprite, dt: f32) {
|
|||||||
_ = dt
|
_ = dt
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_sprite :: proc(renderer: ^sdl.Renderer, sprite: ^Sprite) {
|
to_clip :: proc(px, py, sw, sh: f32) -> [2]f32 {
|
||||||
assert(sprite.data != nil)
|
return {
|
||||||
|
px / sw * 2 - 1,
|
||||||
|
1 - py / sh * 2, // flip y (window pixels are y-down)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_sprite :: proc(app: ^App, sprite: ^Sprite) {
|
||||||
|
if app.render_pass == nil || app.cmd == nil || app.swapchain_texture == nil {
|
||||||
|
return // begin_frame may have skipped (minimized window, etc.)
|
||||||
|
}
|
||||||
|
if sprite == nil || sprite.data == nil || sprite.data.texture == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
rect, ok := character_frame_rect(sprite.data, sprite.clip, sprite.frame)
|
rect, ok := character_frame_rect(sprite.data, sprite.clip, sprite.frame)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w := f32(rect[2])
|
fw := f32(rect[2])
|
||||||
h := f32(rect[3])
|
fh := f32(rect[3])
|
||||||
dst := sdl.FRect {
|
|
||||||
x = sprite.position.x - w * 0.5,
|
// Feet-centered placement, same as renderer version
|
||||||
y = sprite.position.y - h,
|
x0_px := sprite.position.x - fw * 0.5
|
||||||
w = w,
|
y0_px := sprite.position.y - fh
|
||||||
h = h,
|
x1_px := x0_px + fw
|
||||||
|
y1_px := y0_px + fh
|
||||||
|
|
||||||
|
sw := f32(app.swapchain_w)
|
||||||
|
sh := f32(app.swapchain_h)
|
||||||
|
p0 := to_clip(x0_px, y0_px, sw, sh) // top-left
|
||||||
|
p1 := to_clip(x1_px, y0_px, sw, sh) // top-right
|
||||||
|
p2 := to_clip(x1_px, y1_px, sw, sh) // bottom-right
|
||||||
|
p3 := to_clip(x0_px, y1_px, sw, sh) // bottom-left
|
||||||
|
|
||||||
|
tex_w := f32(sprite.data.width)
|
||||||
|
tex_h := f32(sprite.data.height)
|
||||||
|
u0 := f32(rect[0]) / tex_w
|
||||||
|
v0 := f32(rect[1]) / tex_h
|
||||||
|
u1 := f32(rect[0] + rect[2]) / tex_w
|
||||||
|
v1 := f32(rect[1] + rect[3]) / tex_h
|
||||||
|
|
||||||
|
// Two triangles: (0,1,2) and (0,2,3)
|
||||||
|
verts := [6]Vertex {
|
||||||
|
{pos = p0, uv = {u0, v0}},
|
||||||
|
{pos = p1, uv = {u1, v0}},
|
||||||
|
{pos = p2, uv = {u1, v1}},
|
||||||
|
{pos = p0, uv = {u0, v0}},
|
||||||
|
{pos = p2, uv = {u1, v1}},
|
||||||
|
{pos = p3, uv = {u0, v1}},
|
||||||
}
|
}
|
||||||
src := sdl.FRect {
|
|
||||||
x = f32(rect[0]),
|
map_ptr := sdl.MapGPUTransferBuffer(app.device, app.transfer_buffer, false)
|
||||||
y = f32(rect[1]),
|
if map_ptr == nil {
|
||||||
w = w,
|
return
|
||||||
h = h,
|
|
||||||
}
|
}
|
||||||
sdl.RenderTexture(renderer, sprite.data.texture, &src, &dst)
|
mem.copy(map_ptr, raw_data(verts[:]), size_of(verts))
|
||||||
|
sdl.UnmapGPUTransferBuffer(app.device, app.transfer_buffer)
|
||||||
|
|
||||||
|
// IMPORTANT: SDL does not allow beginning a copy pass while a render pass
|
||||||
|
// is active on the same command buffer. Upload on a separate command buffer.
|
||||||
|
copy_cmd := sdl.AcquireGPUCommandBuffer(app.device)
|
||||||
|
if copy_cmd == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
copy_pass := sdl.BeginGPUCopyPass(copy_cmd)
|
||||||
|
|
||||||
|
src := sdl.GPUTransferBufferLocation {
|
||||||
|
transfer_buffer = app.transfer_buffer,
|
||||||
|
offset = 0,
|
||||||
|
}
|
||||||
|
dst := sdl.GPUBufferRegion {
|
||||||
|
buffer = app.vertex_buffer,
|
||||||
|
offset = 0,
|
||||||
|
size = u32(size_of(verts)),
|
||||||
|
}
|
||||||
|
sdl.UploadToGPUBuffer(copy_pass, src, dst, false)
|
||||||
|
sdl.EndGPUCopyPass(copy_pass)
|
||||||
|
fence := sdl.SubmitGPUCommandBufferAndAcquireFence(copy_cmd)
|
||||||
|
if fence == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer sdl.ReleaseGPUFence(app.device, fence)
|
||||||
|
if !sdl.WaitForGPUFences(app.device, true, &fence, 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sampler_binding := sdl.GPUTextureSamplerBinding {
|
||||||
|
texture = sprite.data.texture,
|
||||||
|
sampler = app.sampler,
|
||||||
|
}
|
||||||
|
sdl.BindGPUFragmentSamplers(app.render_pass, 0, &sampler_binding, 1)
|
||||||
|
|
||||||
|
vb_binding := sdl.GPUBufferBinding {
|
||||||
|
buffer = app.vertex_buffer,
|
||||||
|
offset = 0,
|
||||||
|
}
|
||||||
|
sdl.BindGPUVertexBuffers(app.render_pass, 0, &vb_binding, 1)
|
||||||
|
|
||||||
|
sdl.DrawGPUPrimitives(app.render_pass, 6, 1, 0, 0)
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-13
@@ -4,25 +4,19 @@ import eng "pkg:engine"
|
|||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
app: eng.App
|
app: eng.App
|
||||||
if !eng.init(&app, "toad example", 800, 600) {
|
if !eng.init(&app, "toad", 800, 600) do return
|
||||||
return
|
|
||||||
}
|
|
||||||
defer eng.shutdown(&app)
|
defer eng.shutdown(&app)
|
||||||
|
|
||||||
data, ok := eng.load_character_data(
|
data, ok := eng.load_character_data(&app, "assets_baked/characters/toad/toad.char.json")
|
||||||
app.renderer,
|
if !ok do return
|
||||||
"assets_baked/characters/toad/toad.char.json",
|
|
||||||
)
|
defer eng.destroy_character_data(&app, &data)
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer eng.destroy_character_data(&data)
|
|
||||||
|
|
||||||
toad := eng.spawn_sprite(&data, {400, 500}, "idle", 0)
|
toad := eng.spawn_sprite(&data, {400, 500}, "idle", 0)
|
||||||
|
|
||||||
for eng.pump_events() {
|
for eng.events() {
|
||||||
eng.begin_frame(&app)
|
eng.begin_frame(&app)
|
||||||
eng.draw_sprite(app.renderer, &toad)
|
eng.draw_sprite(&app, &toad)
|
||||||
eng.end_frame(&app)
|
eng.end_frame(&app)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(set = 2, binding = 0) uniform sampler2D u_tex; // SDL_GPU fragment sampler slot 0
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 v_uv;
|
||||||
|
layout(location = 0) out vec4 out_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
out_color = texture(u_tex, v_uv);
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,11 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 in_pos;
|
||||||
|
layout(location = 1) in vec2 in_uv;
|
||||||
|
|
||||||
|
layout(location = 0) out vec2 v_uv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
v_uv = in_uv;
|
||||||
|
gl_Position = vec4(in_pos, 0.0, 1.0);
|
||||||
|
}
|
||||||
Binary file not shown.
Reference in New Issue
Block a user