From b92f6a995866cc09f8fbaf312737b1809a7fa913 Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 4 Aug 2026 18:00:07 +0000 Subject: [PATCH 1/4] fix internal backend initialization bug --- CHANGELOG.md | 7 ++ src/graphics/d3d12/pal_adapter_d3d12.c | 7 ++ src/graphics/d3d12/pal_d3d12.c | 46 ++++--- src/graphics/pal_graphics.c | 151 ++++++++--------------- src/graphics/pal_graphics_backends.h | 4 +- src/graphics/vulkan/pal_adapter_vulkan.c | 7 ++ src/graphics/vulkan/pal_vulkan.c | 74 ++++++++--- 7 files changed, 156 insertions(+), 140 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a90051c..18cee66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,11 @@ + + + + +## 2.0.1 +- Fixed a bug where an internal backend failing to initialize also caused the graphics system to fail. + diff --git a/src/graphics/d3d12/pal_adapter_d3d12.c b/src/graphics/d3d12/pal_adapter_d3d12.c index a7046e9..9c0a257 100644 --- a/src/graphics/d3d12/pal_adapter_d3d12.c +++ b/src/graphics/d3d12/pal_adapter_d3d12.c @@ -50,6 +50,13 @@ PalResult PAL_CALL enumerateAdaptersD3D12( uint32_t* count, PalAdapter** outAdapters) { + if (!s_D3D12.factory) { + if (!outAdapters) { + *count = 0; + } + return PAL_RESULT_SUCCESS; + } + uint32_t adapterCount = 0; IDXGIAdapter* adapter = nullptr; IDXGIAdapter4* dxAdapters[32]; diff --git a/src/graphics/d3d12/pal_d3d12.c b/src/graphics/d3d12/pal_d3d12.c index d1d4803..fd677a8 100644 --- a/src/graphics/d3d12/pal_d3d12.c +++ b/src/graphics/d3d12/pal_d3d12.c @@ -974,6 +974,7 @@ void getDescriptorTierLimitsD3D12( if (caps) { *caps = tmp; + } else { descCaps->maxPerStageSampledImages = tmp.maxPerStageSampledImages; descCaps->maxPerSetSampledImages = tmp.maxPerSetSampledImages; @@ -1051,7 +1052,7 @@ void pollMessagesD3D12(DeviceD3D12* device) queue->lpVtbl->ClearStoredMessages(queue); } -PalResult PAL_CALL initGraphicsD3D12( +PalBool PAL_CALL initGraphicsD3D12( const PalGraphicsDebugger* debugger, const PalAllocator* allocator) { @@ -1059,10 +1060,14 @@ PalResult PAL_CALL initGraphicsD3D12( s_D3D12.handle = LoadLibraryA("d3d12.dll"); s_D3D12.dxgi = LoadLibraryA("dxgi.dll"); if (!s_D3D12.handle || !s_D3D12.dxgi) { - return palMakeResult( - PAL_RESULT_CODE_PLATFORM_FAILURE, - PAL_RESULT_SOURCE_WIN32, - GetLastError()); + if (debugger && debugger->callback) { + debugger->callback( + debugger->userData, + PAL_DEBUG_MESSAGE_SEVERITY_ERROR, + PAL_DEBUG_MESSAGE_TYPE_GENERAL, + "Failed to load D3D12"); + } + return PAL_FALSE; } // clang-format off @@ -1137,27 +1142,36 @@ PalResult PAL_CALL initGraphicsD3D12( s_D3D12.adapterCount = 0; HRESULT result = s_D3D12.createDXGIFactory(0, &IID_Factory, (void**)&s_D3D12.factory); if (FAILED(result)) { - return makeResultD3D12(result); + if (debugger && debugger->callback) { + debugger->callback( + debugger->userData, + PAL_DEBUG_MESSAGE_SEVERITY_ERROR, + PAL_DEBUG_MESSAGE_TYPE_GENERAL, + "Failed to create DXGI Factory"); + } + return PAL_FALSE; } s_D3D12.allocator = allocator; - return PAL_RESULT_SUCCESS; + return PAL_TRUE; } void PAL_CALL shutdownGraphicsD3D12() { - for (int i = 0; i < s_D3D12.adapterCount; i++) { - s_D3D12.adapters[i].handle->lpVtbl->Release(s_D3D12.adapters[i].handle); - } + if (s_D3D12.factory) { + for (int i = 0; i < s_D3D12.adapterCount; i++) { + s_D3D12.adapters[i].handle->lpVtbl->Release(s_D3D12.adapters[i].handle); + } - s_D3D12.factory->lpVtbl->Release(s_D3D12.factory); - FreeLibrary(s_D3D12.handle); - FreeLibrary(s_D3D12.dxgi); + s_D3D12.factory->lpVtbl->Release(s_D3D12.factory); + FreeLibrary(s_D3D12.handle); + FreeLibrary(s_D3D12.dxgi); - if (s_D3D12.adapters) { - palFree(s_D3D12.allocator, s_D3D12.adapters); + if (s_D3D12.adapters) { + palFree(s_D3D12.allocator, s_D3D12.adapters); + } + memset(&s_D3D12, 0, sizeof(s_D3D12)); } - memset(&s_D3D12, 0, sizeof(s_D3D12)); } #endif // PAL_HAS_D3D12_BACKEND diff --git a/src/graphics/pal_graphics.c b/src/graphics/pal_graphics.c index 6331d11..ac05dbd 100644 --- a/src/graphics/pal_graphics.c +++ b/src/graphics/pal_graphics.c @@ -40,16 +40,10 @@ PAL_HANDLE(PalSampler) PAL_HANDLE(PalSurface) PAL_HANDLE(PalShaderBindingTable) -typedef struct { - int32_t count; - int32_t startIndex; - PalGraphicsVtable base; -} BackendData; - typedef struct { int32_t backendCount; const PalAllocator* allocator; - BackendData backends[MAX_BACKENDS]; + PalGraphicsVtable backends[MAX_BACKENDS]; } Graphics; static Graphics s_Graphics = {0}; @@ -178,21 +172,26 @@ static PalBool validateVtableVersion1(const PalGraphicsBackendVtable1* vtable1) return PAL_TRUE; } -static PalBool addBackend(const PalGraphicsBackendInfo* backendInfo) +static PalBool addBackend( + const void* infoOrVtable, + PalBool custom) { - BackendData* backendData = &s_Graphics.backends[s_Graphics.backendCount++]; - backendData->startIndex = 0; - backendData->count = 0; + PalGraphicsVtable* backend = &s_Graphics.backends[s_Graphics.backendCount++]; + memset(backend, 0, sizeof(PalGraphicsVtable)); - // populate our internal vtable - if (backendInfo->version == PAL_GRAPHICS_BACKEND_VTABLE_VERSION_1) { - // validate that all version 1 required pointers are set - const PalGraphicsBackendVtable1* vtable1 = (PalGraphicsBackendVtable1*)backendInfo->vtable; - if (!validateVtableVersion1(vtable1)) { - return PAL_FALSE; + if (custom) { + const PalGraphicsBackendInfo* info = infoOrVtable; + if (info->version == PAL_GRAPHICS_BACKEND_VTABLE_VERSION_1) { + // validate that all version 1 required pointers are set + const PalGraphicsBackendVtable1* vtable1 = (PalGraphicsBackendVtable1*)info->vtable; + if (!validateVtableVersion1(vtable1)) { + return PAL_FALSE; + } + backend->vtbl1 = vtable1; } - memset(&backendData->base, 0, sizeof(PalGraphicsVtable)); - backendData->base.vtbl1 = vtable1; + + } else { + backend->vtbl1 = infoOrVtable; } return PAL_TRUE; @@ -204,53 +203,21 @@ PalResult PAL_CALL palInitGraphics( uint32_t customBackendCount, const PalGraphicsBackendInfo* customBackends) { - PalResult result; - BackendData* attachedBackend = nullptr; -#ifdef _WIN32 -#if PAL_HAS_D3D12_BACKEND - result = initGraphicsD3D12(debugger, allocator); - if (result != PAL_RESULT_SUCCESS) { - return result; - } - - attachedBackend = &s_Graphics.backends[s_Graphics.backendCount++]; - attachedBackend->base.vtbl1 = &s_D3D12Backend1; - attachedBackend->startIndex = 0; - attachedBackend->count = 0; -#endif // PAL_HAS_D3D12_BACKEND - #if PAL_HAS_VULKAN_BACKEND - result = initGraphicsVk(debugger, allocator); - if (result != PAL_RESULT_SUCCESS) { - return result; + if (initGraphicsVk(debugger, allocator)) { + addBackend(&s_VkBackend1, PAL_FALSE); } - - attachedBackend = &s_Graphics.backends[s_Graphics.backendCount++]; - attachedBackend->base.vtbl1 = &s_VkBackend1; - attachedBackend->startIndex = 0; - attachedBackend->count = 0; #endif // PAL_HAS_VULKAN_BACKEND -#elif defined(__linux__) - // vulkan -#if PAL_HAS_VULKAN_BACKEND - result = initGraphicsVk(debugger, allocator); - if (result != PAL_RESULT_SUCCESS) { - return result; +#if PAL_HAS_D3D12_BACKEND + if (initGraphicsD3D12(debugger, allocator)) { + addBackend(&s_D3D12Backend1, PAL_FALSE); } - - attachedBackend = &s_Graphics.backends[s_Graphics.backendCount++]; - attachedBackend->base.vtbl1 = &s_VkBackend1; - attachedBackend->startIndex = 0; - attachedBackend->count = 0; -#endif // PAL_HAS_VULKAN_BACKEND -#else - // metal or andriod -#endif // _WIN32 +#endif // PAL_HAS_D3D12_BACKEND // custom backends for (uint32_t i = 0; i < customBackendCount; i++) { - if (!addBackend(&customBackends[i])) { + if (!addBackend(&customBackends[i], PAL_TRUE)) { return PAL_RESULT_CODE_INVALID_ARGUMENT; } } @@ -261,24 +228,13 @@ PalResult PAL_CALL palInitGraphics( void PAL_CALL palShutdownGraphics() { -#ifdef _WIN32 -#if PAL_HAS_D3D12_BACKEND - shutdownGraphicsD3D12(); -#endif // PAL_HAS_D3D12_BACKEND - #if PAL_HAS_VULKAN_BACKEND shutdownGraphicsVk(); #endif // PAL_HAS_VULKAN_BACKEND -#elif defined(__linux__) - // vulkan -#if PAL_HAS_VULKAN_BACKEND - shutdownGraphicsVk(); -#endif // PAL_HAS_VULKAN_BACKEND - -#else - // metal or andriod -#endif // _WIN32 +#if PAL_HAS_D3D12_BACKEND + shutdownGraphicsD3D12(); +#endif // PAL_HAS_D3D12_BACKEND memset(&s_Graphics, 0, sizeof(s_Graphics)); } @@ -296,44 +252,35 @@ PalResult PAL_CALL palEnumerateAdapters( } // enumerate all adapters for both custom and PAL backends - PalResult result = 0; - int totalCount = 0; - int index = 0; - uint32_t _count = 0; - + PalResult result; + uint32_t offset = 0; + uint32_t adapterCount = 0; for (int i = 0; i < s_Graphics.backendCount; i++) { - BackendData* backend = &s_Graphics.backends[i]; + PalGraphicsVtable* backend = &s_Graphics.backends[i]; + uint32_t backendAdapterCount = 0; + result = backend->vtbl1->enumerateAdapters(&backendAdapterCount, nullptr); + if (result == PAL_RESULT_SUCCESS) { + adapterCount += backendAdapterCount; + } + if (outAdapters) { // offset into the array so all backends write at the correct index - PalAdapter** adapters = &outAdapters[backend->startIndex]; - _count = backend->count; - result = backend->base.vtbl1->enumerateAdapters(&_count, adapters); - // break if a backend fails - if (result != PAL_RESULT_SUCCESS) { - return result; + PalAdapter** adapters = &outAdapters[offset]; + result = backend->vtbl1->enumerateAdapters(&backendAdapterCount, adapters); + if (result == PAL_RESULT_SUCCESS) { + for (int j = 0; j < backendAdapterCount; j++) { + PalAdapter* tmp = adapters[j]; + tmp->backend.vtbl1 = backend->vtbl1; + } + + // update offset since the backend provided adapters + offset += backendAdapterCount; } - - for (int j = 0; j < _count; j++) { - PalAdapter* tmp = adapters[j]; - tmp->backend.vtbl1 = backend->base.vtbl1; - } - - } else { - result = backend->base.vtbl1->enumerateAdapters(&_count, nullptr); - // break if a backend fails - if (result != PAL_RESULT_SUCCESS) { - return result; - } - - backend->startIndex = totalCount; - backend->count = _count; - totalCount += _count; - _count = 0; } } if (!outAdapters) { - *count = totalCount; + *count = adapterCount; } return PAL_RESULT_SUCCESS; } diff --git a/src/graphics/pal_graphics_backends.h b/src/graphics/pal_graphics_backends.h index 59fb054..1d290df 100644 --- a/src/graphics/pal_graphics_backends.h +++ b/src/graphics/pal_graphics_backends.h @@ -20,7 +20,7 @@ typedef struct { // ================================================== #if PAL_HAS_VULKAN_BACKEND -PalResult PAL_CALL initGraphicsVk( +PalBool PAL_CALL initGraphicsVk( const PalGraphicsDebugger* debugger, const PalAllocator* allocator); @@ -749,7 +749,7 @@ static PalGraphicsBackendVtable1 s_VkBackend1 = { // ================================================== #if PAL_HAS_D3D12_BACKEND -PalResult PAL_CALL initGraphicsD3D12( +PalBool PAL_CALL initGraphicsD3D12( const PalGraphicsDebugger* debugger, const PalAllocator* allocator); diff --git a/src/graphics/vulkan/pal_adapter_vulkan.c b/src/graphics/vulkan/pal_adapter_vulkan.c index 787118c..937d4ec 100644 --- a/src/graphics/vulkan/pal_adapter_vulkan.c +++ b/src/graphics/vulkan/pal_adapter_vulkan.c @@ -65,6 +65,13 @@ PalResult PAL_CALL enumerateAdaptersVk( uint32_t* count, PalAdapter** outAdapters) { + if (!s_Vk.instance) { + if (!outAdapters) { + *count = 0; + } + return PAL_RESULT_SUCCESS; + } + uint32_t deviceCount = 0; int adapterCount = 0; uint32_t extCount = 0; diff --git a/src/graphics/vulkan/pal_vulkan.c b/src/graphics/vulkan/pal_vulkan.c index 5269db8..7212331 100644 --- a/src/graphics/vulkan/pal_vulkan.c +++ b/src/graphics/vulkan/pal_vulkan.c @@ -26,14 +26,9 @@ #include #define VK_LIB_NAME "vulkan-1.dll" -#define RESULT_SOURCE PAL_RESULT_SOURCE_WIN32 #elif defined(__linux__) #include #define VK_LIB_NAME "libvulkan.so" -#define RESULT_SOURCE PAL_RESULT_SOURCE_POSIX -#else -// Android -#define VK_LIB_NAME "" #endif // _WIN32 #if _PAL_HAS_POSIX @@ -1009,14 +1004,21 @@ VkBool32 VKAPI_CALL debugCallbackVk( return VK_FALSE; } -PalResult PAL_CALL initGraphicsVk( +PalBool PAL_CALL initGraphicsVk( const PalGraphicsDebugger* debugger, const PalAllocator* allocator) { // load vulkan s_Vk.handle = loadLibrary(VK_LIB_NAME); if (!s_Vk.handle) { - return palMakeResult(PAL_RESULT_CODE_PLATFORM_FAILURE, RESULT_SOURCE, getNativeCode()); + if (debugger && debugger->callback) { + debugger->callback( + debugger->userData, + PAL_DEBUG_MESSAGE_SEVERITY_ERROR, + PAL_DEBUG_MESSAGE_TYPE_GENERAL, + "Failed to load Vulkan"); + } + return PAL_FALSE; } // clang-format off @@ -1386,7 +1388,13 @@ PalResult PAL_CALL initGraphicsVk( VkLayerProperties* props = nullptr; props = palAllocate(s_Vk.allocator, sizeof(VkLayerProperties) * layerCount, 0); if (!props) { - return PAL_RESULT_CODE_OUT_OF_MEMORY; + debugger->callback( + debugger->userData, + PAL_DEBUG_MESSAGE_SEVERITY_ERROR, + PAL_DEBUG_MESSAGE_TYPE_GENERAL, + "Out of memory"); + + return PAL_FALSE; } s_Vk.enumerateInstanceLayerProperties(&layerCount, props); @@ -1436,13 +1444,29 @@ PalResult PAL_CALL initGraphicsVk( const char* extensions[8]; result = s_Vk.enumerateInstanceExtensionProperties(nullptr, &extCount, nullptr); if (result != VK_SUCCESS) { - return makeResultVk(result); + if (debugger && debugger->callback) { + debugger->callback( + debugger->userData, + PAL_DEBUG_MESSAGE_SEVERITY_ERROR, + PAL_DEBUG_MESSAGE_TYPE_GENERAL, + "Failed to enumerate vulkan instance extension properties"); + } + + return PAL_FALSE; } VkExtensionProperties* extensionProps = nullptr; extensionProps = palAllocate(s_Vk.allocator, sizeof(VkExtensionProperties) * extCount, 0); if (!extensionProps) { - return PAL_RESULT_SUCCESS; + if (debugger && debugger->callback) { + debugger->callback( + debugger->userData, + PAL_DEBUG_MESSAGE_SEVERITY_ERROR, + PAL_DEBUG_MESSAGE_TYPE_GENERAL, + "Out of memory"); + } + + return PAL_FALSE; } PalBool hasXlib = PAL_FALSE; @@ -1536,7 +1560,15 @@ PalResult PAL_CALL initGraphicsVk( VkInstance instance = nullptr; result = s_Vk.createInstance(&instanceCreateInfo, &s_Vk.allocatorImpl, &instance); if (result != VK_SUCCESS) { - return makeResultVk(result); + if (debugger && debugger->callback) { + debugger->callback( + debugger->userData, + PAL_DEBUG_MESSAGE_SEVERITY_ERROR, + PAL_DEBUG_MESSAGE_TYPE_GENERAL, + "Failed to create vulkan instance - Check Driver ICD"); + } + + return PAL_FALSE; } // clang-format off @@ -1618,21 +1650,23 @@ PalResult PAL_CALL initGraphicsVk( s_Vk.adapters = nullptr; s_Vk.instance = instance; - return PAL_RESULT_SUCCESS; + return PAL_TRUE; } void PAL_CALL shutdownGraphicsVk() { - if (s_Vk.messenger) { - s_Vk.destroyMessenger(s_Vk.instance, s_Vk.messenger, &s_Vk.allocatorImpl); - } + if (s_Vk.instance) { + if (s_Vk.messenger) { + s_Vk.destroyMessenger(s_Vk.instance, s_Vk.messenger, &s_Vk.allocatorImpl); + } - s_Vk.destroyInstance(s_Vk.instance, &s_Vk.allocatorImpl); - freeLibrary(s_Vk.handle); - if (s_Vk.adapters) { - palFree(s_Vk.allocator, s_Vk.adapters); + s_Vk.destroyInstance(s_Vk.instance, &s_Vk.allocatorImpl); + freeLibrary(s_Vk.handle); + if (s_Vk.adapters) { + palFree(s_Vk.allocator, s_Vk.adapters); + } + memset(&s_Vk, 0, sizeof(s_Vk)); } - memset(&s_Vk, 0, sizeof(s_Vk)); } #endif // PAL_HAS_VULKAN_BACKEND \ No newline at end of file From ef165659dc240ffdd5c3fb8f8c9f5ca701974911 Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 4 Aug 2026 18:47:24 -0700 Subject: [PATCH 2/4] test graphics initialization bug win32 --- src/graphics/d3d12/pal_d3d12.c | 2 +- src/graphics/vulkan/pal_vulkan.c | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/graphics/d3d12/pal_d3d12.c b/src/graphics/d3d12/pal_d3d12.c index fd677a8..eaa375a 100644 --- a/src/graphics/d3d12/pal_d3d12.c +++ b/src/graphics/d3d12/pal_d3d12.c @@ -1065,7 +1065,7 @@ PalBool PAL_CALL initGraphicsD3D12( debugger->userData, PAL_DEBUG_MESSAGE_SEVERITY_ERROR, PAL_DEBUG_MESSAGE_TYPE_GENERAL, - "Failed to load D3D12"); + "Failed to load D3D12 runtime"); } return PAL_FALSE; } diff --git a/src/graphics/vulkan/pal_vulkan.c b/src/graphics/vulkan/pal_vulkan.c index 7212331..293a6c8 100644 --- a/src/graphics/vulkan/pal_vulkan.c +++ b/src/graphics/vulkan/pal_vulkan.c @@ -1444,14 +1444,6 @@ PalBool PAL_CALL initGraphicsVk( const char* extensions[8]; result = s_Vk.enumerateInstanceExtensionProperties(nullptr, &extCount, nullptr); if (result != VK_SUCCESS) { - if (debugger && debugger->callback) { - debugger->callback( - debugger->userData, - PAL_DEBUG_MESSAGE_SEVERITY_ERROR, - PAL_DEBUG_MESSAGE_TYPE_GENERAL, - "Failed to enumerate vulkan instance extension properties"); - } - return PAL_FALSE; } @@ -1560,14 +1552,6 @@ PalBool PAL_CALL initGraphicsVk( VkInstance instance = nullptr; result = s_Vk.createInstance(&instanceCreateInfo, &s_Vk.allocatorImpl, &instance); if (result != VK_SUCCESS) { - if (debugger && debugger->callback) { - debugger->callback( - debugger->userData, - PAL_DEBUG_MESSAGE_SEVERITY_ERROR, - PAL_DEBUG_MESSAGE_TYPE_GENERAL, - "Failed to create vulkan instance - Check Driver ICD"); - } - return PAL_FALSE; } From 19480ae9aa6a49d25f231ea8b92e2e5280ff0820 Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 4 Aug 2026 18:54:16 +0000 Subject: [PATCH 3/4] test graphics initialization bug linux --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18cee66..8bbf85b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ## 2.0.1 -- Fixed a bug where an internal backend failing to initialize also caused the graphics system to fail. +- Fixed a bug where an internal backend failing to initialize also caused the graphics system to fail. (#5) From 00b8bd0f7cf5445f2a51eabc93d760bd3a6173c9 Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 4 Aug 2026 19:14:38 +0000 Subject: [PATCH 4/4] add examples section to readme --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 74af4d3..bb0336b 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,17 @@ cd Release To view additional commands, run the abi dump tool with `--help`. +## Examples +PAL tests are blueprints that can be copy-paste with little code changes. See below for some examples: +- [Triangle Example](./tests/graphics/triangle_test.c) +- [Texture Example](./tests/graphics/texture_test.c) +- [Compute Example](./tests/graphics/compute_test.c) +- [Mesh Example](./tests/graphics/mesh_test.c) +- [Ray Tracing Example](./tests/graphics/ray_tracing_test.c) +- [Custom Graphics Backend Example](./tests/graphics/custom_backend_test.c) +- [Window Example](./tests/video/window_test.c) +- [Input Window Example](./tests/video/input_window_test.c) + ## Documentation PAL uses [Doxygen](https://www.doxygen.nl/) for generating API documentation.