Harbor

branch main
showing the latest snapshot on main
shaderkit.odin 3.3 KB · Plain text
gpu/shaderkit/shaderkit.odin 0644 Raw
package shaderkit

import "core:log"
import "core:os"
import gpu ".."
import shader "../shader"

Options :: struct {
	entry:        string,
	opt_level:    shader.Opt_Level,
	debug:        bool,
	include_dirs: []string,
	file_reader:  shader.File_Reader,
}

compile_target :: proc() -> shader.Target {
	when gpu.REQUIRED_SHADER_FORMAT == .SPIRV {
		return .SPIR_V
	} else when gpu.REQUIRED_SHADER_FORMAT == .HLSL {
		return .HLSL_SM6
	} else when gpu.REQUIRED_SHADER_FORMAT == .GLSL {
		return .GLSL_450_OPENGL
	} else {
		#assert(false, "unsupported REQUIRED_SHADER_FORMAT")
	}
}

compile_options :: proc(stage: gpu.Shader_Stage, options: Options) -> shader.Compile_Options {
	return {
		target = compile_target(),
		opt_level = options.opt_level,
		stage = shader_stage(stage),
		entry = options.entry,
		debug = options.debug,
		include_dirs = options.include_dirs,
		file_reader = options.file_reader,
		hlsl_cbuffer_push_constants = true,
		hlsl_push_constant_slot = 13,
		hlsl_omit_register_spaces = true,
	}
}

compile_shader_source :: proc(name: string, stage: gpu.Shader_Stage, source: string, options: Options = {}, allocator := context.allocator) -> shader.Compile_Result {
	return shader.compile(source, compile_options(stage, options), name, allocator)
}

create_shader_from_source :: proc(name: string, stage: gpu.Shader_Stage, source: string, options: Options = {}) -> (gpu.Shader_Handle, bool) {
	result := compile_shader_source(name, stage, source, options)
	defer shader.destroy_compile_result(&result)

	if !result.success {
		log_compile_diagnostics(name, source, result.diagnostics)
		return {}, false
	}

	entry := options.entry
	if entry == "" do entry = "main"

	handle, ok := gpu.create_shader_module({
		stage  = stage,
		format = gpu.REQUIRED_SHADER_FORMAT,
		name   = name,
		entry  = entry,
		data   = result.output,
	})
	if !ok {
		log.errorf("gpu/shaderkit: failed to create shader module %s", name)
		return {}, false
	}

	return handle, true
}

create_shader_from_file :: proc(path: string, stage: gpu.Shader_Stage, options: Options = {}) -> (gpu.Shader_Handle, bool) {
	source, err := os.read_entire_file(path, context.allocator)
	if err != nil {
		log.errorf("gpu/shaderkit: failed to read shader source %s", path)
		return {}, false
	}
	defer delete(source)

	return create_shader_from_source(path, stage, string(source), options)
}

load_compute_shader_from_source :: proc(name: string, source: string, num_buffers: u32 = 4, push_constant_size: u32 = 0, options: Options = {}) -> gpu.Compute_Shader {
	result := compile_shader_source(name, .Compute, source, options)
	defer shader.destroy_compile_result(&result)

	if !result.success {
		log_compile_diagnostics(name, source, result.diagnostics)
		return {}
	}

	return gpu.load_compute_shader_from_bytes(
		name,
		result.output,
		gpu.REQUIRED_SHADER_FORMAT,
		num_buffers,
		push_constant_size,
	)
}

log_compile_diagnostics :: proc(name, source: string, diagnostics: []shader.Diagnostic) {
	for d in diagnostics {
		msg := shader.format_diagnostic_with_source(d, source)
		log.errorf("gpu/shaderkit: %s: %s", name, msg)
		delete(msg)
	}
}

shader_stage :: proc(stage: gpu.Shader_Stage) -> shader.Shader_Stage {
	switch stage {
	case .Vertex:   return .Vertex
	case .Fragment: return .Fragment
	case .Compute:  return .Compute
	}
	return .None
}