Added draw package as renderer focused on mixed use layout / 2D / 3D scene applications (#7)
Co-authored-by: Zachary Levy <zachary@sunforge.is> Reviewed-on: #7
This commit was merged in pull request #7.
This commit is contained in:
141
meta/gen_shaders.odin
Normal file
141
meta/gen_shaders.odin
Normal file
@@ -0,0 +1,141 @@
|
||||
package meta
|
||||
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
|
||||
// Compiles all GLSL shaders in source_dir to both SPIR-V (.spv) and
|
||||
// Metal Shading Language (.metal), writing results to generated_dir.
|
||||
// Overwrites any previously generated files with matching names.
|
||||
// Requires `glslangValidator` and `spirv-cross` on PATH.
|
||||
gen_shaders :: proc(source_dir, generated_dir: string) -> (success: bool) {
|
||||
if !verify_shader_tool("glslangValidator") do return false
|
||||
if !verify_shader_tool("spirv-cross") do return false
|
||||
|
||||
source_entries, read_err := os.read_all_directory_by_path(source_dir, context.temp_allocator)
|
||||
if read_err != nil {
|
||||
fmt.eprintfln("Failed to read shader source directory '%s': %v", source_dir, read_err)
|
||||
return false
|
||||
}
|
||||
shader_names := make([dynamic]string, len = 0, cap = 24, allocator = context.temp_allocator)
|
||||
|
||||
for entry in source_entries {
|
||||
if strings.has_suffix(entry.name, ".vert") || strings.has_suffix(entry.name, ".frag") {
|
||||
append(&shader_names, entry.name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(shader_names) == 0 {
|
||||
fmt.eprintfln("No shader source files (.vert, .frag) found in '%s'.", source_dir)
|
||||
return false
|
||||
}
|
||||
if os.exists(generated_dir) {
|
||||
rmdir_err := os.remove_all(generated_dir)
|
||||
if rmdir_err != nil {
|
||||
fmt.eprintfln("Failed to remove old output directory '%s': %v", generated_dir, rmdir_err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
mkdir_err := os.mkdir(generated_dir)
|
||||
if mkdir_err != nil {
|
||||
fmt.eprintfln("Failed to create output directory '%s': %v", generated_dir, mkdir_err)
|
||||
return false
|
||||
}
|
||||
|
||||
compiled_count := 0
|
||||
for shader_name in shader_names {
|
||||
source_path := fmt.tprintf("%s/%s", source_dir, shader_name)
|
||||
spv_path := fmt.tprintf("%s/%s.spv", generated_dir, shader_name)
|
||||
metal_path := fmt.tprintf("%s/%s.metal", generated_dir, shader_name)
|
||||
|
||||
fmt.printfln("[GLSL -> SPIR-V] %s", shader_name)
|
||||
if !compile_glsl_to_spirv(source_path, spv_path) do continue
|
||||
|
||||
fmt.printfln("[SPIR-V -> MSL] %s", shader_name)
|
||||
if !compile_spirv_to_msl(spv_path, metal_path) do continue
|
||||
|
||||
compiled_count += 1
|
||||
}
|
||||
|
||||
total := len(shader_names)
|
||||
if compiled_count == total {
|
||||
fmt.printfln("Successfully compiled all %d shaders.", total)
|
||||
return true
|
||||
}
|
||||
|
||||
fmt.eprintfln("%d of %d shaders failed to compile.", total - compiled_count, total)
|
||||
return false
|
||||
}
|
||||
|
||||
verify_shader_tool :: proc(tool_name: string) -> bool {
|
||||
_, _, _, err := os.process_exec(
|
||||
os.Process_Desc{command = []string{tool_name, "--version"}},
|
||||
context.temp_allocator,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
fmt.eprintfln("Required tool '%s' not found on PATH.", tool_name)
|
||||
if tool_name == "glslangValidator" {
|
||||
fmt.eprintln("\tInstall the Vulkan SDK or the glslang package:")
|
||||
fmt.eprintln("\t macOS: brew install glslang")
|
||||
fmt.eprintln("\t Arch: sudo pacman -S glslang")
|
||||
fmt.eprintln("\t Debian: sudo apt install glslang-tools")
|
||||
} else if tool_name == "spirv-cross" {
|
||||
fmt.eprintln("\tInstall SPIRV-Cross:")
|
||||
fmt.eprintln("\t macOS: brew install spirv-cross")
|
||||
fmt.eprintln("\t Arch: sudo pacman -S spirv-cross")
|
||||
fmt.eprintln("\t Debian: sudo apt install spirv-cross")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
compile_glsl_to_spirv :: proc(source_path, output_path: string) -> bool {
|
||||
state, stdout_bytes, stderr_bytes, err := os.process_exec(
|
||||
os.Process_Desc{command = []string{"glslangValidator", "-V", source_path, "-o", output_path}},
|
||||
context.temp_allocator,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
fmt.eprintfln("\tFailed to run glslangValidator for '%s': %v", source_path, err)
|
||||
return false
|
||||
}
|
||||
|
||||
if !state.success {
|
||||
fmt.eprintfln("\tglslangValidator failed for '%s' (exit code %d):", source_path, state.exit_code)
|
||||
print_tool_output(stdout_bytes, stderr_bytes)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
compile_spirv_to_msl :: proc(spv_path, output_path: string) -> bool {
|
||||
state, stdout_bytes, stderr_bytes, err := os.process_exec(
|
||||
os.Process_Desc{command = []string{"spirv-cross", "--msl", spv_path, "--output", output_path}},
|
||||
context.temp_allocator,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
fmt.eprintfln("\tFailed to run spirv-cross for '%s': %v", spv_path, err)
|
||||
return false
|
||||
}
|
||||
|
||||
if !state.success {
|
||||
fmt.eprintfln("\tspirv-cross failed for '%s' (exit code %d):", spv_path, state.exit_code)
|
||||
print_tool_output(stdout_bytes, stderr_bytes)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
print_tool_output :: proc(stdout_bytes, stderr_bytes: []u8) {
|
||||
stderr_text := strings.trim_right_space(transmute(string)stderr_bytes)
|
||||
stdout_text := strings.trim_right_space(transmute(string)stdout_bytes)
|
||||
|
||||
if len(stderr_text) > 0 do fmt.eprintfln("\t%s", stderr_text)
|
||||
if len(stdout_text) > 0 do fmt.eprintfln("\t%s", stdout_text)
|
||||
}
|
||||
51
meta/main.odin
Normal file
51
meta/main.odin
Normal file
@@ -0,0 +1,51 @@
|
||||
package meta
|
||||
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
|
||||
Command :: struct {
|
||||
name: string,
|
||||
description: string,
|
||||
run: proc() -> bool,
|
||||
}
|
||||
|
||||
COMMANDS :: []Command {
|
||||
{
|
||||
name = "gen-shaders",
|
||||
description = "Compile GLSL shaders to SPIR-V and Metal Shading Language.",
|
||||
run = proc() -> bool {
|
||||
return gen_shaders("draw/shaders/source", "draw/shaders/generated")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
args := os.args[1:]
|
||||
|
||||
if len(args) == 0 {
|
||||
print_usage()
|
||||
return
|
||||
}
|
||||
|
||||
command_name := args[0]
|
||||
for command in COMMANDS {
|
||||
if command.name == command_name {
|
||||
if !command.run() do os.exit(1)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fmt.eprintfln("Unknown command '%s'.", command_name)
|
||||
fmt.eprintln()
|
||||
print_usage()
|
||||
os.exit(1)
|
||||
}
|
||||
|
||||
print_usage :: proc() {
|
||||
fmt.eprintln("Usage: meta <command>")
|
||||
fmt.eprintln()
|
||||
fmt.eprintln("Commands:")
|
||||
for command in COMMANDS {
|
||||
fmt.eprintfln(" %-20s %s", command.name, command.description)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user