| 1 | + | package opengl_backend | |
| 2 | + | ||
| 3 | + | import "core:log" | |
| 4 | + | import gl "vendor:OpenGL" | |
| 5 | + | ||
| 6 | + | import bk ".." | |
| 7 | + | ||
| 8 | + | @(private) | |
| 9 | + | g_gl: ^OpenGL_State | |
| 10 | + | ||
| 11 | + | OpenGL_State :: struct { | |
| 12 | + | gl_ctx: GL_Context, | |
| 13 | + | width: u32, | |
| 14 | + | height: u32, | |
| 15 | + | ||
| 16 | + | default_render_pass: bk.Render_Pass_Handle, | |
| 17 | + | current_frame: u32, | |
| 18 | + | frame_active: bool, | |
| 19 | + | render_pass_active: bool, | |
| 20 | + | } | |
| 21 | + | ||
| 22 | + | init_opengl_backend :: proc(surface: bk.Surface_Desc, width, height: u32, title: cstring) -> (backend: bk.Backend, ok: bool) { | |
| 23 | + | state := new(OpenGL_State) | |
| 24 | + | if state == nil { | |
| 25 | + | log.error("gpu/opengl: failed to allocate OpenGL state") | |
| 26 | + | return {}, false | |
| 27 | + | } | |
| 28 | + | g_gl = state | |
| 29 | + | ||
| 30 | + | gl_ctx, context_ok := create_gl_context(surface, width, height, title) | |
| 31 | + | if !context_ok { | |
| 32 | + | log.error("gpu/opengl: failed to create OpenGL 4.5 core context") | |
| 33 | + | free(state) | |
| 34 | + | g_gl = nil | |
| 35 | + | return {}, false | |
| 36 | + | } | |
| 37 | + | state.gl_ctx = gl_ctx | |
| 38 | + | state.width = width | |
| 39 | + | state.height = height | |
| 40 | + | state.default_render_pass = bk.Render_Pass_Handle(1) | |
| 41 | + | ||
| 42 | + | gl.load_up_to(4, 5, gl_context_set_proc_address) | |
| 43 | + | gl.ClipControl(gl.UPPER_LEFT, gl.ZERO_TO_ONE) | |
| 44 | + | gl.Disable(gl.DEPTH_TEST) | |
| 45 | + | gl.Disable(gl.SCISSOR_TEST) | |
| 46 | + | gl.Disable(gl.BLEND) | |
| 47 | + | ||
| 48 | + | backend = bk.Backend{ | |
| 49 | + | capabilities = bk.advertised_render_capabilities(), | |
| 50 | + | ||
| 51 | + | shutdown = shutdown_opengl, | |
| 52 | + | wait_idle = wait_idle_opengl, | |
| 53 | + | ||
| 54 | + | begin_frame = begin_frame_opengl, | |
| 55 | + | end_frame = end_frame_opengl, | |
| 56 | + | on_resize = on_resize_opengl, | |
| 57 | + | get_extent = get_extent_opengl, | |
| 58 | + | current_frame_index = current_frame_index_opengl, | |
| 59 | + | ||
| 60 | + | begin_render_pass = begin_render_pass_opengl, | |
| 61 | + | begin_default_pass = begin_default_pass_opengl, | |
| 62 | + | end_render_pass = end_render_pass_opengl, | |
| 63 | + | set_viewport = set_viewport_opengl, | |
| 64 | + | set_scissor = set_scissor_opengl, | |
| 65 | + | set_depth_bias = set_depth_bias_opengl, | |
| 66 | + | ||
| 67 | + | create_graphics_pipeline = create_graphics_pipeline_opengl, | |
| 68 | + | destroy_graphics_pipeline = destroy_graphics_pipeline_opengl, | |
| 69 | + | bind_graphics_pipeline = bind_graphics_pipeline_opengl, | |
| 70 | + | push_constants = push_constants_opengl, | |
| 71 | + | ||
| 72 | + | create_buffer = create_buffer_opengl, | |
| 73 | + | create_buffer_staged = create_buffer_staged_opengl, | |
| 74 | + | destroy_buffer = destroy_buffer_opengl, | |
| 75 | + | map_buffer = map_buffer_opengl, | |
| 76 | + | unmap_buffer = unmap_buffer_opengl, | |
| 77 | + | get_buffer_mapped = get_buffer_mapped_opengl, | |
| 78 | + | bind_vertex_buffer = bind_vertex_buffer_opengl, | |
| 79 | + | bind_index_buffer = bind_index_buffer_opengl, | |
| 80 | + | ||
| 81 | + | create_texture = create_texture_opengl, | |
| 82 | + | destroy_texture = destroy_texture_opengl, | |
| 83 | + | ||
| 84 | + | create_sampler = create_sampler_opengl, | |
| 85 | + | destroy_sampler = destroy_sampler_opengl, | |
| 86 | + | ||
| 87 | + | create_image = create_image_opengl, | |
| 88 | + | create_image_view = create_image_view_opengl, | |
| 89 | + | destroy_image = destroy_image_opengl, | |
| 90 | + | ||
| 91 | + | create_descriptor_set_layout = create_descriptor_set_layout_opengl, | |
| 92 | + | destroy_descriptor_set_layout = destroy_descriptor_set_layout_opengl, | |
| 93 | + | create_descriptor_pool = create_descriptor_pool_opengl, | |
| 94 | + | destroy_descriptor_pool = destroy_descriptor_pool_opengl, | |
| 95 | + | allocate_descriptor_set = allocate_descriptor_set_opengl, | |
| 96 | + | bind_descriptor_set = bind_descriptor_set_opengl, | |
| 97 | + | update_descriptor_image = update_descriptor_image_opengl, | |
| 98 | + | update_descriptor_buffer = update_descriptor_buffer_opengl, | |
| 99 | + | ||
| 100 | + | create_render_pass = create_render_pass_opengl, | |
| 101 | + | destroy_render_pass = destroy_render_pass_opengl, | |
| 102 | + | create_framebuffer = create_framebuffer_opengl, | |
| 103 | + | destroy_framebuffer = destroy_framebuffer_opengl, | |
| 104 | + | ||
| 105 | + | create_shader_module = create_shader_module_opengl, | |
| 106 | + | destroy_shader = destroy_shader_opengl, | |
| 107 | + | ||
| 108 | + | draw_indexed = draw_indexed_opengl, | |
| 109 | + | ||
| 110 | + | create_compute_pipeline = create_compute_pipeline_opengl, | |
| 111 | + | destroy_compute_pipeline = destroy_compute_pipeline_opengl, | |
| 112 | + | bind_compute_pipeline = bind_compute_pipeline_opengl, | |
| 113 | + | dispatch_compute = dispatch_compute_opengl, | |
| 114 | + | compute_barrier = compute_barrier_opengl, | |
| 115 | + | ||
| 116 | + | get_default_render_pass = get_default_render_pass_opengl, | |
| 117 | + | get_depth_format = get_depth_format_opengl, | |
| 118 | + | acquire_frame = acquire_frame_opengl, | |
| 119 | + | present_frame = present_frame_opengl, | |
| 120 | + | } | |
| 121 | + | ||
| 122 | + | bk.backend_initialized = true | |
| 123 | + | return backend, true | |
| 124 | + | } | |
| 125 | + | ||
| 126 | + | shutdown_opengl :: proc() { | |
| 127 | + | if g_gl == nil do return | |
| 128 | + | destroy_gl_context(&g_gl.gl_ctx) | |
| 129 | + | free(g_gl) | |
| 130 | + | g_gl = nil | |
| 131 | + | bk.backend_initialized = false | |
| 132 | + | } | |
| 133 | + | ||
| 134 | + | wait_idle_opengl :: proc() { | |
| 135 | + | gl.Finish() | |
| 136 | + | } | |
| 137 | + | ||
| 138 | + | current_frame_index_opengl :: proc() -> u32 { | |
| 139 | + | if g_gl == nil do return 0 | |
| 140 | + | return g_gl.current_frame | |
| 141 | + | } | |
| 142 | + | ||
| 143 | + | get_extent_opengl :: proc() -> bk.Extent { | |
| 144 | + | if g_gl == nil do return {} | |
| 145 | + | return {g_gl.width, g_gl.height} | |
| 146 | + | } | |
| 147 | + | ||
| 148 | + | on_resize_opengl :: proc(width, height: u32) { | |
| 149 | + | if g_gl == nil do return | |
| 150 | + | g_gl.width = width | |
| 151 | + | g_gl.height = height | |
| 152 | + | resize_gl_context(&g_gl.gl_ctx, width, height) | |
| 153 | + | } | |
| 154 | + | ||
| 155 | + | get_default_render_pass_opengl :: proc() -> bk.Render_Pass_Handle { | |
| 156 | + | if g_gl == nil do return bk.NULL_RENDER_PASS | |
| 157 | + | return g_gl.default_render_pass | |
| 158 | + | } | |
| 159 | + | ||
| 160 | + | get_depth_format_opengl :: proc() -> bk.Format { | |
| 161 | + | return .D32_SFLOAT | |
| 162 | + | } |
| 1 | + | #+build linux | |
| 2 | + | package opengl_backend | |
| 3 | + | ||
| 4 | + | import "core:log" | |
| 5 | + | import egl "vendor:egl" | |
| 6 | + | ||
| 7 | + | import bk ".." | |
| 8 | + | ||
| 9 | + | GL_Context :: struct { | |
| 10 | + | display: egl.Display, | |
| 11 | + | surface: egl.Surface, | |
| 12 | + | gl_ctx: egl.Context, | |
| 13 | + | width: u32, | |
| 14 | + | height: u32, | |
| 15 | + | } | |
| 16 | + | ||
| 17 | + | create_gl_context :: proc(surface: bk.Surface_Desc, width, height: u32, title: cstring) -> (GL_Context, bool) { | |
| 18 | + | display: egl.Display | |
| 19 | + | native_window: egl.NativeWindowType | |
| 20 | + | ||
| 21 | + | switch surface.kind { | |
| 22 | + | case .X11: | |
| 23 | + | if surface.x11.display == nil || surface.x11.window == 0 { | |
| 24 | + | log.error("gpu/opengl: invalid X11 surface") | |
| 25 | + | return {}, false | |
| 26 | + | } | |
| 27 | + | display = egl.GetDisplay(egl.NativeDisplayType(surface.x11.display)) | |
| 28 | + | native_window = egl.NativeWindowType(uintptr(surface.x11.window)) | |
| 29 | + | case .Wayland: | |
| 30 | + | if surface.wayland.display == nil || surface.wayland.surface == nil { | |
| 31 | + | log.error("gpu/opengl: invalid Wayland surface") | |
| 32 | + | return {}, false | |
| 33 | + | } | |
| 34 | + | display = egl.GetDisplay(egl.NativeDisplayType(surface.wayland.display)) | |
| 35 | + | native_window = egl.NativeWindowType(surface.wayland.surface) | |
| 36 | + | case .None, .Win32: | |
| 37 | + | log.error("gpu/opengl: Linux OpenGL backend requires X11 or Wayland surface") | |
| 38 | + | return {}, false | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | if display == egl.NO_DISPLAY { | |
| 42 | + | log.error("gpu/opengl: eglGetDisplay failed") | |
| 43 | + | return {}, false | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | major, minor: i32 | |
| 47 | + | if !egl.Initialize(display, &major, &minor) { | |
| 48 | + | log.error("gpu/opengl: eglInitialize failed") | |
| 49 | + | return {}, false | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | if !egl.BindAPI(egl.OPENGL_API) { | |
| 53 | + | log.error("gpu/opengl: eglBindAPI(OpenGL) failed") | |
| 54 | + | egl.Terminate(display) | |
| 55 | + | return {}, false | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | config_attribs := [?]i32{ | |
| 59 | + | egl.SURFACE_TYPE, egl.WINDOW_BIT, | |
| 60 | + | egl.RENDERABLE_TYPE, egl.OPENGL_BIT, | |
| 61 | + | egl.RED_SIZE, 8, | |
| 62 | + | egl.GREEN_SIZE, 8, | |
| 63 | + | egl.BLUE_SIZE, 8, | |
| 64 | + | egl.ALPHA_SIZE, 8, | |
| 65 | + | egl.DEPTH_SIZE, 24, | |
| 66 | + | egl.NONE, | |
| 67 | + | } | |
| 68 | + | config: egl.Config | |
| 69 | + | config_count: i32 | |
| 70 | + | if !egl.ChooseConfig(display, &config_attribs[0], &config, 1, &config_count) || config_count == 0 { | |
| 71 | + | log.error("gpu/opengl: no compatible EGL config") | |
| 72 | + | egl.Terminate(display) | |
| 73 | + | return {}, false | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | egl_surface := egl.CreateWindowSurface(display, config, native_window, nil) | |
| 77 | + | if egl_surface == egl.NO_SURFACE { | |
| 78 | + | log.error("gpu/opengl: eglCreateWindowSurface failed") | |
| 79 | + | egl.Terminate(display) | |
| 80 | + | return {}, false | |
| 81 | + | } | |
| 82 | + | ||
| 83 | + | context_attribs := [?]i32{ | |
| 84 | + | egl.CONTEXT_MAJOR_VERSION, 4, | |
| 85 | + | egl.CONTEXT_MINOR_VERSION, 5, | |
| 86 | + | egl.CONTEXT_OPENGL_PROFILE_MASK, egl.CONTEXT_OPENGL_CORE_PROFILE_BIT, | |
| 87 | + | egl.NONE, | |
| 88 | + | } | |
| 89 | + | gl_ctx := egl.CreateContext(display, config, egl.NO_CONTEXT, &context_attribs[0]) | |
| 90 | + | if gl_ctx == egl.NO_CONTEXT { | |
| 91 | + | log.error("gpu/opengl: eglCreateContext(4.5 core) failed") | |
| 92 | + | egl.DestroySurface(display, egl_surface) | |
| 93 | + | egl.Terminate(display) | |
| 94 | + | return {}, false | |
| 95 | + | } | |
| 96 | + | ||
| 97 | + | if !egl.MakeCurrent(display, egl_surface, egl_surface, gl_ctx) { | |
| 98 | + | log.error("gpu/opengl: eglMakeCurrent failed") | |
| 99 | + | egl.DestroyContext(display, gl_ctx) | |
| 100 | + | egl.DestroySurface(display, egl_surface) | |
| 101 | + | egl.Terminate(display) | |
| 102 | + | return {}, false | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | egl.SwapInterval(display, 1) | |
| 106 | + | return {display = display, surface = egl_surface, gl_ctx = gl_ctx, width = width, height = height}, true | |
| 107 | + | } | |
| 108 | + | ||
| 109 | + | destroy_gl_context :: proc(ctx: ^GL_Context) { | |
| 110 | + | if ctx == nil || ctx.display == egl.NO_DISPLAY do return | |
| 111 | + | egl.MakeCurrent(ctx.display, egl.NO_SURFACE, egl.NO_SURFACE, egl.NO_CONTEXT) | |
| 112 | + | if ctx.gl_ctx != egl.NO_CONTEXT do egl.DestroyContext(ctx.display, ctx.gl_ctx) | |
| 113 | + | if ctx.surface != egl.NO_SURFACE do egl.DestroySurface(ctx.display, ctx.surface) | |
| 114 | + | egl.Terminate(ctx.display) | |
| 115 | + | ctx^ = {} | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | make_gl_context_current :: proc(ctx: ^GL_Context) -> bool { | |
| 119 | + | if ctx == nil || ctx.display == egl.NO_DISPLAY || ctx.gl_ctx == egl.NO_CONTEXT do return false | |
| 120 | + | return egl.MakeCurrent(ctx.display, ctx.surface, ctx.surface, ctx.gl_ctx) == egl.TRUE | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | swap_gl_context :: proc(ctx: ^GL_Context) -> bool { | |
| 124 | + | if ctx == nil || ctx.display == egl.NO_DISPLAY || ctx.surface == egl.NO_SURFACE do return false | |
| 125 | + | return egl.SwapBuffers(ctx.display, ctx.surface) == egl.TRUE | |
| 126 | + | } | |
| 127 | + | ||
| 128 | + | resize_gl_context :: proc(ctx: ^GL_Context, width, height: u32) { | |
| 129 | + | if ctx == nil do return | |
| 130 | + | ctx.width = width | |
| 131 | + | ctx.height = height | |
| 132 | + | } | |
| 133 | + | ||
| 134 | + | gl_context_set_proc_address :: proc(p: rawptr, name: cstring) { | |
| 135 | + | (^rawptr)(p)^ = egl.GetProcAddress(name) | |
| 136 | + | } |
| 1 | + | #+build windows | |
| 2 | + | package opengl_backend | |
| 3 | + | ||
| 4 | + | import "core:log" | |
| 5 | + | import win32 "core:sys/windows" | |
| 6 | + | import gl "vendor:OpenGL" | |
| 7 | + | ||
| 8 | + | import bk ".." | |
| 9 | + | ||
| 10 | + | GL_Context :: struct { | |
| 11 | + | hwnd: win32.HWND, | |
| 12 | + | hdc: win32.HDC, | |
| 13 | + | gl_ctx: win32.HGLRC, | |
| 14 | + | width: u32, | |
| 15 | + | height: u32, | |
| 16 | + | } | |
| 17 | + | ||
| 18 | + | create_gl_context :: proc(surface: bk.Surface_Desc, width, height: u32, title: cstring) -> (GL_Context, bool) { | |
| 19 | + | if surface.kind != .Win32 || surface.win32.hwnd == nil { | |
| 20 | + | log.error("gpu/opengl: Windows OpenGL backend requires Win32 surface") | |
| 21 | + | return {}, false | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | hwnd := win32.HWND(surface.win32.hwnd) | |
| 25 | + | hdc := win32.GetDC(hwnd) | |
| 26 | + | if hdc == nil { | |
| 27 | + | log.error("gpu/opengl: GetDC failed") | |
| 28 | + | return {}, false | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | pfd := win32.PIXELFORMATDESCRIPTOR{ | |
| 32 | + | nSize = size_of(win32.PIXELFORMATDESCRIPTOR), | |
| 33 | + | nVersion = 1, | |
| 34 | + | dwFlags = win32.PFD_DRAW_TO_WINDOW | win32.PFD_SUPPORT_OPENGL | win32.PFD_DOUBLEBUFFER, | |
| 35 | + | iPixelType = win32.PFD_TYPE_RGBA, | |
| 36 | + | cColorBits = 32, | |
| 37 | + | cDepthBits = 24, | |
| 38 | + | cStencilBits = 8, | |
| 39 | + | iLayerType = win32.PFD_MAIN_PLANE, | |
| 40 | + | } | |
| 41 | + | pixel_format := win32.ChoosePixelFormat(hdc, &pfd) | |
| 42 | + | if pixel_format == 0 || !win32.SetPixelFormat(hdc, pixel_format, &pfd) { | |
| 43 | + | log.error("gpu/opengl: failed to set Win32 pixel format") | |
| 44 | + | win32.ReleaseDC(hwnd, hdc) | |
| 45 | + | return {}, false | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | dummy := win32.wglCreateContext(hdc) | |
| 49 | + | if dummy == nil || !win32.wglMakeCurrent(hdc, dummy) { | |
| 50 | + | log.error("gpu/opengl: failed to create WGL bootstrap context") | |
| 51 | + | if dummy != nil do win32.wglDeleteContext(dummy) | |
| 52 | + | win32.ReleaseDC(hwnd, hdc) | |
| 53 | + | return {}, false | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | gl.load_up_to(4, 5, win32.gl_set_proc_address) | |
| 57 | + | win32.wglCreateContextAttribsARB = auto_cast win32.wglGetProcAddress("wglCreateContextAttribsARB") | |
| 58 | + | if win32.wglCreateContextAttribsARB == nil { | |
| 59 | + | log.error("gpu/opengl: WGL_ARB_create_context unavailable") | |
| 60 | + | win32.wglMakeCurrent(nil, nil) | |
| 61 | + | win32.wglDeleteContext(dummy) | |
| 62 | + | win32.ReleaseDC(hwnd, hdc) | |
| 63 | + | return {}, false | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | attribs := [?]c_int{ | |
| 67 | + | win32.CONTEXT_MAJOR_VERSION_ARB, 4, | |
| 68 | + | win32.CONTEXT_MINOR_VERSION_ARB, 5, | |
| 69 | + | win32.CONTEXT_PROFILE_MASK_ARB, win32.CONTEXT_CORE_PROFILE_BIT_ARB, | |
| 70 | + | 0, | |
| 71 | + | } | |
| 72 | + | gl_ctx := win32.wglCreateContextAttribsARB(hdc, nil, &attribs[0]) | |
| 73 | + | win32.wglMakeCurrent(nil, nil) | |
| 74 | + | win32.wglDeleteContext(dummy) | |
| 75 | + | if gl_ctx == nil || !win32.wglMakeCurrent(hdc, gl_ctx) { | |
| 76 | + | log.error("gpu/opengl: failed to create WGL 4.5 core context") | |
| 77 | + | if gl_ctx != nil do win32.wglDeleteContext(gl_ctx) | |
| 78 | + | win32.ReleaseDC(hwnd, hdc) | |
| 79 | + | return {}, false | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | gl.load_up_to(4, 5, win32.gl_set_proc_address) | |
| 83 | + | return {hwnd = hwnd, hdc = hdc, gl_ctx = gl_ctx, width = width, height = height}, true | |
| 84 | + | } | |
| 85 | + | ||
| 86 | + | destroy_gl_context :: proc(ctx: ^GL_Context) { | |
| 87 | + | if ctx == nil || ctx.hdc == nil do return | |
| 88 | + | win32.wglMakeCurrent(nil, nil) | |
| 89 | + | if ctx.gl_ctx != nil do win32.wglDeleteContext(ctx.gl_ctx) | |
| 90 | + | if ctx.hwnd != nil do win32.ReleaseDC(ctx.hwnd, ctx.hdc) | |
| 91 | + | ctx^ = {} | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | make_gl_context_current :: proc(ctx: ^GL_Context) -> bool { | |
| 95 | + | if ctx == nil || ctx.hdc == nil || ctx.gl_ctx == nil do return false | |
| 96 | + | return win32.wglMakeCurrent(ctx.hdc, ctx.gl_ctx) | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | swap_gl_context :: proc(ctx: ^GL_Context) -> bool { | |
| 100 | + | if ctx == nil || ctx.hdc == nil do return false | |
| 101 | + | return win32.SwapBuffers(ctx.hdc) | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | resize_gl_context :: proc(ctx: ^GL_Context, width, height: u32) { | |
| 105 | + | if ctx == nil do return | |
| 106 | + | ctx.width = width | |
| 107 | + | ctx.height = height | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | gl_context_set_proc_address :: proc(p: rawptr, name: cstring) { | |
| 111 | + | win32.gl_set_proc_address(p, name) | |
| 112 | + | } |
| 1 | + | package opengl_backend | |
| 2 | + | ||
| 3 | + | import "core:log" | |
| 4 | + | import gl "vendor:OpenGL" | |
| 5 | + | ||
| 6 | + | import bk ".." | |
| 7 | + | ||
| 8 | + | begin_frame_opengl :: proc(clear_color: [4]f32) -> (bk.Frame_Context, bool) { | |
| 9 | + | if g_gl == nil { | |
| 10 | + | log.error("gpu/opengl: begin_frame before backend init") | |
| 11 | + | return {}, false | |
| 12 | + | } | |
| 13 | + | make_gl_context_current(&g_gl.gl_ctx) | |
| 14 | + | g_gl.frame_active = true | |
| 15 | + | return {frame_index = g_gl.current_frame}, true | |
| 16 | + | } | |
| 17 | + | ||
| 18 | + | end_frame_opengl :: proc(ctx: bk.Frame_Context) -> bool { | |
| 19 | + | if g_gl == nil do return false | |
| 20 | + | g_gl.frame_active = false | |
| 21 | + | g_gl.current_frame = (g_gl.current_frame + 1) % bk.MAX_FRAMES_IN_FLIGHT | |
| 22 | + | return false | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | acquire_frame_opengl :: proc() -> (image_index: u32, ok: bool) { | |
| 26 | + | if g_gl == nil do return 0, false | |
| 27 | + | return 0, true | |
| 28 | + | } | |
| 29 | + | ||
| 30 | + | present_frame_opengl :: proc(image_index: u32) -> bool { | |
| 31 | + | if g_gl == nil do return false | |
| 32 | + | return swap_gl_context(&g_gl.gl_ctx) | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | begin_default_pass_opengl :: proc(ctx: bk.Frame_Context, clear_color: [4]f32) { | |
| 36 | + | if g_gl == nil do return | |
| 37 | + | gl.BindFramebuffer(gl.FRAMEBUFFER, 0) | |
| 38 | + | gl.Viewport(0, 0, i32(g_gl.width), i32(g_gl.height)) | |
| 39 | + | gl.ClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]) | |
| 40 | + | gl.ClearDepth(1.0) | |
| 41 | + | gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) | |
| 42 | + | g_gl.render_pass_active = true | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | begin_render_pass_opengl :: proc(ctx: bk.Frame_Context, desc: bk.Render_Pass_Begin_Desc) { | |
| 46 | + | if desc.framebuffer == bk.NULL_FRAMEBUFFER { | |
| 47 | + | begin_default_pass_opengl(ctx, desc.clear_color) | |
| 48 | + | return | |
| 49 | + | } | |
| 50 | + | log.error("gpu/opengl: offscreen render passes not implemented yet") | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | end_render_pass_opengl :: proc(ctx: bk.Frame_Context) { | |
| 54 | + | if g_gl == nil do return | |
| 55 | + | g_gl.render_pass_active = false | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | set_viewport_opengl :: proc(ctx: bk.Frame_Context, x, y, w, h: f32) { | |
| 59 | + | gl.Viewport(i32(x), i32(y), i32(w), i32(h)) | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | set_scissor_opengl :: proc(ctx: bk.Frame_Context, x, y: i32, w, h: u32) { | |
| 63 | + | gl.Enable(gl.SCISSOR_TEST) | |
| 64 | + | gl.Scissor(x, y, i32(w), i32(h)) | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | set_depth_bias_opengl :: proc(ctx: bk.Frame_Context, constant, slope: f32) { | |
| 68 | + | log.error("gpu/opengl: depth bias not implemented yet") | |
| 69 | + | } |
| 1 | + | package opengl_backend | |
| 2 | + | ||
| 3 | + | import "core:log" | |
| 4 | + | import gl "vendor:OpenGL" | |
| 5 | + | ||
| 6 | + | import bk ".." | |
| 7 | + | ||
| 8 | + | create_graphics_pipeline_opengl :: proc(desc: bk.Pipeline_Desc) -> (bk.Pipeline_Handle, bool) { | |
| 9 | + | log.error("gpu/opengl: graphics pipelines not implemented yet") | |
| 10 | + | return bk.NULL_PIPELINE, false | |
| 11 | + | } | |
| 12 | + | ||
| 13 | + | destroy_graphics_pipeline_opengl :: proc(handle: bk.Pipeline_Handle) {} | |
| 14 | + | ||
| 15 | + | bind_graphics_pipeline_opengl :: proc(ctx: bk.Frame_Context, handle: bk.Pipeline_Handle) {} | |
| 16 | + | ||
| 17 | + | push_constants_opengl :: proc(ctx: bk.Frame_Context, pipeline: bk.Pipeline_Handle, stages: bk.Shader_Stage_Flags, offset, size: u32, data: rawptr) { | |
| 18 | + | log.error("gpu/opengl: push constants not implemented yet") | |
| 19 | + | } | |
| 20 | + | ||
| 21 | + | create_buffer_opengl :: proc(desc: bk.Buffer_Desc) -> (bk.Buffer_Handle, bool) { | |
| 22 | + | log.error("gpu/opengl: buffers not implemented yet") | |
| 23 | + | return bk.NULL_BUFFER, false | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | create_buffer_staged_opengl :: proc(data: rawptr, size: int, usage: bk.Buffer_Usage_Flags) -> (bk.Buffer_Handle, bool) { | |
| 27 | + | log.error("gpu/opengl: staged buffers not implemented yet") | |
| 28 | + | return bk.NULL_BUFFER, false | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | destroy_buffer_opengl :: proc(handle: bk.Buffer_Handle) {} | |
| 32 | + | ||
| 33 | + | map_buffer_opengl :: proc(handle: bk.Buffer_Handle) -> rawptr { | |
| 34 | + | log.error("gpu/opengl: buffer mapping not implemented yet") | |
| 35 | + | return nil | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | unmap_buffer_opengl :: proc(handle: bk.Buffer_Handle) {} | |
| 39 | + | ||
| 40 | + | get_buffer_mapped_opengl :: proc(handle: bk.Buffer_Handle) -> rawptr { | |
| 41 | + | return nil | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | bind_vertex_buffer_opengl :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) {} | |
| 45 | + | ||
| 46 | + | bind_index_buffer_opengl :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) {} | |
| 47 | + | ||
| 48 | + | create_texture_opengl :: proc(desc: bk.Texture_Desc, pixels: rawptr) -> (bk.Texture_Handle, bool) { | |
| 49 | + | log.error("gpu/opengl: textures not implemented yet") | |
| 50 | + | return bk.NULL_TEXTURE, false | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | destroy_texture_opengl :: proc(handle: bk.Texture_Handle) {} | |
| 54 | + | ||
| 55 | + | create_sampler_opengl :: proc(desc: bk.Sampler_Desc) -> (bk.Sampler_Handle, bool) { | |
| 56 | + | log.error("gpu/opengl: samplers not implemented yet") | |
| 57 | + | return bk.NULL_SAMPLER, false | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | destroy_sampler_opengl :: proc(handle: bk.Sampler_Handle) {} | |
| 61 | + | ||
| 62 | + | create_image_opengl :: proc(desc: bk.Texture_Desc) -> (bk.Texture_Handle, bool) { | |
| 63 | + | log.error("gpu/opengl: images not implemented yet") | |
| 64 | + | return bk.NULL_TEXTURE, false | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | create_image_view_opengl :: proc(texture: bk.Texture_Handle, format: bk.Format, aspect: bk.Image_Aspect_Flags) -> bool { | |
| 68 | + | log.error("gpu/opengl: image views not implemented yet") | |
| 69 | + | return false | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | destroy_image_opengl :: proc(handle: bk.Texture_Handle) {} | |
| 73 | + | ||
| 74 | + | create_descriptor_set_layout_opengl :: proc(bindings: []bk.Descriptor_Set_Layout_Binding) -> (bk.Descriptor_Handle, bool) { | |
| 75 | + | log.error("gpu/opengl: descriptor set layouts not implemented yet") | |
| 76 | + | return bk.NULL_DESCRIPTOR, false | |
| 77 | + | } | |
| 78 | + | ||
| 79 | + | destroy_descriptor_set_layout_opengl :: proc(handle: bk.Descriptor_Handle) {} | |
| 80 | + | ||
| 81 | + | create_descriptor_pool_opengl :: proc(max_sets: u32, types: []bk.Descriptor_Type, counts: []u32) -> (bk.Descriptor_Handle, bool) { | |
| 82 | + | log.error("gpu/opengl: descriptor pools not implemented yet") | |
| 83 | + | return bk.NULL_DESCRIPTOR, false | |
| 84 | + | } | |
| 85 | + | ||
| 86 | + | destroy_descriptor_pool_opengl :: proc(handle: bk.Descriptor_Handle) {} | |
| 87 | + | ||
| 88 | + | allocate_descriptor_set_opengl :: proc(pool: bk.Descriptor_Handle, layout: bk.Descriptor_Handle) -> (bk.Descriptor_Handle, bool) { | |
| 89 | + | log.error("gpu/opengl: descriptor sets not implemented yet") | |
| 90 | + | return bk.NULL_DESCRIPTOR, false | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | bind_descriptor_set_opengl :: proc(ctx: bk.Frame_Context, pipeline: bk.Pipeline_Handle, set: bk.Descriptor_Handle, index: u32) { | |
| 94 | + | log.error("gpu/opengl: descriptor binding not implemented yet") | |
| 95 | + | } | |
| 96 | + | ||
| 97 | + | update_descriptor_image_opengl :: proc(set: bk.Descriptor_Handle, binding: u32, texture: bk.Texture_Handle, sampler: bk.Sampler_Handle, layout: bk.Image_Layout) { | |
| 98 | + | log.error("gpu/opengl: image descriptor updates not implemented yet") | |
| 99 | + | } | |
| 100 | + | ||
| 101 | + | update_descriptor_buffer_opengl :: proc(set: bk.Descriptor_Handle, binding: u32, buffer: bk.Buffer_Handle, size: u64) { | |
| 102 | + | log.error("gpu/opengl: buffer descriptor updates not implemented yet") | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | create_render_pass_opengl :: proc(desc: bk.Render_Pass_Desc) -> (bk.Render_Pass_Handle, bool) { | |
| 106 | + | log.error("gpu/opengl: render pass objects not implemented yet") | |
| 107 | + | return bk.NULL_RENDER_PASS, false | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | destroy_render_pass_opengl :: proc(handle: bk.Render_Pass_Handle) {} | |
| 111 | + | ||
| 112 | + | create_framebuffer_opengl :: proc(desc: bk.Framebuffer_Desc) -> (bk.Framebuffer_Handle, bool) { | |
| 113 | + | log.error("gpu/opengl: framebuffers not implemented yet") | |
| 114 | + | return bk.NULL_FRAMEBUFFER, false | |
| 115 | + | } | |
| 116 | + | ||
| 117 | + | destroy_framebuffer_opengl :: proc(handle: bk.Framebuffer_Handle) {} | |
| 118 | + | ||
| 119 | + | create_shader_module_opengl :: proc(desc: bk.Shader_Module_Desc) -> (bk.Shader_Handle, bool) { | |
| 120 | + | if desc.format != .GLSL { | |
| 121 | + | log.error("gpu/opengl: shader module requires GLSL source") | |
| 122 | + | return bk.NULL_SHADER, false | |
| 123 | + | } | |
| 124 | + | log.error("gpu/opengl: shader modules not implemented yet") | |
| 125 | + | return bk.NULL_SHADER, false | |
| 126 | + | } | |
| 127 | + | ||
| 128 | + | destroy_shader_opengl :: proc(handle: bk.Shader_Handle) {} | |
| 129 | + | ||
| 130 | + | draw_indexed_opengl :: proc(ctx: bk.Frame_Context, index_count, instance_count: u32, first_index: u32, vertex_offset: i32, first_instance: u32) { | |
| 131 | + | log.error("gpu/opengl: draw_indexed not implemented yet") | |
| 132 | + | } | |
| 133 | + | ||
| 134 | + | create_compute_pipeline_opengl :: proc(shader: bk.Shader_Handle, num_buffers: u32, push_constant_size: u32) -> (bk.Pipeline_Handle, bool) { | |
| 135 | + | log.error("gpu/opengl: compute pipelines not implemented yet") | |
| 136 | + | return bk.NULL_PIPELINE, false | |
| 137 | + | } | |
| 138 | + | ||
| 139 | + | destroy_compute_pipeline_opengl :: proc(handle: bk.Pipeline_Handle) {} | |
| 140 | + | ||
| 141 | + | bind_compute_pipeline_opengl :: proc(ctx: bk.Frame_Context, handle: bk.Pipeline_Handle) {} | |
| 142 | + | ||
| 143 | + | dispatch_compute_opengl :: proc(ctx: bk.Frame_Context, groups_x, groups_y, groups_z: u32) { | |
| 144 | + | log.error("gpu/opengl: compute dispatch not implemented yet") | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | compute_barrier_opengl :: proc(ctx: bk.Frame_Context) { | |
| 148 | + | gl.MemoryBarrier(gl.SHADER_STORAGE_BARRIER_BIT | gl.TEXTURE_FETCH_BARRIER_BIT | gl.FRAMEBUFFER_BARRIER_BIT) | |
| 149 | + | } |
Diff hidden because this file has more than 800 lines.
| … | 635 unchanged lines hidden | ||
| 636 | 636 | // D3D NDC has Y+ up so we swap bottom/top to achieve the same Y-down result. | |
| 637 | 637 | ortho_2d :: proc(left, right, bottom, top: f32) -> glsl.mat4x4 { | |
| 638 | 638 | b, t: f32 | |
| 639 | - | when bk.GPU_BACKEND == "vulkan" { | |
| 639 | + | when bk.GPU_BACKEND == "vulkan" || bk.GPU_BACKEND == "opengl" { | |
| 640 | 640 | b = bottom | |
| 641 | 641 | t = top | |
| 642 | 642 | } else { | |
| … | 18 unchanged lines hidden | ||