# gpu Shader Integration Guide Luma is now gpu's built-in shader language/compiler. Most gpu code should use the easy `shaderkit` facade; compiler tools can import `gpu/shader` directly. ## Basic Compilation ```odin import shaderkit "path/to/gpu/shaderkit" handle, ok := shaderkit.create_shader_from_source( "material.frag", .Fragment, source, {opt_level = .Basic}, ) ``` The facade chooses the backend target from `gpu.REQUIRED_SHADER_FORMAT`, applies the D3D/HLSL defaults, logs diagnostics, creates the backend shader module, and destroys compiler output. For compiler tools that need bytecode and reflection: ```odin import shader "path/to/gpu/shader" result := shader.compile(source, {target = .SPIR_V, stage = .Vertex}, "shader.luma") defer shader.destroy_compile_result(&result) bytecode := result.output reflection := result.reflection ``` `Compile_Result` owns `output`, `diagnostics`, and `reflection`. They remain valid across later compiles and must be released with `destroy_compile_result`. ## Pipeline Stages Each stage is exposed independently for custom tooling: ```odin // Parse only (AST inspection, syntax highlighting) ast, diags := shader.compile_parse(source, "shader.luma") // Parse + desugar + typecheck (IDE diagnostics) ast, sema, diags := shader.compile_check(source, "shader.luma") // Full lowering to IR (custom optimization passes) ir_module, diags := shader.compile_lower(source, "shader.luma") // Dump AST (parse + desugar only) ast, diags := shader.compile_dump_ast(source, "shader.luma") ``` ## Reflection API ### Standard Reflection Extract binding layouts, I/O variables, and struct metadata: ```odin result := shader.compile(source, {target = .SPIR_V, stage = .Fragment}, "shader.luma") defer shader.destroy_compile_result(&result) info := result.reflection json := shader.reflect_to_json(info) // Or access structured data directly: for entry in info.entry_points { log.infof("Entry: %s (%v)", entry.name, entry.stage) for io in entry.inputs { log.infof(" in: %s @ location %d", io.name, io.location) } } for b in info.bindings { log.infof("Binding: %s group=%d binding=%d", b.name, b.group, b.binding) } ``` ### SPIR-V Reflection For Vulkan pipeline creation with descriptor set layouts: ```odin info, diags := shader.compile_reflect_spirv(source, "shader.luma") json := shader.reflect_spirv_to_json(info) // Descriptor set layout creation for ds in info.descriptor_sets { // ds.set is the descriptor set number for binding in ds.bindings { // binding.descriptor_type: "UNIFORM_BUFFER", "STORAGE_BUFFER", // "SAMPLED_IMAGE", "SAMPLER" // binding.binding: binding number // binding.stage_flags: ["VERTEX", "FRAGMENT", etc.] // binding.block_size: buffer size in bytes // binding.members: field-level layout with offsets } } ``` ## Compile Options ```odin shader.Compile_Options :: struct { target: shader.Target, // GLSL_450, GLSL_450_OPENGL, HLSL_SM6, MSL_2_4, WGSL, SPIR_V opt_level: shader.Opt_Level, // None, Basic, Aggressive stage: shader.Shader_Stage, entry: string, debug: bool, // Emit debug info (SPIR-V OpLine/OpSource) validate: bool, include_dirs: []string, // Include search paths file_reader: shader.File_Reader, // Custom file reader for virtual filesystem } ``` ## Custom File Reader For virtual filesystems or asset pipelines, provide a custom file reader: ```odin my_reader :: proc(path: string) -> (content: string, ok: bool) { // Read from your asset system data, read_ok := asset_system.read(path) return data, read_ok } result := shader.compile(source, shader.Compile_Options{ target = .SPIR_V, file_reader = my_reader, include_dirs = []string{"shaders/include"}, }) defer shader.destroy_compile_result(&result) ``` ## Error Handling Diagnostics include source location, severity, and message: ```odin for d in result.diagnostics { switch d.level { case .Error: msg := shader.format_diagnostic(d) log.errorf("%s", msg) delete(msg) case .Warning: msg := shader.format_diagnostic(d) log.warnf("%s", msg) delete(msg) case .Note: msg := shader.format_diagnostic(d) log.infof("%s", msg) delete(msg) } } // Rich formatting with source context (for CLI tools) for d in result.diagnostics { msg := shader.format_diagnostic_with_source(d, source) fmt.eprintln(msg) delete(msg) } ```