#+build linux package opengl_backend import "core:log" import egl "vendor:egl" import bk ".." EGL_PBUFFER_BIT :: 0x0001 GL_Context :: struct { display: egl.Display, surface: egl.Surface, gl_ctx: egl.Context, width: u32, height: u32, headless: bool, } create_gl_context :: proc( surface: bk.Surface_Desc, width, height: u32, title: cstring, ) -> ( GL_Context, bool, ) { display: egl.Display native_window: egl.NativeWindowType headless := false switch surface.kind { case .X11: if surface.x11.display == nil || surface.x11.window == 0 { log.error("gpu/opengl: invalid X11 surface") return {}, false } display = egl.GetDisplay(egl.NativeDisplayType(surface.x11.display)) native_window = egl.NativeWindowType(uintptr(surface.x11.window)) case .Wayland: if surface.wayland.display == nil || surface.wayland.surface == nil { log.error("gpu/opengl: invalid Wayland surface") return {}, false } display = egl.GetDisplay(egl.NativeDisplayType(surface.wayland.display)) native_window = egl.NativeWindowType(surface.wayland.surface) case .None: display = egl.GetPlatformDisplay(.SURFACELESS_MESA, nil, nil) native_window = nil headless = true case .Win32: log.error("gpu/opengl: Linux OpenGL backend cannot use Win32 surface") return {}, false } if display == egl.NO_DISPLAY { log.error("gpu/opengl: eglGetDisplay failed") return {}, false } major, minor: i32 if !egl.Initialize(display, &major, &minor) { log.error("gpu/opengl: eglInitialize failed") return {}, false } if !egl.BindAPI(egl.OPENGL_API) { log.error("gpu/opengl: eglBindAPI(OpenGL) failed") egl.Terminate(display) return {}, false } window_config_attribs := [?]i32 { egl.SURFACE_TYPE, egl.WINDOW_BIT, egl.RENDERABLE_TYPE, egl.OPENGL_BIT, egl.RED_SIZE, 8, egl.GREEN_SIZE, 8, egl.BLUE_SIZE, 8, egl.ALPHA_SIZE, 8, egl.DEPTH_SIZE, 24, egl.NONE, } headless_config_attribs := [?]i32 { egl.SURFACE_TYPE, EGL_PBUFFER_BIT, egl.RENDERABLE_TYPE, egl.OPENGL_BIT, egl.RED_SIZE, 8, egl.GREEN_SIZE, 8, egl.BLUE_SIZE, 8, egl.ALPHA_SIZE, 8, egl.DEPTH_SIZE, 24, egl.NONE, } config: egl.Config config_count: i32 if headless { if !egl.ChooseConfig(display, &headless_config_attribs[0], &config, 1, &config_count) || config_count == 0 { log.error("gpu/opengl: no compatible surfaceless EGL config") egl.Terminate(display) return {}, false } } else if !egl.ChooseConfig(display, &window_config_attribs[0], &config, 1, &config_count) || config_count == 0 { log.error("gpu/opengl: no compatible EGL config") egl.Terminate(display) return {}, false } egl_surface := egl.NO_SURFACE if !headless { egl_surface = egl.CreateWindowSurface(display, config, native_window, nil) if egl_surface == egl.NO_SURFACE { log.error("gpu/opengl: eglCreateWindowSurface failed") egl.Terminate(display) return {}, false } } context_attribs := [?]i32 { egl.CONTEXT_MAJOR_VERSION, 4, egl.CONTEXT_MINOR_VERSION, 5, egl.CONTEXT_OPENGL_PROFILE_MASK, egl.CONTEXT_OPENGL_CORE_PROFILE_BIT, egl.NONE, } gl_ctx := egl.CreateContext(display, config, egl.NO_CONTEXT, &context_attribs[0]) if gl_ctx == egl.NO_CONTEXT { log.error("gpu/opengl: eglCreateContext(4.5 core) failed") egl.DestroySurface(display, egl_surface) egl.Terminate(display) return {}, false } if !egl.MakeCurrent(display, egl_surface, egl_surface, gl_ctx) { log.error("gpu/opengl: eglMakeCurrent failed") egl.DestroyContext(display, gl_ctx) if egl_surface != egl.NO_SURFACE do egl.DestroySurface(display, egl_surface) egl.Terminate(display) return {}, false } if !headless do egl.SwapInterval(display, 1) return { display = display, surface = egl_surface, gl_ctx = gl_ctx, width = width, height = height, headless = headless, }, true } destroy_gl_context :: proc(ctx: ^GL_Context) { if ctx == nil || ctx.display == egl.NO_DISPLAY do return egl.MakeCurrent(ctx.display, egl.NO_SURFACE, egl.NO_SURFACE, egl.NO_CONTEXT) if ctx.gl_ctx != egl.NO_CONTEXT do egl.DestroyContext(ctx.display, ctx.gl_ctx) if ctx.surface != egl.NO_SURFACE do egl.DestroySurface(ctx.display, ctx.surface) egl.Terminate(ctx.display) ctx^ = {} } make_gl_context_current :: proc(ctx: ^GL_Context) -> bool { if ctx == nil || ctx.display == egl.NO_DISPLAY || ctx.gl_ctx == egl.NO_CONTEXT do return false return egl.MakeCurrent(ctx.display, ctx.surface, ctx.surface, ctx.gl_ctx) == egl.TRUE } swap_gl_context :: proc(ctx: ^GL_Context) -> bool { if ctx == nil || ctx.display == egl.NO_DISPLAY do return false if ctx.headless do return true if ctx.surface == egl.NO_SURFACE do return false return egl.SwapBuffers(ctx.display, ctx.surface) == egl.TRUE } resize_gl_context :: proc(ctx: ^GL_Context, width, height: u32) { if ctx == nil do return ctx.width = width ctx.height = height } gl_context_set_proc_address :: proc(p: rawptr, name: cstring) { (^rawptr)(p)^ = egl.GetProcAddress(name) }