mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-30 09:54:45 +00:00
412 lines
13 KiB
Odin
412 lines
13 KiB
Odin
package vendor_gl
|
|
|
|
// Helper for loading shaders into a program
|
|
|
|
import "core:os"
|
|
import "core:fmt"
|
|
import "core:strings"
|
|
import "core:runtime"
|
|
_ :: fmt
|
|
_ :: runtime
|
|
|
|
Shader_Type :: enum i32 {
|
|
NONE = 0x0000,
|
|
FRAGMENT_SHADER = 0x8B30,
|
|
VERTEX_SHADER = 0x8B31,
|
|
GEOMETRY_SHADER = 0x8DD9,
|
|
COMPUTE_SHADER = 0x91B9,
|
|
TESS_EVALUATION_SHADER = 0x8E87,
|
|
TESS_CONTROL_SHADER = 0x8E88,
|
|
SHADER_LINK = -1, // @Note: Not an OpenGL constant, but used for error checking.
|
|
}
|
|
|
|
|
|
@(private, thread_local)
|
|
last_compile_error_message: []byte
|
|
@(private, thread_local)
|
|
last_link_error_message: []byte
|
|
|
|
@(private, thread_local)
|
|
last_compile_error_type: Shader_Type
|
|
@(private, thread_local)
|
|
last_link_error_type: Shader_Type
|
|
|
|
get_last_error_messages :: proc() -> (compile_message: string, compile_type: Shader_Type, link_message: string, link_type: Shader_Type) {
|
|
compile_message = string(last_compile_error_message[:max(0, len(last_compile_error_message)-1)])
|
|
compile_type = last_compile_error_type
|
|
link_message = string(last_link_error_message[:max(0, len(last_link_error_message)-1)])
|
|
link_type = last_link_error_type
|
|
return
|
|
}
|
|
|
|
get_last_error_message :: proc() -> (compile_message: string, compile_type: Shader_Type) {
|
|
compile_message = string(last_compile_error_message[:max(0, len(last_compile_error_message)-1)])
|
|
compile_type = last_compile_error_type
|
|
return
|
|
}
|
|
|
|
// Shader checking and linking checking are identical
|
|
// except for calling differently named GL functions
|
|
// it's a bit ugly looking, but meh
|
|
when GL_DEBUG {
|
|
@private
|
|
check_error :: proc(
|
|
id: u32, type: Shader_Type, status: u32,
|
|
iv_func: proc "c" (u32, u32, [^]i32, runtime.Source_Code_Location),
|
|
log_func: proc "c" (u32, i32, ^i32, [^]u8, runtime.Source_Code_Location),
|
|
loc := #caller_location,
|
|
) -> (success: bool) {
|
|
result, info_log_length: i32
|
|
iv_func(id, status, &result, loc)
|
|
iv_func(id, INFO_LOG_LENGTH, &info_log_length, loc)
|
|
|
|
if result == 0 {
|
|
if log_func == GetShaderInfoLog {
|
|
delete(last_compile_error_message)
|
|
last_compile_error_message = make([]byte, info_log_length)
|
|
last_compile_error_type = type
|
|
|
|
log_func(id, i32(info_log_length), nil, raw_data(last_compile_error_message), loc)
|
|
//fmt.printf_err("Error in %v:\n%s", type, string(last_compile_error_message[0:len(last_compile_error_message)-1]));
|
|
} else {
|
|
|
|
delete(last_link_error_message)
|
|
last_link_error_message = make([]byte, info_log_length)
|
|
last_compile_error_type = type
|
|
|
|
log_func(id, i32(info_log_length), nil, raw_data(last_link_error_message), loc)
|
|
//fmt.printf_err("Error in %v:\n%s", type, string(last_link_error_message[0:len(last_link_error_message)-1]));
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
} else {
|
|
@private
|
|
check_error :: proc(
|
|
id: u32, type: Shader_Type, status: u32,
|
|
iv_func: proc "c" (u32, u32, [^]i32),
|
|
log_func: proc "c" (u32, i32, ^i32, [^]u8),
|
|
) -> (success: bool) {
|
|
result, info_log_length: i32
|
|
iv_func(id, status, &result)
|
|
iv_func(id, INFO_LOG_LENGTH, &info_log_length)
|
|
|
|
if result == 0 {
|
|
if log_func == GetShaderInfoLog {
|
|
delete(last_compile_error_message)
|
|
last_compile_error_message = make([]u8, info_log_length)
|
|
last_link_error_type = type
|
|
|
|
log_func(id, i32(info_log_length), nil, raw_data(last_compile_error_message))
|
|
fmt.eprintf("Error in %v:\n%s", type, string(last_compile_error_message[0:len(last_compile_error_message)-1]))
|
|
} else {
|
|
delete(last_link_error_message)
|
|
last_link_error_message = make([]u8, info_log_length)
|
|
last_link_error_type = type
|
|
|
|
log_func(id, i32(info_log_length), nil, raw_data(last_link_error_message))
|
|
fmt.eprintf("Error in %v:\n%s", type, string(last_link_error_message[0:len(last_link_error_message)-1]))
|
|
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Compiling shaders are identical for any shader (vertex, geometry, fragment, tesselation, (maybe compute too))
|
|
@private
|
|
compile_shader_from_source :: proc(shader_data: string, shader_type: Shader_Type) -> (shader_id: u32, ok: bool) {
|
|
shader_id = CreateShader(cast(u32)shader_type)
|
|
length := i32(len(shader_data))
|
|
shader_data_copy := cstring(raw_data(shader_data))
|
|
ShaderSource(shader_id, 1, &shader_data_copy, &length)
|
|
CompileShader(shader_id)
|
|
|
|
check_error(shader_id, shader_type, COMPILE_STATUS, GetShaderiv, GetShaderInfoLog) or_return
|
|
ok = true
|
|
return
|
|
}
|
|
|
|
// only used once, but I'd just make a subprocedure(?) for consistency
|
|
@private
|
|
create_and_link_program :: proc(shader_ids: []u32, binary_retrievable := false) -> (program_id: u32, ok: bool) {
|
|
program_id = CreateProgram()
|
|
for id in shader_ids {
|
|
AttachShader(program_id, id)
|
|
}
|
|
if binary_retrievable {
|
|
ProgramParameteri(program_id, PROGRAM_BINARY_RETRIEVABLE_HINT, 1/*true*/)
|
|
}
|
|
LinkProgram(program_id)
|
|
|
|
check_error(program_id, Shader_Type.SHADER_LINK, LINK_STATUS, GetProgramiv, GetProgramInfoLog) or_return
|
|
ok = true
|
|
return
|
|
}
|
|
|
|
load_compute_file :: proc(filename: string, binary_retrievable := false) -> (program_id: u32, ok: bool) {
|
|
cs_data := os.read_entire_file(filename) or_return
|
|
defer delete(cs_data)
|
|
|
|
// Create the shaders
|
|
compute_shader_id := compile_shader_from_source(string(cs_data), Shader_Type(COMPUTE_SHADER)) or_return
|
|
return create_and_link_program([]u32{compute_shader_id}, binary_retrievable)
|
|
}
|
|
|
|
load_compute_source :: proc(cs_data: string, binary_retrievable := false) -> (program_id: u32, ok: bool) {
|
|
// Create the shaders
|
|
compute_shader_id := compile_shader_from_source(cs_data, Shader_Type(COMPUTE_SHADER)) or_return
|
|
return create_and_link_program([]u32{compute_shader_id}, binary_retrievable)
|
|
}
|
|
|
|
load_shaders_file :: proc(vs_filename, fs_filename: string, binary_retrievable := false) -> (program_id: u32, ok: bool) {
|
|
vs_data := os.read_entire_file(vs_filename) or_return
|
|
defer delete(vs_data)
|
|
|
|
fs_data := os.read_entire_file(fs_filename) or_return
|
|
defer delete(fs_data)
|
|
|
|
return load_shaders_source(string(vs_data), string(fs_data), binary_retrievable)
|
|
}
|
|
|
|
load_shaders_source :: proc(vs_source, fs_source: string, binary_retrievable := false) -> (program_id: u32, ok: bool) {
|
|
// actual function from here
|
|
vertex_shader_id := compile_shader_from_source(vs_source, Shader_Type.VERTEX_SHADER) or_return
|
|
defer DeleteShader(vertex_shader_id)
|
|
|
|
fragment_shader_id := compile_shader_from_source(fs_source, Shader_Type.FRAGMENT_SHADER) or_return
|
|
defer DeleteShader(fragment_shader_id)
|
|
|
|
return create_and_link_program([]u32{vertex_shader_id, fragment_shader_id}, binary_retrievable)
|
|
}
|
|
|
|
load_shaders :: proc{load_shaders_file}
|
|
|
|
|
|
when ODIN_OS == .Windows {
|
|
update_shader_if_changed :: proc(
|
|
vertex_name, fragment_name: string,
|
|
program: u32,
|
|
last_vertex_time, last_fragment_time: os.File_Time,
|
|
) -> (
|
|
old_program: u32,
|
|
current_vertex_time, current_fragment_time: os.File_Time,
|
|
updated: bool,
|
|
) {
|
|
current_vertex_time, _ = os.last_write_time_by_name(vertex_name)
|
|
current_fragment_time, _ = os.last_write_time_by_name(fragment_name)
|
|
old_program = program
|
|
|
|
if current_vertex_time != last_vertex_time || current_fragment_time != last_fragment_time {
|
|
new_program, success := load_shaders(vertex_name, fragment_name)
|
|
if success {
|
|
DeleteProgram(old_program)
|
|
old_program = new_program
|
|
fmt.println("Updated shaders")
|
|
updated = true
|
|
} else {
|
|
fmt.println("Failed to update shaders")
|
|
}
|
|
}
|
|
|
|
return old_program, current_vertex_time, current_fragment_time, updated
|
|
}
|
|
|
|
update_shader_if_changed_compute :: proc(
|
|
compute_name: string,
|
|
program: u32,
|
|
last_compute_time: os.File_Time,
|
|
) -> (
|
|
old_program: u32,
|
|
current_compute_time: os.File_Time,
|
|
updated: bool,
|
|
) {
|
|
current_compute_time, _ = os.last_write_time_by_name(compute_name)
|
|
old_program = program
|
|
|
|
if current_compute_time != last_compute_time {
|
|
new_program, success := load_compute_file(compute_name)
|
|
if success {
|
|
DeleteProgram(old_program)
|
|
old_program = new_program
|
|
fmt.println("Updated shaders")
|
|
updated = true
|
|
} else {
|
|
fmt.println("Failed to update shaders")
|
|
}
|
|
}
|
|
|
|
return old_program, current_compute_time, updated
|
|
}
|
|
}
|
|
|
|
|
|
Uniform_Type :: enum i32 {
|
|
FLOAT = 0x1406,
|
|
FLOAT_VEC2 = 0x8B50,
|
|
FLOAT_VEC3 = 0x8B51,
|
|
FLOAT_VEC4 = 0x8B52,
|
|
|
|
DOUBLE = 0x140A,
|
|
DOUBLE_VEC2 = 0x8FFC,
|
|
DOUBLE_VEC3 = 0x8FFD,
|
|
DOUBLE_VEC4 = 0x8FFE,
|
|
|
|
INT = 0x1404,
|
|
INT_VEC2 = 0x8B53,
|
|
INT_VEC3 = 0x8B54,
|
|
INT_VEC4 = 0x8B55,
|
|
|
|
UNSIGNED_INT = 0x1405,
|
|
UNSIGNED_INT_VEC2 = 0x8DC6,
|
|
UNSIGNED_INT_VEC3 = 0x8DC7,
|
|
UNSIGNED_INT_VEC4 = 0x8DC8,
|
|
|
|
BOOL = 0x8B56,
|
|
BOOL_VEC2 = 0x8B57,
|
|
BOOL_VEC3 = 0x8B58,
|
|
BOOL_VEC4 = 0x8B59,
|
|
|
|
FLOAT_MAT2 = 0x8B5A,
|
|
FLOAT_MAT3 = 0x8B5B,
|
|
FLOAT_MAT4 = 0x8B5C,
|
|
FLOAT_MAT2x3 = 0x8B65,
|
|
FLOAT_MAT2x4 = 0x8B66,
|
|
FLOAT_MAT3x2 = 0x8B67,
|
|
FLOAT_MAT3x4 = 0x8B68,
|
|
FLOAT_MAT4x2 = 0x8B69,
|
|
FLOAT_MAT4x3 = 0x8B6A,
|
|
|
|
DOUBLE_MAT2 = 0x8F46,
|
|
DOUBLE_MAT3 = 0x8F47,
|
|
DOUBLE_MAT4 = 0x8F48,
|
|
DOUBLE_MAT2x3 = 0x8F49,
|
|
DOUBLE_MAT2x4 = 0x8F4A,
|
|
DOUBLE_MAT3x2 = 0x8F4B,
|
|
DOUBLE_MAT3x4 = 0x8F4C,
|
|
DOUBLE_MAT4x2 = 0x8F4D,
|
|
DOUBLE_MAT4x3 = 0x8F4E,
|
|
|
|
SAMPLER_1D = 0x8B5D,
|
|
SAMPLER_2D = 0x8B5E,
|
|
SAMPLER_3D = 0x8B5F,
|
|
SAMPLER_CUBE = 0x8B60,
|
|
SAMPLER_1D_SHADOW = 0x8B61,
|
|
SAMPLER_2D_SHADOW = 0x8B62,
|
|
SAMPLER_1D_ARRAY = 0x8DC0,
|
|
SAMPLER_2D_ARRAY = 0x8DC1,
|
|
SAMPLER_1D_ARRAY_SHADOW = 0x8DC3,
|
|
SAMPLER_2D_ARRAY_SHADOW = 0x8DC4,
|
|
SAMPLER_2D_MULTISAMPLE = 0x9108,
|
|
SAMPLER_2D_MULTISAMPLE_ARRAY = 0x910B,
|
|
SAMPLER_CUBE_SHADOW = 0x8DC5,
|
|
SAMPLER_BUFFER = 0x8DC2,
|
|
SAMPLER_2D_RECT = 0x8B63,
|
|
SAMPLER_2D_RECT_SHADOW = 0x8B64,
|
|
|
|
INT_SAMPLER_1D = 0x8DC9,
|
|
INT_SAMPLER_2D = 0x8DCA,
|
|
INT_SAMPLER_3D = 0x8DCB,
|
|
INT_SAMPLER_CUBE = 0x8DCC,
|
|
INT_SAMPLER_1D_ARRAY = 0x8DCE,
|
|
INT_SAMPLER_2D_ARRAY = 0x8DCF,
|
|
INT_SAMPLER_2D_MULTISAMPLE = 0x9109,
|
|
INT_SAMPLER_2D_MULTISAMPLE_ARRAY = 0x910C,
|
|
INT_SAMPLER_BUFFER = 0x8DD0,
|
|
INT_SAMPLER_2D_RECT = 0x8DCD,
|
|
|
|
UNSIGNED_INT_SAMPLER_1D = 0x8DD1,
|
|
UNSIGNED_INT_SAMPLER_2D = 0x8DD2,
|
|
UNSIGNED_INT_SAMPLER_3D = 0x8DD3,
|
|
UNSIGNED_INT_SAMPLER_CUBE = 0x8DD4,
|
|
UNSIGNED_INT_SAMPLER_1D_ARRAY = 0x8DD6,
|
|
UNSIGNED_INT_SAMPLER_2D_ARRAY = 0x8DD7,
|
|
UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE = 0x910A,
|
|
UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY = 0x910D,
|
|
UNSIGNED_INT_SAMPLER_BUFFER = 0x8DD8,
|
|
UNSIGNED_INT_SAMPLER_2D_RECT = 0x8DD5,
|
|
|
|
IMAGE_1D = 0x904C,
|
|
IMAGE_2D = 0x904D,
|
|
IMAGE_3D = 0x904E,
|
|
IMAGE_2D_RECT = 0x904F,
|
|
IMAGE_CUBE = 0x9050,
|
|
IMAGE_BUFFER = 0x9051,
|
|
IMAGE_1D_ARRAY = 0x9052,
|
|
IMAGE_2D_ARRAY = 0x9053,
|
|
IMAGE_CUBE_MAP_ARRAY = 0x9054,
|
|
IMAGE_2D_MULTISAMPLE = 0x9055,
|
|
IMAGE_2D_MULTISAMPLE_ARRAY = 0x9056,
|
|
|
|
INT_IMAGE_1D = 0x9057,
|
|
INT_IMAGE_2D = 0x9058,
|
|
INT_IMAGE_3D = 0x9059,
|
|
INT_IMAGE_2D_RECT = 0x905A,
|
|
INT_IMAGE_CUBE = 0x905B,
|
|
INT_IMAGE_BUFFER = 0x905C,
|
|
INT_IMAGE_1D_ARRAY = 0x905D,
|
|
INT_IMAGE_2D_ARRAY = 0x905E,
|
|
INT_IMAGE_CUBE_MAP_ARRAY = 0x905F,
|
|
INT_IMAGE_2D_MULTISAMPLE = 0x9060,
|
|
INT_IMAGE_2D_MULTISAMPLE_ARRAY = 0x9061,
|
|
|
|
UNSIGNED_INT_IMAGE_1D = 0x9062,
|
|
UNSIGNED_INT_IMAGE_2D = 0x9063,
|
|
UNSIGNED_INT_IMAGE_3D = 0x9064,
|
|
UNSIGNED_INT_IMAGE_2D_RECT = 0x9065,
|
|
UNSIGNED_INT_IMAGE_CUBE = 0x9066,
|
|
UNSIGNED_INT_IMAGE_BUFFER = 0x9067,
|
|
UNSIGNED_INT_IMAGE_1D_ARRAY = 0x9068,
|
|
UNSIGNED_INT_IMAGE_2D_ARRAY = 0x9069,
|
|
UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY = 0x906A,
|
|
UNSIGNED_INT_IMAGE_2D_MULTISAMPLE = 0x906B,
|
|
UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY = 0x906C,
|
|
|
|
UNSIGNED_INT_ATOMIC_COUNTER = 0x92DB,
|
|
}
|
|
|
|
Uniform_Info :: struct {
|
|
location: i32,
|
|
size: i32,
|
|
kind: Uniform_Type,
|
|
name: string, // NOTE: This will need to be freed
|
|
}
|
|
|
|
Uniforms :: map[string]Uniform_Info
|
|
|
|
destroy_uniforms :: proc(u: Uniforms) {
|
|
for _, v in u {
|
|
delete(v.name)
|
|
}
|
|
delete(u)
|
|
}
|
|
|
|
get_uniforms_from_program :: proc(program: u32) -> (uniforms: Uniforms) {
|
|
uniform_count: i32
|
|
GetProgramiv(program, ACTIVE_UNIFORMS, &uniform_count)
|
|
|
|
if uniform_count > 0 {
|
|
reserve(&uniforms, int(uniform_count))
|
|
}
|
|
|
|
for i in 0..<uniform_count {
|
|
uniform_info: Uniform_Info
|
|
|
|
length: i32
|
|
cname: [256]u8
|
|
GetActiveUniform(program, u32(i), 256, &length, &uniform_info.size, cast(^u32)&uniform_info.kind, &cname[0])
|
|
|
|
uniform_info.location = GetUniformLocation(program, cstring(&cname[0]))
|
|
uniform_info.name = strings.clone(string(cname[:length])) // @NOTE: These need to be freed
|
|
uniforms[uniform_info.name] = uniform_info
|
|
}
|
|
|
|
return uniforms
|
|
}
|