Files
Odin/vendor/OpenGL/helpers.odin
2023-08-04 15:54:44 +01:00

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
}