package opengl_backend import "core:log" import gl "vendor:OpenGL" import bk ".." @(private) g_gl: ^OpenGL_State OpenGL_State :: struct { gl_ctx: GL_Context, width: u32, height: u32, default_render_pass: bk.Render_Pass_Handle, current_frame: u32, current_pipeline: bk.Pipeline_Handle, frame_active: bool, render_pass_active: bool, headless: bool, push_constant_buffer: u32, buffers: [MAX_BUFFERS]GL_Buffer_Entry, textures: [MAX_TEXTURES]GL_Texture_Entry, shaders: [MAX_SHADERS]GL_Shader_Entry, pipelines: [MAX_PIPELINES]GL_Pipeline_Entry, descriptors: [MAX_DESCRIPTORS]GL_Descriptor_Entry, render_passes: [MAX_RENDER_PASSES]GL_Render_Pass_Entry, framebuffers: [MAX_FRAMEBUFFERS]GL_Framebuffer_Entry, samplers: [MAX_SAMPLERS]GL_Sampler_Entry, } MAX_BUFFERS :: 1024 MAX_TEXTURES :: 512 MAX_SHADERS :: 256 MAX_PIPELINES :: 128 MAX_DESCRIPTORS :: 512 MAX_RENDER_PASSES :: 32 MAX_FRAMEBUFFERS :: 64 MAX_SAMPLERS :: 64 OPENGL_BINDINGS_PER_GROUP :: 16 OPENGL_PUSH_CONSTANT_BINDING :: 15 PUSH_CONSTANT_MAX_SIZE :: 256 GL_Buffer_Entry :: struct { id: u32, size: u64, usage: bk.Buffer_Usage_Flags, memory: bk.Memory_Property_Flags, mapped_ptr: rawptr, active: bool, } GL_Texture_Entry :: struct { id: u32, width: u32, height: u32, format: bk.Format, usage: bk.Image_Usage_Flags, active: bool, } GL_Shader_Entry :: struct { id: u32, stage: bk.Shader_Stage, active: bool, } GL_Pipeline_Entry :: struct { program: u32, vao: u32, topology: u32, enable_blending: bool, blend_mode: bk.Blend_Mode, enable_depth_test: bool, enable_depth_bias: bool, stencil: bk.Stencil_State, cull_mode: bk.Cull_Mode, front_face: bk.Front_Face, vertex_stride: u32, vertex_strides: [16]u32, push_constant_size: u32, push_constant_stages: bk.Shader_Stage_Flags, is_compute: bool, active: bool, } Descriptor_Kind :: enum { Set_Layout, Pool, Set, } GL_Descriptor_Binding :: struct { binding: u32, type: bk.Descriptor_Type, buffer: bk.Buffer_Handle, texture: bk.Texture_Handle, sampler: bk.Sampler_Handle, buf_size: u64, } GL_Descriptor_Entry :: struct { kind: Descriptor_Kind, layout_bindings: [16]bk.Descriptor_Set_Layout_Binding, layout_count: u32, bindings: [16]GL_Descriptor_Binding, binding_count: u32, active: bool, } GL_Render_Pass_Entry :: struct { desc: bk.Render_Pass_Desc, active: bool, } GL_Framebuffer_Entry :: struct { id: u32, color_tex: bk.Texture_Handle, color_texs: [bk.MAX_COLOR_TARGETS]bk.Texture_Handle, color_count: u32, depth_tex: bk.Texture_Handle, width: u32, height: u32, active: bool, } GL_Sampler_Entry :: struct { id: u32, active: bool, } init_opengl_backend :: proc( surface: bk.Surface_Desc, width, height: u32, title: cstring, ) -> ( backend: bk.Backend, ok: bool, ) { state := new(OpenGL_State) if state == nil { log.error("gpu/opengl: failed to allocate OpenGL state") return {}, false } g_gl = state gl_ctx, context_ok := create_gl_context(surface, width, height, title) if !context_ok { log.error("gpu/opengl: failed to create OpenGL 4.5 core context") free(state) g_gl = nil return {}, false } state.gl_ctx = gl_ctx state.headless = gl_ctx.headless state.width = width state.height = height state.default_render_pass = bk.Render_Pass_Handle(1) state.render_passes[1].desc = { has_color = true, has_depth = true, color_format = .R8G8B8A8_UNORM, depth_format = .D32_SFLOAT, } state.render_passes[1].active = true gl.load_up_to(4, 5, gl_context_set_proc_address) gl.ClipControl(gl.UPPER_LEFT, gl.ZERO_TO_ONE) gl.Disable(gl.DEPTH_TEST) gl.Disable(gl.SCISSOR_TEST) gl.Disable(gl.BLEND) gl.CreateBuffers(1, &state.push_constant_buffer) gl.NamedBufferData(state.push_constant_buffer, PUSH_CONSTANT_MAX_SIZE, nil, gl.DYNAMIC_DRAW) backend = bk.Backend { capabilities = bk.implemented_base_capabilities( bk.MAX_COLOR_TARGETS, PUSH_CONSTANT_MAX_SIZE, ), shutdown = shutdown_opengl, wait_idle = wait_idle_opengl, begin_frame = begin_frame_opengl, end_frame = end_frame_opengl, on_resize = on_resize_opengl, get_extent = get_extent_opengl, current_frame_index = current_frame_index_opengl, begin_render_pass = begin_render_pass_opengl, begin_default_pass = begin_default_pass_opengl, end_render_pass = end_render_pass_opengl, set_viewport = set_viewport_opengl, set_scissor = set_scissor_opengl, set_depth_bias = set_depth_bias_opengl, create_graphics_pipeline = create_graphics_pipeline_opengl, destroy_graphics_pipeline = destroy_graphics_pipeline_opengl, bind_graphics_pipeline = bind_graphics_pipeline_opengl, push_constants = push_constants_opengl, create_buffer = create_buffer_opengl, create_buffer_staged = create_buffer_staged_opengl, destroy_buffer = destroy_buffer_opengl, map_buffer = map_buffer_opengl, unmap_buffer = unmap_buffer_opengl, get_buffer_mapped = get_buffer_mapped_opengl, bind_vertex_buffer = bind_vertex_buffer_opengl, bind_vertex_buffer_slot = bind_vertex_buffer_slot_opengl, bind_index_buffer = bind_index_buffer_opengl, create_texture = create_texture_opengl, destroy_texture = destroy_texture_opengl, read_texture_rgba8 = read_texture_rgba8_opengl, create_sampler = create_sampler_opengl, destroy_sampler = destroy_sampler_opengl, create_image = create_image_opengl, create_image_view = create_image_view_opengl, destroy_image = destroy_image_opengl, create_descriptor_set_layout = create_descriptor_set_layout_opengl, destroy_descriptor_set_layout = destroy_descriptor_set_layout_opengl, create_descriptor_pool = create_descriptor_pool_opengl, destroy_descriptor_pool = destroy_descriptor_pool_opengl, allocate_descriptor_set = allocate_descriptor_set_opengl, bind_descriptor_set = bind_descriptor_set_opengl, update_descriptor_image = update_descriptor_image_opengl, update_descriptor_buffer = update_descriptor_buffer_opengl, create_render_pass = create_render_pass_opengl, destroy_render_pass = destroy_render_pass_opengl, create_framebuffer = create_framebuffer_opengl, destroy_framebuffer = destroy_framebuffer_opengl, create_shader_module = create_shader_module_opengl, destroy_shader = destroy_shader_opengl, draw = draw_opengl, draw_indexed = draw_indexed_opengl, draw_indirect = draw_indirect_opengl, draw_indexed_indirect = draw_indexed_indirect_opengl, create_compute_pipeline = create_compute_pipeline_opengl, destroy_compute_pipeline = destroy_compute_pipeline_opengl, bind_compute_pipeline = bind_compute_pipeline_opengl, dispatch_compute = dispatch_compute_opengl, compute_barrier = compute_barrier_opengl, get_default_render_pass = get_default_render_pass_opengl, get_depth_format = get_depth_format_opengl, } bk.backend_initialized = true return backend, true } shutdown_opengl :: proc() { if g_gl == nil do return if g_gl.push_constant_buffer != 0 { id := g_gl.push_constant_buffer gl.DeleteBuffers(1, &id) } for i in 1 ..< MAX_PIPELINES { if g_gl.pipelines[i].active { destroy_graphics_pipeline_opengl(bk.Pipeline_Handle(i)) } } for i in 1 ..< MAX_SHADERS { if g_gl.shaders[i].active { destroy_shader_opengl(bk.Shader_Handle(i)) } } for i in 1 ..< MAX_BUFFERS { if g_gl.buffers[i].active { destroy_buffer_opengl(bk.Buffer_Handle(i)) } } for i in 1 ..< MAX_TEXTURES { if g_gl.textures[i].active { destroy_texture_opengl(bk.Texture_Handle(i)) } } for i in 1 ..< MAX_FRAMEBUFFERS { if g_gl.framebuffers[i].active { destroy_framebuffer_opengl(bk.Framebuffer_Handle(i)) } } for i in 1 ..< MAX_SAMPLERS { if g_gl.samplers[i].active { destroy_sampler_opengl(bk.Sampler_Handle(i)) } } destroy_gl_context(&g_gl.gl_ctx) free(g_gl) g_gl = nil bk.backend_initialized = false } wait_idle_opengl :: proc() { gl.Finish() } current_frame_index_opengl :: proc() -> u32 { if g_gl == nil do return 0 return g_gl.current_frame } get_extent_opengl :: proc() -> bk.Extent { if g_gl == nil do return {} return {g_gl.width, g_gl.height} } on_resize_opengl :: proc(width, height: u32) { if g_gl == nil do return g_gl.width = width g_gl.height = height resize_gl_context(&g_gl.gl_ctx, width, height) } get_default_render_pass_opengl :: proc() -> bk.Render_Pass_Handle { if g_gl == nil do return bk.NULL_RENDER_PASS return g_gl.default_render_pass } get_depth_format_opengl :: proc() -> bk.Format { return .D32_SFLOAT }