add shader backend + support for multiple shader formats
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
.PHONY: shaders-vulkan shaders-d3d12 shaders-metal shaders-all toad check help
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Targets:"
|
||||||
|
@echo " shaders-vulkan Compile SPIR-V into shaders/vulkan/"
|
||||||
|
@echo " shaders-d3d12 Compile DXIL into shaders/d3d12/ (needs shadercross)"
|
||||||
|
@echo " shaders-metal Compile MSL into shaders/metal/ (needs shadercross)"
|
||||||
|
@echo " shaders-all Build all shader backends"
|
||||||
|
@echo " check Typecheck examples/toad"
|
||||||
|
@echo " toad Run the toad example"
|
||||||
|
|
||||||
|
shaders-vulkan:
|
||||||
|
./scripts/shaders_vulkan.sh
|
||||||
|
|
||||||
|
shaders-d3d12:
|
||||||
|
./scripts/shaders_d3d12.sh
|
||||||
|
|
||||||
|
shaders-metal:
|
||||||
|
./scripts/shaders_metal.sh
|
||||||
|
|
||||||
|
shaders-all: shaders-vulkan shaders-d3d12 shaders-metal
|
||||||
|
|
||||||
|
check:
|
||||||
|
odin check examples/toad -collection:pkg=.
|
||||||
|
|
||||||
|
toad:
|
||||||
|
odin run examples/toad -collection:pkg=.
|
||||||
+79
-23
@@ -1,6 +1,7 @@
|
|||||||
package engine
|
package engine
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:path/filepath"
|
||||||
import sdl "vendor:sdl3"
|
import sdl "vendor:sdl3"
|
||||||
|
|
||||||
Vertex :: struct {
|
Vertex :: struct {
|
||||||
@@ -11,12 +12,10 @@ Vertex :: struct {
|
|||||||
SPRITE_VERT_COUNT :: 6
|
SPRITE_VERT_COUNT :: 6
|
||||||
VERTEX_BUFFER_SIZE :: SPRITE_VERT_COUNT * size_of(Vertex)
|
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,
|
||||||
device: ^sdl.GPUDevice,
|
device: ^sdl.GPUDevice,
|
||||||
|
shader: Shader_Runtime,
|
||||||
pipeline: ^sdl.GPUGraphicsPipeline,
|
pipeline: ^sdl.GPUGraphicsPipeline,
|
||||||
sampler: ^sdl.GPUSampler,
|
sampler: ^sdl.GPUSampler,
|
||||||
cmd: ^sdl.GPUCommandBuffer,
|
cmd: ^sdl.GPUCommandBuffer,
|
||||||
@@ -28,6 +27,19 @@ App :: struct {
|
|||||||
transfer_buffer: ^sdl.GPUTransferBuffer,
|
transfer_buffer: ^sdl.GPUTransferBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shader_Backend :: enum {
|
||||||
|
Vulkan_SPIRV,
|
||||||
|
DSD12_DXIL,
|
||||||
|
Metal_MSL,
|
||||||
|
}
|
||||||
|
|
||||||
|
Shader_Runtime :: struct {
|
||||||
|
backend: Shader_Backend,
|
||||||
|
format: sdl.GPUShaderFormat,
|
||||||
|
shader_dir: string,
|
||||||
|
entrypoint: cstring,
|
||||||
|
}
|
||||||
|
|
||||||
init :: proc(app: ^App, title: cstring, width, height: i32) -> bool {
|
init :: proc(app: ^App, title: cstring, width, height: i32) -> bool {
|
||||||
if !sdl.Init({.VIDEO}) {
|
if !sdl.Init({.VIDEO}) {
|
||||||
fmt.eprintfln("SDL_Init failed: %s", sdl.GetError())
|
fmt.eprintfln("SDL_Init failed: %s", sdl.GetError())
|
||||||
@@ -41,7 +53,8 @@ init :: proc(app: ^App, title: cstring, width, height: i32) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
app.device = sdl.CreateGPUDevice({.SPIRV}, true, nil)
|
requested: sdl.GPUShaderFormat = {.SPIRV, .DXIL, .MSL}
|
||||||
|
app.device = sdl.CreateGPUDevice(requested, true, nil)
|
||||||
if app.device == nil {
|
if app.device == nil {
|
||||||
fmt.eprintfln("CreateGPUDevice failed: %s", sdl.GetError())
|
fmt.eprintfln("CreateGPUDevice failed: %s", sdl.GetError())
|
||||||
return false
|
return false
|
||||||
@@ -52,7 +65,11 @@ init :: proc(app: ^App, title: cstring, width, height: i32) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
app.pipeline = create_sprite_pipeline(app.device, app.window)
|
ok: bool
|
||||||
|
app.shader, ok = choose_shader_runtime(app.device)
|
||||||
|
if !ok do return false
|
||||||
|
|
||||||
|
app.pipeline = create_sprite_pipeline(app)
|
||||||
if app.pipeline == nil {
|
if app.pipeline == nil {
|
||||||
fmt.eprintfln("create_sprite_pipeline failed: %s", sdl.GetError())
|
fmt.eprintfln("create_sprite_pipeline failed: %s", sdl.GetError())
|
||||||
return false
|
return false
|
||||||
@@ -196,10 +213,12 @@ end_frame :: proc(app: ^App) {
|
|||||||
app.swapchain_texture = nil
|
app.swapchain_texture = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
load_spirv_shader :: proc(
|
load_gpu_shader :: proc(
|
||||||
device: ^sdl.GPUDevice,
|
device: ^sdl.GPUDevice,
|
||||||
code: []u8,
|
code: []u8,
|
||||||
stage: sdl.GPUShaderStage,
|
stage: sdl.GPUShaderStage,
|
||||||
|
format: sdl.GPUShaderFormat,
|
||||||
|
entrypoint: cstring,
|
||||||
num_samplers: u32,
|
num_samplers: u32,
|
||||||
) -> ^sdl.GPUShader {
|
) -> ^sdl.GPUShader {
|
||||||
return sdl.CreateGPUShader(
|
return sdl.CreateGPUShader(
|
||||||
@@ -207,8 +226,8 @@ load_spirv_shader :: proc(
|
|||||||
{
|
{
|
||||||
code_size = len(code),
|
code_size = len(code),
|
||||||
code = raw_data(code),
|
code = raw_data(code),
|
||||||
entrypoint = "main",
|
entrypoint = entrypoint,
|
||||||
format = {.SPIRV},
|
format = format,
|
||||||
stage = stage,
|
stage = stage,
|
||||||
num_samplers = num_samplers,
|
num_samplers = num_samplers,
|
||||||
num_storage_textures = 0,
|
num_storage_textures = 0,
|
||||||
@@ -218,20 +237,57 @@ load_spirv_shader :: proc(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
create_sprite_pipeline :: proc(
|
shader_filenames :: proc(backend: Shader_Backend) -> (vert_name, frag_name: string) {
|
||||||
device: ^sdl.GPUDevice,
|
switch backend {
|
||||||
window: ^sdl.Window,
|
case .Vulkan_SPIRV:
|
||||||
) -> ^sdl.GPUGraphicsPipeline {
|
return "sprite.vert.spv", "sprite.frag.spv"
|
||||||
vert := load_spirv_shader(device, VERT_SPV[:], .VERTEX, 0)
|
case .DSD12_DXIL:
|
||||||
if vert == nil do return nil
|
return "sprite.vert.dxil", "sprite.frag.dxil"
|
||||||
|
case .Metal_MSL:
|
||||||
frag := load_spirv_shader(device, FRAG_SPV[:], .FRAGMENT, 1)
|
return "sprite.vert.msl", "sprite.frag.msl"
|
||||||
if frag == nil {
|
}
|
||||||
sdl.ReleaseGPUShader(device, vert)
|
return "", ""
|
||||||
return nil
|
}
|
||||||
|
create_sprite_pipeline :: proc(app: ^App) -> ^sdl.GPUGraphicsPipeline {
|
||||||
|
vert_name, frag_name := shader_filenames(app.shader.backend)
|
||||||
|
vert_path, _ := filepath.join({app.shader.shader_dir, vert_name})
|
||||||
|
frag_path, _ := filepath.join({app.shader.shader_dir, frag_name})
|
||||||
|
defer {
|
||||||
|
delete(vert_path)
|
||||||
|
delete(frag_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
swap_format := sdl.GetGPUSwapchainTextureFormat(device, window)
|
vert_code, vok := load_shader_blob(vert_path)
|
||||||
|
if !vok do return nil
|
||||||
|
defer delete(vert_code)
|
||||||
|
|
||||||
|
frag_code, fok := load_shader_blob(frag_path)
|
||||||
|
if !fok do return nil
|
||||||
|
defer delete(frag_code)
|
||||||
|
|
||||||
|
vert := load_gpu_shader(
|
||||||
|
app.device,
|
||||||
|
vert_code,
|
||||||
|
.VERTEX,
|
||||||
|
app.shader.format,
|
||||||
|
app.shader.entrypoint,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if vert == nil do return nil
|
||||||
|
|
||||||
|
frag := load_gpu_shader(
|
||||||
|
app.device,
|
||||||
|
frag_code,
|
||||||
|
.FRAGMENT,
|
||||||
|
app.shader.format,
|
||||||
|
app.shader.entrypoint,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
if frag == nil {
|
||||||
|
sdl.ReleaseGPUShader(app.device, vert)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
swap_format := sdl.GetGPUSwapchainTextureFormat(app.device, app.window)
|
||||||
|
|
||||||
|
|
||||||
blend := sdl.GPUColorTargetBlendState {
|
blend := sdl.GPUColorTargetBlendState {
|
||||||
@@ -274,10 +330,10 @@ create_sprite_pipeline :: proc(
|
|||||||
target_info = {color_target_descriptions = &color_target, num_color_targets = 1},
|
target_info = {color_target_descriptions = &color_target, num_color_targets = 1},
|
||||||
}
|
}
|
||||||
|
|
||||||
pipeline := sdl.CreateGPUGraphicsPipeline(device, pipeline_info)
|
pipeline := sdl.CreateGPUGraphicsPipeline(app.device, pipeline_info)
|
||||||
|
|
||||||
sdl.ReleaseGPUShader(device, vert)
|
sdl.ReleaseGPUShader(app.device, vert)
|
||||||
sdl.ReleaseGPUShader(device, frag)
|
sdl.ReleaseGPUShader(app.device, frag)
|
||||||
|
|
||||||
return pipeline
|
return pipeline
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:os"
|
||||||
|
import sdl "vendor:sdl3"
|
||||||
|
|
||||||
|
choose_shader_runtime :: proc(device: ^sdl.GPUDevice) -> (Shader_Runtime, bool) {
|
||||||
|
supported := sdl.GetGPUShaderFormats(device)
|
||||||
|
|
||||||
|
if .MSL in supported {
|
||||||
|
return {
|
||||||
|
backend = .Metal_MSL,
|
||||||
|
format = {.MSL},
|
||||||
|
shader_dir = "shaders/metal",
|
||||||
|
entrypoint = "main",
|
||||||
|
},
|
||||||
|
true
|
||||||
|
|
||||||
|
}
|
||||||
|
if .DXIL in supported {
|
||||||
|
return {
|
||||||
|
backend = .DSD12_DXIL,
|
||||||
|
format = {.DXIL},
|
||||||
|
shader_dir = "shaders/d3d12",
|
||||||
|
entrypoint = "main",
|
||||||
|
},
|
||||||
|
true
|
||||||
|
|
||||||
|
}
|
||||||
|
if .SPIRV in supported {
|
||||||
|
return {
|
||||||
|
backend = .Vulkan_SPIRV,
|
||||||
|
format = {.SPIRV},
|
||||||
|
shader_dir = "shaders/vulkan",
|
||||||
|
entrypoint = "main",
|
||||||
|
},
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.eprintfln("No supported shader format from device {got %v}", supported)
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
load_shader_blob :: proc(path: string) -> ([]u8, bool) {
|
||||||
|
data, err := os.read_entire_file(path, context.allocator)
|
||||||
|
if err != nil {
|
||||||
|
fmt.eprintfln("failed to read shader %s: %v", path, err)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, true
|
||||||
|
}
|
||||||
Executable
+9
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
cd "$ROOT"
|
||||||
|
|
||||||
|
./scripts/shaders_vulkan.sh
|
||||||
|
./scripts/shaders_d3d12.sh
|
||||||
|
./scripts/shaders_metal.sh
|
||||||
Executable
+11
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if ! command -v shadercross >/dev/null 2>&1; then
|
||||||
|
echo "shadercross not found (SDL_shadercross). Install it to build D3D12 DXIL shaders." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p shaders/d3d12
|
||||||
|
shadercross shaders/sprite.vert.glsl -o shaders/d3d12/sprite.vert.dxil -s vertex -e main
|
||||||
|
shadercross shaders/sprite.frag.glsl -o shaders/d3d12/sprite.frag.dxil -s fragment -e main
|
||||||
Executable
+11
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if ! command -v shadercross >/dev/null 2>&1; then
|
||||||
|
echo "shadercross not found (SDL_shadercross). Install it to build Metal MSL shaders." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p shaders/metal
|
||||||
|
shadercross shaders/sprite.vert.glsl -o shaders/metal/sprite.vert.msl -s vertex -e main
|
||||||
|
shadercross shaders/sprite.frag.glsl -o shaders/metal/sprite.frag.msl -s fragment -e main
|
||||||
Executable
+6
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
mkdir -p shaders/vulkan
|
||||||
|
glslangValidator -V shaders/sprite.vert.glsl -o shaders/vulkan/sprite.vert.spv
|
||||||
|
glslangValidator -V shaders/sprite.frag.glsl -o shaders/vulkan/sprite.frag.spv
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user