diff --git a/include/Refresh.h b/include/Refresh.h index 285d73c..eae3531 100644 --- a/include/Refresh.h +++ b/include/Refresh.h @@ -46,6 +46,12 @@ #endif /* __GNUC__ */ #endif /* REFRESHNAMELESS */ +#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; + +VK_DEFINE_HANDLE(VkInstance) +VK_DEFINE_HANDLE(VkDevice) +VK_DEFINE_HANDLE(VkPhysicalDevice) + #include #ifdef __cplusplus @@ -608,6 +614,56 @@ typedef struct Refresh_FramebufferCreateInfo uint32_t height; } Refresh_FramebufferCreateInfo; +/* Interop Structs */ + +typedef enum Refresh_SysRendererType +{ + REFRESH_RENDERER_TYPE_VULKAN +} Refresh_SysRendererType; + +typedef struct Refresh_SysRenderer +{ + Refresh_SysRendererType rendererType; + + union + { +#if REFRESH_DRIVER_VULKAN + struct + { + VkInstance instance; + VkPhysicalDevice physicalDevice; + VkDevice logicalDevice; + uint32_t queueFamilyIndex; + } vulkan; +#endif /* REFRESH_DRIVER_VULKAN */ + uint8_t filler[64]; + } renderer; +} Refresh_SysRenderer; + +typedef struct Refresh_TextureHandles +{ + Refresh_SysRendererType rendererType; + + union + { +#if REFRESH_DRIVER_VULKAN + +#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) +#define REFRESH_VULKAN_HANDLE_TYPE void* +#else +#define REFRESH_VULKAN_HANDLE_TYPE uint64_t +#endif + + struct + { + REFRESH_VULKAN_HANDLE_TYPE image; /* VkImage */ + REFRESH_VULKAN_HANDLE_TYPE view; /* VkImageView */ + } vulkan; +#endif /* REFRESH_DRIVER_VULKAN */ + uint8_t filler[64]; + } texture; +} Refresh_TextureHandles; + /* Version API */ #define REFRESH_ABI_VERSION 0 @@ -655,6 +711,18 @@ REFRESHAPI Refresh_Device* Refresh_CreateDevice( uint8_t debugMode ); +/* Create a rendering context by taking an externally-initialized VkDevice. + * Only valid with Vulkan backend. + * Useful for piggybacking on a separate graphics library like FNA3D. + * + * sysRenderer: Externally-initialized device info. + * debugMode: Enable debug mode properties. + */ +REFRESHAPI Refresh_Device* Refresh_CreateDeviceUsingExternal( + Refresh_SysRenderer *sysRenderer, + uint8_t debugMode +); + /* Destroys a rendering context previously returned by Refresh_CreateDevice. */ REFRESHAPI void Refresh_DestroyDevice(Refresh_Device *device); @@ -1357,6 +1425,13 @@ REFRESHAPI void Refresh_Wait( Refresh_Device *device ); +/* Export handles to be consumed by another API */ +REFRESHAPI void Refresh_GetTextureHandles( + Refresh_Device* device, + Refresh_Texture* texture, + Refresh_TextureHandles* handles +); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/Refresh.c b/src/Refresh.c index 676a80e..f845c5e 100644 --- a/src/Refresh.c +++ b/src/Refresh.c @@ -141,6 +141,26 @@ Refresh_Device* Refresh_CreateDevice( ); } +Refresh_Device* Refresh_CreateDeviceUsingExternal( + Refresh_SysRenderer *sysRenderer, + uint8_t debugMode +) { + if (selectedDriver < 0) + { + return NULL; + } + + if (sysRenderer == NULL) + { + return NULL; + } + + return drivers[selectedDriver]->CreateDeviceUsingExternal( + sysRenderer, + debugMode + ); +} + void Refresh_DestroyDevice(Refresh_Device *device) { NULL_RETURN(device); @@ -895,4 +915,17 @@ void Refresh_Wait( ); } +void Refresh_GetTextureHandles( + Refresh_Device* device, + Refresh_Texture* texture, + Refresh_TextureHandles *handles +) { + NULL_RETURN(device); + return device->GetTextureHandles( + device->driverData, + texture, + handles + ); +} + /* vim: set noexpandtab shiftwidth=8 tabstop=8: */ diff --git a/src/Refresh_Driver.h b/src/Refresh_Driver.h index 2cb005e..272eccf 100644 --- a/src/Refresh_Driver.h +++ b/src/Refresh_Driver.h @@ -526,6 +526,12 @@ struct Refresh_Device Refresh_Renderer *driverData ); + void(*GetTextureHandles)( + Refresh_Renderer *driverData, + Refresh_Texture *texture, + Refresh_TextureHandles *handles + ); + /* Opaque pointer for the Driver */ Refresh_Renderer *driverData; }; @@ -583,7 +589,8 @@ struct Refresh_Device ASSIGN_DRIVER_FUNC(AcquireCommandBuffer, name) \ ASSIGN_DRIVER_FUNC(QueuePresent, name) \ ASSIGN_DRIVER_FUNC(Submit, name) \ - ASSIGN_DRIVER_FUNC(Wait, name) + ASSIGN_DRIVER_FUNC(Wait, name) \ + ASSIGN_DRIVER_FUNC(GetTextureHandles, name) typedef struct Refresh_Driver { @@ -592,6 +599,10 @@ typedef struct Refresh_Driver Refresh_PresentationParameters *presentationParameters, uint8_t debugMode ); + Refresh_Device* (*CreateDeviceUsingExternal)( + Refresh_SysRenderer *sysRenderer, + uint8_t debugMode + ); } Refresh_Driver; extern Refresh_Driver VulkanDriver; diff --git a/src/Refresh_Driver_Vulkan.c b/src/Refresh_Driver_Vulkan.c index b976d8a..1e0c5c6 100644 --- a/src/Refresh_Driver_Vulkan.c +++ b/src/Refresh_Driver_Vulkan.c @@ -1446,6 +1446,10 @@ typedef struct VulkanRenderer uint32_t submittedRenderPassesToDestroyCount; uint32_t submittedRenderPassesToDestroyCapacity; + /* External Interop */ + + uint8_t usesExternalDevice; + #define VULKAN_INSTANCE_FUNCTION(ext, ret, func, params) \ vkfntype_##func func; #define VULKAN_DEVICE_FUNCTION(ext, ret, func, params) \ @@ -3650,8 +3654,11 @@ static void VULKAN_DestroyDevice( SDL_free(renderer->buffersInUse); - renderer->vkDestroyDevice(renderer->logicalDevice, NULL); - renderer->vkDestroyInstance(renderer->instance, NULL); + if (!renderer->usesExternalDevice) + { + renderer->vkDestroyDevice(renderer->logicalDevice, NULL); + renderer->vkDestroyInstance(renderer->instance, NULL); + } SDL_free(renderer); SDL_free(device); @@ -8226,7 +8233,7 @@ static void VULKAN_Submit( } else { - submitInfo.pWaitDstStageMask = NULL; + submitInfo.pWaitDstStageMask = waitStages; submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = NULL; } @@ -8380,6 +8387,21 @@ static void VULKAN_Wait( ); } +/* External interop */ + +static void VULKAN_GetTextureHandles( + Refresh_Renderer* driverData, + Refresh_Texture* texture, + Refresh_TextureHandles *handles +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanTexture *vulkanTexture = (VulkanTexture*) texture; + + handles->rendererType = REFRESH_RENDERER_TYPE_VULKAN; + handles->texture.vulkan.image = vulkanTexture->image; + handles->texture.vulkan.view = vulkanTexture->view; +} + /* Device instantiation */ static inline uint8_t VULKAN_INTERNAL_SupportsExtension( @@ -8768,6 +8790,24 @@ static uint8_t VULKAN_INTERNAL_IsDeviceSuitable( return 0; } +static VULKAN_INTERNAL_GetPhysicalDeviceProperties( + VulkanRenderer *renderer +) { + renderer->physicalDeviceDriverProperties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR; + renderer->physicalDeviceDriverProperties.pNext = NULL; + + renderer->physicalDeviceProperties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + renderer->physicalDeviceProperties.pNext = + &renderer->physicalDeviceDriverProperties; + + renderer->vkGetPhysicalDeviceProperties2KHR( + renderer->physicalDevice, + &renderer->physicalDeviceProperties + ); +} + static uint8_t VULKAN_INTERNAL_DeterminePhysicalDevice( VulkanRenderer *renderer, const char **deviceExtensionNames, @@ -8855,19 +8895,7 @@ static uint8_t VULKAN_INTERNAL_DeterminePhysicalDevice( renderer->physicalDevice = physicalDevice; renderer->queueFamilyIndices = queueFamilyIndices; - renderer->physicalDeviceDriverProperties.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR; - renderer->physicalDeviceDriverProperties.pNext = NULL; - - renderer->physicalDeviceProperties.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - renderer->physicalDeviceProperties.pNext = - &renderer->physicalDeviceDriverProperties; - - renderer->vkGetPhysicalDeviceProperties2KHR( - renderer->physicalDevice, - &renderer->physicalDeviceProperties - ); + VULKAN_INTERNAL_GetPhysicalDeviceProperties(renderer); SDL_stack_free(physicalDevices); return 1; @@ -8991,12 +9019,44 @@ static uint8_t VULKAN_INTERNAL_CreateLogicalDevice( return 1; } -static Refresh_Device* VULKAN_CreateDevice( - Refresh_PresentationParameters *presentationParameters, - uint8_t debugMode +static void VULKAN_INTERNAL_LoadEntryPoints( + VulkanRenderer *renderer +) { + /* Load Vulkan entry points */ + if (SDL_Vulkan_LoadLibrary(NULL) < 0) + { + Refresh_LogWarn("Vulkan: SDL_Vulkan_LoadLibrary failed!"); + return 0; + } + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" + vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr(); + #pragma GCC diagnostic pop + if (vkGetInstanceProcAddr == NULL) + { + Refresh_LogWarn( + "SDL_Vulkan_GetVkGetInstanceProcAddr(): %s", + SDL_GetError() + ); + return 0; + } + + #define VULKAN_GLOBAL_FUNCTION(name) \ + name = (PFN_##name) vkGetInstanceProcAddr(VK_NULL_HANDLE, #name); \ + if (name == NULL) \ + { \ + Refresh_LogWarn("vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed"); \ + return 0; \ + } + #include "Refresh_Driver_Vulkan_vkfuncs.h" +} + +/* Expects a partially initialized VulkanRenderer */ +static Refresh_Device* VULKAN_INTERNAL_CreateDevice( + VulkanRenderer *renderer ) { Refresh_Device *result; - VulkanRenderer *renderer; VkResult vulkanResult; uint32_t i; @@ -9028,124 +9088,7 @@ static Refresh_Device* VULKAN_CreateDevice( result = (Refresh_Device*) SDL_malloc(sizeof(Refresh_Device)); ASSIGN_DRIVER(VULKAN) - renderer = (VulkanRenderer*) SDL_malloc(sizeof(VulkanRenderer)); - - /* Load Vulkan entry points */ - if (SDL_Vulkan_LoadLibrary(NULL) < 0) - { - Refresh_LogWarn("Vulkan: SDL_Vulkan_LoadLibrary failed!"); - return 0; - } - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpedantic" - vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) SDL_Vulkan_GetVkGetInstanceProcAddr(); -#pragma GCC diagnostic pop - if (vkGetInstanceProcAddr == NULL) - { - Refresh_LogWarn( - "SDL_Vulkan_GetVkGetInstanceProcAddr(): %s", - SDL_GetError() - ); - return 0; - } - - #define VULKAN_GLOBAL_FUNCTION(name) \ - name = (PFN_##name) vkGetInstanceProcAddr(VK_NULL_HANDLE, #name); \ - if (name == NULL) \ - { \ - Refresh_LogWarn("vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed"); \ - return 0; \ - } - #include "Refresh_Driver_Vulkan_vkfuncs.h" - result->driverData = (Refresh_Renderer*) renderer; - renderer->debugMode = debugMode; - renderer->headless = presentationParameters->deviceWindowHandle == NULL; - - /* Create the VkInstance */ - if (!VULKAN_INTERNAL_CreateInstance(renderer, presentationParameters->deviceWindowHandle)) - { - Refresh_LogError("Error creating vulkan instance"); - return NULL; - } - - renderer->deviceWindowHandle = presentationParameters->deviceWindowHandle; - renderer->presentMode = presentationParameters->presentMode; - - /* - * Create the WSI vkSurface - */ - - if (!SDL_Vulkan_CreateSurface( - (SDL_Window*) renderer->deviceWindowHandle, - renderer->instance, - &renderer->surface - )) { - Refresh_LogError( - "SDL_Vulkan_CreateSurface failed: %s", - SDL_GetError() - ); - return NULL; - } - - /* - * Get vkInstance entry points - */ - - #define VULKAN_INSTANCE_FUNCTION(ext, ret, func, params) \ - renderer->func = (vkfntype_##func) vkGetInstanceProcAddr(renderer->instance, #func); - #include "Refresh_Driver_Vulkan_vkfuncs.h" - - /* - * Choose/Create vkDevice - */ - - if (SDL_strcmp(SDL_GetPlatform(), "Stadia") != 0) - { - deviceExtensionCount -= 1; - } - if (!VULKAN_INTERNAL_DeterminePhysicalDevice( - renderer, - deviceExtensionNames, - deviceExtensionCount - )) { - Refresh_LogError("Failed to determine a suitable physical device"); - return NULL; - } - - Refresh_LogInfo("Refresh Driver: Vulkan"); - Refresh_LogInfo( - "Vulkan Device: %s", - renderer->physicalDeviceProperties.properties.deviceName - ); - Refresh_LogInfo( - "Vulkan Driver: %s %s", - renderer->physicalDeviceDriverProperties.driverName, - renderer->physicalDeviceDriverProperties.driverInfo - ); - Refresh_LogInfo( - "Vulkan Conformance: %u.%u.%u", - renderer->physicalDeviceDriverProperties.conformanceVersion.major, - renderer->physicalDeviceDriverProperties.conformanceVersion.minor, - renderer->physicalDeviceDriverProperties.conformanceVersion.patch - ); - Refresh_LogWarn( - "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" - "! Refresh Vulkan is still in development! !\n" - "! The API is unstable and subject to change! !\n" - "! You have been warned! !\n" - "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - ); - - if (!VULKAN_INTERNAL_CreateLogicalDevice( - renderer, - deviceExtensionNames, - deviceExtensionCount - )) { - Refresh_LogError("Failed to create logical device"); - return NULL; - } /* * Create initial swapchain @@ -9818,9 +9761,181 @@ static Refresh_Device* VULKAN_CreateDevice( return result; } +static Refresh_Device* VULKAN_CreateDevice( + Refresh_PresentationParameters *presentationParameters, + uint8_t debugMode +) { + VulkanRenderer *renderer = (VulkanRenderer*) SDL_malloc(sizeof(VulkanRenderer)); + + VULKAN_INTERNAL_LoadEntryPoints(renderer); + + /* Create the VkInstance */ + if (!VULKAN_INTERNAL_CreateInstance(renderer, presentationParameters->deviceWindowHandle)) + { + Refresh_LogError("Error creating vulkan instance"); + return NULL; + } + + renderer->deviceWindowHandle = presentationParameters->deviceWindowHandle; + renderer->presentMode = presentationParameters->presentMode; + renderer->debugMode = debugMode; + renderer->headless = presentationParameters->deviceWindowHandle == NULL; + renderer->usesExternalDevice = 0; + + /* + * Create the WSI vkSurface + */ + + if (!SDL_Vulkan_CreateSurface( + (SDL_Window*)renderer->deviceWindowHandle, + renderer->instance, + &renderer->surface + )) { + Refresh_LogError( + "SDL_Vulkan_CreateSurface failed: %s", + SDL_GetError() + ); + return NULL; + } + + /* + * Get vkInstance entry points + */ + + #define VULKAN_INSTANCE_FUNCTION(ext, ret, func, params) \ + renderer->func = (vkfntype_##func) vkGetInstanceProcAddr(renderer->instance, #func); + #include "Refresh_Driver_Vulkan_vkfuncs.h" + + /* + * Choose/Create vkDevice + */ + + if (SDL_strcmp(SDL_GetPlatform(), "Stadia") != 0) + { + deviceExtensionCount -= 1; + } + if (!VULKAN_INTERNAL_DeterminePhysicalDevice( + renderer, + deviceExtensionNames, + deviceExtensionCount + )) { + Refresh_LogError("Failed to determine a suitable physical device"); + return NULL; + } + + Refresh_LogInfo("Refresh Driver: Vulkan"); + Refresh_LogInfo( + "Vulkan Device: %s", + renderer->physicalDeviceProperties.properties.deviceName + ); + Refresh_LogInfo( + "Vulkan Driver: %s %s", + renderer->physicalDeviceDriverProperties.driverName, + renderer->physicalDeviceDriverProperties.driverInfo + ); + Refresh_LogInfo( + "Vulkan Conformance: %u.%u.%u", + renderer->physicalDeviceDriverProperties.conformanceVersion.major, + renderer->physicalDeviceDriverProperties.conformanceVersion.minor, + renderer->physicalDeviceDriverProperties.conformanceVersion.patch + ); + Refresh_LogWarn( + "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" + "! Refresh Vulkan is still in development! !\n" + "! The API is unstable and subject to change! !\n" + "! You have been warned! !\n" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + ); + + if (!VULKAN_INTERNAL_CreateLogicalDevice( + renderer, + deviceExtensionNames, + deviceExtensionCount + )) { + Refresh_LogError("Failed to create logical device"); + return NULL; + } + + return VULKAN_INTERNAL_CreateDevice(renderer); +} + +static Refresh_Device* VULKAN_CreateDeviceUsingExternal( + Refresh_SysRenderer *sysRenderer, + uint8_t debugMode +) { + VulkanRenderer* renderer = (VulkanRenderer*)SDL_malloc(sizeof(VulkanRenderer)); + + renderer->instance = sysRenderer->renderer.vulkan.instance; + renderer->physicalDevice = sysRenderer->renderer.vulkan.physicalDevice; + renderer->logicalDevice = sysRenderer->renderer.vulkan.logicalDevice; + renderer->queueFamilyIndices.computeFamily = sysRenderer->renderer.vulkan.queueFamilyIndex; + renderer->queueFamilyIndices.graphicsFamily = sysRenderer->renderer.vulkan.queueFamilyIndex; + renderer->queueFamilyIndices.presentFamily = sysRenderer->renderer.vulkan.queueFamilyIndex; + renderer->queueFamilyIndices.transferFamily = sysRenderer->renderer.vulkan.queueFamilyIndex; + renderer->deviceWindowHandle = NULL; + renderer->presentMode = 0; + renderer->debugMode = debugMode; + renderer->headless = 1; + renderer->usesExternalDevice = 1; + + VULKAN_INTERNAL_LoadEntryPoints(renderer); + + /* + * Get vkInstance entry points + */ + + #define VULKAN_INSTANCE_FUNCTION(ext, ret, func, params) \ + renderer->func = (vkfntype_##func) vkGetInstanceProcAddr(renderer->instance, #func); + #include "Refresh_Driver_Vulkan_vkfuncs.h" + + /* Load vkDevice entry points */ + + #define VULKAN_DEVICE_FUNCTION(ext, ret, func, params) \ + renderer->func = (vkfntype_##func) \ + renderer->vkGetDeviceProcAddr( \ + renderer->logicalDevice, \ + #func \ + ); + #include "Refresh_Driver_Vulkan_vkfuncs.h" + + renderer->vkGetDeviceQueue( + renderer->logicalDevice, + renderer->queueFamilyIndices.graphicsFamily, + 0, + &renderer->graphicsQueue + ); + + renderer->vkGetDeviceQueue( + renderer->logicalDevice, + renderer->queueFamilyIndices.presentFamily, + 0, + &renderer->presentQueue + ); + + renderer->vkGetDeviceQueue( + renderer->logicalDevice, + renderer->queueFamilyIndices.computeFamily, + 0, + &renderer->computeQueue + ); + + renderer->vkGetDeviceQueue( + renderer->logicalDevice, + renderer->queueFamilyIndices.transferFamily, + 0, + &renderer->transferQueue + ); + + VULKAN_INTERNAL_GetPhysicalDeviceProperties(renderer); + + return VULKAN_INTERNAL_CreateDevice(renderer); +} + + Refresh_Driver VulkanDriver = { "Vulkan", - VULKAN_CreateDevice + VULKAN_CreateDevice, + VULKAN_CreateDeviceUsingExternal }; #endif //REFRESH_DRIVER_VULKAN