Harbor

branch main
showing the latest snapshot on main
gl_backend.odin 9.3 KB · Plain text
backend/opengl/gl_backend.odin 0644 Raw
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
}