diff --git a/Examples/OpenGL/main.c b/Examples/OpenGL/main.c new file mode 100644 index 0000000..9df25ad --- /dev/null +++ b/Examples/OpenGL/main.c @@ -0,0 +1,33 @@ +#include + +#include +#include +#include + +#define WGSL_SOURCE(...) #__VA_ARGS__ + +void DebugCallBack(PulseDebugMessageSeverity severity, const char* message) +{ + if(severity == PULSE_DEBUG_MESSAGE_SEVERITY_ERROR) + { + fprintf(stderr, "Pulse Error: %s\n", message); + exit(1); + } + else if(severity == PULSE_DEBUG_MESSAGE_SEVERITY_WARNING) + fprintf(stderr, "Pulse Warning: %s\n", message); + else + printf("Pulse: %s\n", message); +} + +int main(int ac, char** av) +{ + PulseBackendFlags backend_type = PULSE_BACKEND_OPENGL; + if(ac >= 2 && strcmp(av[1], "--opengl-es") == 0) + backend_type = PULSE_BACKEND_OPENGL_ES; + PulseBackend backend = PulseLoadBackend(backend_type, PULSE_SHADER_FORMAT_GLSL_BIT, PULSE_HIGH_DEBUG); + PulseSetDebugCallback(backend, DebugCallBack); + + PulseUnloadBackend(backend); + puts("Successfully executed Pulse example using OpenGL !"); + return 0; +} diff --git a/Examples/OpenGL/xmake.lua b/Examples/OpenGL/xmake.lua new file mode 100644 index 0000000..75a5e7d --- /dev/null +++ b/Examples/OpenGL/xmake.lua @@ -0,0 +1,7 @@ +target("OpenGLExample") + add_deps("pulse_gpu") + if is_plat("linux") then + set_extension(".x86_64") + end + add_files("*.c") +target_end() diff --git a/Examples/xmake.lua b/Examples/xmake.lua index fa470a8..8f95e8e 100644 --- a/Examples/xmake.lua +++ b/Examples/xmake.lua @@ -8,6 +8,9 @@ if has_config("examples") then if has_config("webgpu") then includes("WebGPU/xmake.lua") end + if has_config("opengl") then + includes("OpenGL/xmake.lua") + end if has_config("software") then includes("Software/xmake.lua") end diff --git a/Includes/Pulse.h b/Includes/Pulse.h index 8f7c101..aa0df77 100644 --- a/Includes/Pulse.h +++ b/Includes/Pulse.h @@ -16,7 +16,7 @@ extern "C" { #include "PulseProfile.h" -#define PULSE_VERSION PULSE_MAKE_VERSION(0, 0, 1) +#define PULSE_VERSION PULSE_MAKE_VERSION(0, 1, 0) // Types typedef uint64_t PulseDeviceSize; @@ -34,12 +34,14 @@ PULSE_DEFINE_NULLABLE_HANDLE(PulseComputePass); // Flags typedef enum PulseBackendBits { - PULSE_BACKEND_INVALID = PULSE_BIT(1), - PULSE_BACKEND_ANY = PULSE_BIT(2), - PULSE_BACKEND_VULKAN = PULSE_BIT(3), - PULSE_BACKEND_METAL = PULSE_BIT(4), - PULSE_BACKEND_WEBGPU = PULSE_BIT(5), - PULSE_BACKEND_SOFTWARE = PULSE_BIT(6), + PULSE_BACKEND_INVALID = PULSE_BIT(1), + PULSE_BACKEND_ANY = PULSE_BIT(2), + PULSE_BACKEND_VULKAN = PULSE_BIT(3), + PULSE_BACKEND_METAL = PULSE_BIT(4), + PULSE_BACKEND_WEBGPU = PULSE_BIT(5), + PULSE_BACKEND_SOFTWARE = PULSE_BIT(6), + PULSE_BACKEND_OPENGL = PULSE_BIT(7), + PULSE_BACKEND_OPENGL_ES = PULSE_BIT(8), } PulseBackendBits; typedef PulseFlags PulseBackendFlags; @@ -72,6 +74,7 @@ typedef enum PulseShaderFormatsBits PULSE_SHADER_FORMAT_MSL_BIT = PULSE_BIT(2), // Can be used by Metal backend PULSE_SHADER_FORMAT_METALLIB_BIT = PULSE_BIT(3), // Can be used by Metal backend PULSE_SHADER_FORMAT_WGSL_BIT = PULSE_BIT(4), // Can be used by WebGPU backend + PULSE_SHADER_FORMAT_GLSL_BIT = PULSE_BIT(5), // Can be used by OpenGL / OpenGL_ES backend // More to come } PulseShaderFormatsBits; typedef PulseFlags PulseShaderFormatsFlags; diff --git a/Sources/Backends/OpenGL/EGL/EGLContext.c b/Sources/Backends/OpenGL/EGL/EGLContext.c new file mode 100644 index 0000000..e69de29 diff --git a/Sources/Backends/OpenGL/EGL/EGLContext.h b/Sources/Backends/OpenGL/EGL/EGLContext.h new file mode 100644 index 0000000..769b363 --- /dev/null +++ b/Sources/Backends/OpenGL/EGL/EGLContext.h @@ -0,0 +1,19 @@ +// Copyright (C) 2025 kanel +// This file is part of "Pulse" +// For conditions of distribution and use, see copyright notice in LICENSE + +#ifdef PULSE_ENABLE_OPENGL_BACKEND + +#ifndef PULSE_EGL_CONTEXT_H_ +#define PULSE_EGL_CONTEXT_H_ + +typedef struct EGLContext +{ + EGLDisplay display; + EGLSurface surface; + EGLContext handle; +} EGLContext; + +#endif // PULSE_EGL_CONTEXT_H_ + +#endif // PULSE_ENABLE_OPENGL_BACKEND diff --git a/Sources/Backends/OpenGL/EGL/EGLFunctions.h b/Sources/Backends/OpenGL/EGL/EGLFunctions.h new file mode 100644 index 0000000..dd06b55 --- /dev/null +++ b/Sources/Backends/OpenGL/EGL/EGLFunctions.h @@ -0,0 +1,24 @@ +// Copyright (C) 2025 kanel +// This file is part of "Pulse" +// For conditions of distribution and use, see copyright notice in LICENSE + +// No header guards + +#ifndef PULSE_EGL_FUNCTION + #error "You must define PULSE_EGL_FUNCTION before including this file" +#endif + +PULSE_EGL_FUNCTION(eglBindAPI, PFNEGLBINDAPIPROC) +PULSE_EGL_FUNCTION(eglChooseConfig, PFNEGLCHOOSECONFIGPROC) +PULSE_EGL_FUNCTION(eglCreateContext, PFNEGLCREATECONTEXTPROC) +PULSE_EGL_FUNCTION(eglCreatePbufferSurface, PFNEGLCREATEPBUFFERSURFACEPROC) +PULSE_EGL_FUNCTION(eglDestroyContext, PFNEGLDESTROYCONTEXTPROC) +PULSE_EGL_FUNCTION(eglDestroySurface, PFNEGLDESTROYSURFACEPROC) +PULSE_EGL_FUNCTION(eglGetConfigAttrib, PFNEGLGETCONFIGATTRIBPROC) +PULSE_EGL_FUNCTION(eglGetDisplay, PFNEGLGETDISPLAYPROC) +PULSE_EGL_FUNCTION(eglGetError, PFNEGLGETERRORPROC) +PULSE_EGL_FUNCTION(eglGetProcAddress, PFNEGLGETPROCADDRESSPROC) +PULSE_EGL_FUNCTION(eglInitialize, PFNEGLINITIALIZEPROC) +PULSE_EGL_FUNCTION(eglMakeCurrent, PFNEGLMAKECURRENTPROC) +PULSE_EGL_FUNCTION(eglQueryString, PFNEGLQUERYSTRINGPROC) +PULSE_EGL_FUNCTION(eglTerminate, PFNEGLTERMINATEPROC) diff --git a/Sources/Backends/OpenGL/OpenGL.c b/Sources/Backends/OpenGL/OpenGL.c new file mode 100644 index 0000000..569f465 --- /dev/null +++ b/Sources/Backends/OpenGL/OpenGL.c @@ -0,0 +1,76 @@ +// Copyright (C) 2025 kanel +// This file is part of "Pulse" +// For conditions of distribution and use, see copyright notice in LICENSE + +#include +#include "../../PulseInternal.h" + +#include "OpenGL.h" + +PulseBackendFlags OpenGLCheckSupport(PulseBackendFlags candidates, PulseShaderFormatsFlags shader_formats_used) +{ + if(candidates != PULSE_BACKEND_ANY && (candidates & PULSE_BACKEND_OPENGL) == 0) + return PULSE_BACKEND_INVALID; + if((shader_formats_used & PULSE_SHADER_FORMAT_GLSL_BIT) == 0) + return PULSE_BACKEND_INVALID; + + return PULSE_BACKEND_OPENGL; +} + +bool OpenGLLoadBackend(PulseBackend backend, PulseDebugLevel debug_level) +{ + PULSE_UNUSED(backend); + PULSE_UNUSED(debug_level); + OpenGLDriverData* driver_data = (OpenGLDriverData*)calloc(1, sizeof(OpenGLDriverData)); + PULSE_CHECK_ALLOCATION_RETVAL(driver_data, false); + OpenGLDriver.driver_data = driver_data; + return true; +} + +void OpenGLUnloadBackend(PulseBackend backend) +{ + free(backend->driver_data); +} + +PulseBackendFlags OpenGLESCheckSupport(PulseBackendFlags candidates, PulseShaderFormatsFlags shader_formats_used) +{ + if(candidates != PULSE_BACKEND_ANY && (candidates & PULSE_BACKEND_OPENGL) == 0) + return PULSE_BACKEND_INVALID; + if((shader_formats_used & PULSE_SHADER_FORMAT_GLSL_BIT) == 0) + return PULSE_BACKEND_INVALID; + + return PULSE_BACKEND_OPENGL; +} + +bool OpenGLESLoadBackend(PulseBackend backend, PulseDebugLevel debug_level) +{ + PULSE_UNUSED(backend); + PULSE_UNUSED(debug_level); + OpenGLDriverData* driver_data = (OpenGLDriverData*)calloc(1, sizeof(OpenGLDriverData)); + PULSE_CHECK_ALLOCATION_RETVAL(driver_data, false); + OpenGLDriver.driver_data = driver_data; + return true; +} + +void OpenGLESUnloadBackend(PulseBackend backend) +{ + free(backend->driver_data); +} + +PulseBackendHandler OpenGLDriver = { + .PFN_LoadBackend = OpenGLLoadBackend, + .PFN_UnloadBackend = OpenGLUnloadBackend, + .PFN_CreateDevice = PULSE_NULLPTR, + .backend = PULSE_BACKEND_OPENGL, + .supported_shader_formats = PULSE_SHADER_FORMAT_GLSL_BIT, + .driver_data = PULSE_NULLPTR +}; + +PulseBackendHandler OpenGLESDriver = { + .PFN_LoadBackend = OpenGLESLoadBackend, + .PFN_UnloadBackend = OpenGLESUnloadBackend, + .PFN_CreateDevice = PULSE_NULLPTR, + .backend = PULSE_BACKEND_OPENGL_ES, + .supported_shader_formats = PULSE_SHADER_FORMAT_GLSL_BIT, + .driver_data = PULSE_NULLPTR +}; diff --git a/Sources/Backends/OpenGL/OpenGL.h b/Sources/Backends/OpenGL/OpenGL.h new file mode 100644 index 0000000..62095fc --- /dev/null +++ b/Sources/Backends/OpenGL/OpenGL.h @@ -0,0 +1,23 @@ +// Copyright (C) 2025 kanel +// This file is part of "Pulse" +// For conditions of distribution and use, see copyright notice in LICENSE + +#include + +#ifdef PULSE_ENABLE_OPENGL_BACKEND + +#ifndef PULSE_OPENGL_H_ +#define PULSE_OPENGL_H_ + +#define OPENGL_RETRIEVE_DRIVER_DATA_AS(handle, cast) ((cast)handle->driver_data) + +typedef struct OpenGLDriverData +{ +} OpenGLDriverData; + +PulseBackendFlags OpenGLCheckSupport(PulseBackendFlags candidates, PulseShaderFormatsFlags shader_formats_used); // Return PULSE_BACKEND_OPENGL in case of success and PULSE_BACKEND_INVALID otherwise +PulseBackendFlags OpenGLESCheckSupport(PulseBackendFlags candidates, PulseShaderFormatsFlags shader_formats_used); // Return PULSE_BACKEND_OPENGL_ES in case of success and PULSE_BACKEND_INVALID otherwise + +#endif // PULSE_OPENGL_H_ + +#endif // PULSE_ENABLE_OPENGL_BACKEND diff --git a/Sources/Backends/OpenGL/OpenGLContext.h b/Sources/Backends/OpenGL/OpenGLContext.h new file mode 100644 index 0000000..e69de29 diff --git a/Sources/PulseBackend.c b/Sources/PulseBackend.c index a16e7cc..4791f3b 100644 --- a/Sources/PulseBackend.c +++ b/Sources/PulseBackend.c @@ -20,6 +20,9 @@ #ifdef PULSE_ENABLE_SOFTWARE_BACKEND #include "Backends/Software/Soft.h" #endif +#ifdef PULSE_ENABLE_OPENGL_BACKEND + #include "Backends/OpenGL/OpenGL.h" +#endif // Ordered by default preference static const PulseCheckBackendSupportPFN backends_supports[] = { @@ -32,6 +35,10 @@ static const PulseCheckBackendSupportPFN backends_supports[] = { #ifdef PULSE_ENABLE_WEBGPU_BACKEND WebGPUCheckSupport, #endif + #ifdef PULSE_ENABLE_OPENGL_BACKEND + OpenGLCheckSupport, + OpenGLESCheckSupport, + #endif #ifdef PULSE_ENABLE_SOFTWARE_BACKEND SoftCheckSupport, #endif @@ -49,7 +56,7 @@ void PulseSetInternalError(PulseErrorType error) void PulseLogBackend(PulseBackend backend, PulseDebugMessageSeverity type, const char* message, const char* file, const char* function, int line, ...) { - (void)file; // May be used later + PULSE_UNUSED(file); // May be used later if(backend == PULSE_NULL_HANDLE) return; if(!backend->PFN_UserDebugCallback) @@ -108,6 +115,10 @@ static PulseBackend PulseGetBackendFromFlag(PulseBackendBits flag) #ifdef PULSE_ENABLE_SOFTWARE_BACKEND case PULSE_BACKEND_SOFTWARE: return &SoftwareDriver; #endif + #ifdef PULSE_ENABLE_OPENGL_BACKEND + case PULSE_BACKEND_OPENGL: return &OpenGLDriver; + case PULSE_BACKEND_OPENGL_ES: return &OpenGLESDriver; + #endif default: break; } diff --git a/Sources/PulseInternal.h b/Sources/PulseInternal.h index cca4ee0..9f96384 100644 --- a/Sources/PulseInternal.h +++ b/Sources/PulseInternal.h @@ -181,5 +181,9 @@ void PulseLogBackend(PulseBackend backend, PulseDebugMessageSeverity type, const #ifdef PULSE_ENABLE_SOFTWARE_BACKEND extern PulseBackendHandler SoftwareDriver; #endif // PULSE_ENABLE_SOFTWARE_BACKEND +#ifdef PULSE_ENABLE_OPENGL_BACKEND + extern PulseBackendHandler OpenGLDriver; + extern PulseBackendHandler OpenGLESDriver; +#endif // PULSE_ENABLE_OPENGL_BACKEND #endif // PULSE_INTERNAL_H_ diff --git a/xmake.lua b/xmake.lua index 39bb6bd..a6f639b 100644 --- a/xmake.lua +++ b/xmake.lua @@ -34,7 +34,20 @@ local backends = { option = "software", default = true, packages = { "spirv-vm", "cpuinfo" } - } + }, + OpenGL = { + option = "opengl", + default = not is_plat("macosx", "iphoneos", "wasm", "appletvos"), + packages = { "opengl-headers", "egl-headers" }, + custom = function() + add_defines("EGL_NO_X11") + if is_plat("windows", "mingw") then + add_syslinks("User32") + else + remove_files("Sources/Backends/OpenGL/WGL/**.c") + end + end + }, } local sanitizers = {