2020-12-17 00:27:14 +00:00
|
|
|
/* Refresh - XNA-inspired 3D Graphics Library with modern capabilities
|
|
|
|
*
|
|
|
|
* Copyright (c) 2020 Evan Hemsley
|
|
|
|
*
|
|
|
|
* This software is provided 'as-is', without any express or implied warranty.
|
|
|
|
* In no event will the authors be held liable for any damages arising from
|
|
|
|
* the use of this software.
|
|
|
|
*
|
|
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
|
|
* including commercial applications, and to alter it and redistribute it
|
|
|
|
* freely, subject to the following restrictions:
|
|
|
|
*
|
|
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
|
|
* claim that you wrote the original software. If you use this software in a
|
|
|
|
* product, an acknowledgment in the product documentation would be
|
|
|
|
* appreciated but is not required.
|
|
|
|
*
|
|
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
|
|
* misrepresented as being the original software.
|
|
|
|
*
|
|
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
|
|
*
|
|
|
|
* Evan "cosmonaut" Hemsley <evan@moonside.games>
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if REFRESH_DRIVER_VULKAN
|
|
|
|
|
|
|
|
#define VK_NO_PROTOTYPES
|
|
|
|
#include "vulkan/vulkan.h"
|
|
|
|
|
|
|
|
#include "Refresh_Driver.h"
|
|
|
|
|
|
|
|
#include <SDL.h>
|
|
|
|
#include <SDL_syswm.h>
|
|
|
|
#include <SDL_vulkan.h>
|
|
|
|
|
2020-12-17 02:38:22 +00:00
|
|
|
/* Global Vulkan Loader Entry Points */
|
|
|
|
|
|
|
|
static PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
|
|
|
|
|
|
|
|
#define VULKAN_GLOBAL_FUNCTION(name) \
|
|
|
|
static PFN_##name name = NULL;
|
|
|
|
#include "Refresh_Driver_Vulkan_vkfuncs.h"
|
|
|
|
|
|
|
|
/* vkInstance/vkDevice function typedefs */
|
|
|
|
|
|
|
|
#define VULKAN_INSTANCE_FUNCTION(ext, ret, func, params) \
|
|
|
|
typedef ret (VKAPI_CALL *vkfntype_##func) params;
|
|
|
|
#define VULKAN_DEVICE_FUNCTION(ext, ret, func, params) \
|
|
|
|
typedef ret (VKAPI_CALL *vkfntype_##func) params;
|
|
|
|
#include "Refresh_Driver_Vulkan_vkfuncs.h"
|
|
|
|
|
|
|
|
/* Required extensions */
|
|
|
|
static const char* deviceExtensionNames[] =
|
|
|
|
{
|
|
|
|
/* Globally supported */
|
|
|
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
|
|
|
/* Core since 1.1 */
|
|
|
|
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
|
|
|
|
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
|
|
|
|
VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
|
|
|
|
/* Core since 1.2 */
|
|
|
|
VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
|
|
|
|
/* EXT, probably not going to be Core */
|
|
|
|
VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
|
|
|
|
};
|
|
|
|
static uint32_t deviceExtensionCount = SDL_arraysize(deviceExtensionNames);
|
|
|
|
|
2020-12-17 03:28:02 +00:00
|
|
|
/* Enums */
|
|
|
|
|
|
|
|
typedef enum VulkanResourceAccessType
|
|
|
|
{
|
|
|
|
/* Reads */
|
|
|
|
RESOURCE_ACCESS_NONE, /* For initialization */
|
|
|
|
RESOURCE_ACCESS_INDEX_BUFFER,
|
|
|
|
RESOURCE_ACCESS_VERTEX_BUFFER,
|
|
|
|
RESOURCE_ACCESS_VERTEX_SHADER_READ_UNIFORM_BUFFER,
|
|
|
|
RESOURCE_ACCESS_VERTEX_SHADER_READ_SAMPLED_IMAGE,
|
|
|
|
RESOURCE_ACCESS_FRAGMENT_SHADER_READ_UNIFORM_BUFFER,
|
|
|
|
RESOURCE_ACCESS_FRAGMENT_SHADER_READ_SAMPLED_IMAGE,
|
|
|
|
RESOURCE_ACCESS_FRAGMENT_SHADER_READ_COLOR_ATTACHMENT,
|
|
|
|
RESOURCE_ACCESS_FRAGMENT_SHADER_READ_DEPTH_STENCIL_ATTACHMENT,
|
|
|
|
RESOURCE_ACCESS_COLOR_ATTACHMENT_READ,
|
|
|
|
RESOURCE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ,
|
|
|
|
RESOURCE_ACCESS_TRANSFER_READ,
|
|
|
|
RESOURCE_ACCESS_HOST_READ,
|
|
|
|
RESOURCE_ACCESS_PRESENT,
|
|
|
|
RESOURCE_ACCESS_END_OF_READ,
|
|
|
|
|
|
|
|
/* Writes */
|
|
|
|
RESOURCE_ACCESS_VERTEX_SHADER_WRITE,
|
|
|
|
RESOURCE_ACCESS_FRAGMENT_SHADER_WRITE,
|
|
|
|
RESOURCE_ACCESS_COLOR_ATTACHMENT_WRITE,
|
|
|
|
RESOURCE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE,
|
|
|
|
RESOURCE_ACCESS_TRANSFER_WRITE,
|
|
|
|
RESOURCE_ACCESS_HOST_WRITE,
|
|
|
|
|
|
|
|
/* Read-Writes */
|
|
|
|
RESOURCE_ACCESS_COLOR_ATTACHMENT_READ_WRITE,
|
|
|
|
RESOURCE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_WRITE,
|
|
|
|
RESOURCE_ACCESS_MEMORY_TRANSFER_READ_WRITE,
|
|
|
|
RESOURCE_ACCESS_GENERAL,
|
|
|
|
|
|
|
|
/* Count */
|
|
|
|
RESOURCE_ACCESS_TYPES_COUNT
|
|
|
|
} VulkanResourceAccessType;
|
|
|
|
|
2020-12-17 03:50:31 +00:00
|
|
|
typedef enum CreateSwapchainResult
|
|
|
|
{
|
|
|
|
CREATE_SWAPCHAIN_FAIL,
|
|
|
|
CREATE_SWAPCHAIN_SUCCESS,
|
|
|
|
CREATE_SWAPCHAIN_SURFACE_ZERO,
|
|
|
|
} CreateSwapchainResult;
|
|
|
|
|
2020-12-17 03:28:02 +00:00
|
|
|
/* Structures */
|
|
|
|
|
2020-12-17 02:38:22 +00:00
|
|
|
typedef struct QueueFamilyIndices
|
|
|
|
{
|
|
|
|
uint32_t graphicsFamily;
|
|
|
|
uint32_t presentFamily;
|
|
|
|
} QueueFamilyIndices;
|
|
|
|
|
2020-12-17 03:28:02 +00:00
|
|
|
typedef struct SwapChainSupportDetails
|
|
|
|
{
|
|
|
|
VkSurfaceCapabilitiesKHR capabilities;
|
|
|
|
VkSurfaceFormatKHR *formats;
|
|
|
|
uint32_t formatsLength;
|
|
|
|
VkPresentModeKHR *presentModes;
|
|
|
|
uint32_t presentModesLength;
|
|
|
|
} SwapChainSupportDetails;
|
|
|
|
|
|
|
|
typedef struct VulkanRenderer
|
2020-12-17 01:23:49 +00:00
|
|
|
{
|
2020-12-17 02:12:20 +00:00
|
|
|
VkInstance instance;
|
2020-12-17 02:38:22 +00:00
|
|
|
VkPhysicalDevice physicalDevice;
|
|
|
|
VkPhysicalDeviceProperties2 physicalDeviceProperties;
|
|
|
|
VkPhysicalDeviceDriverPropertiesKHR physicalDeviceDriverProperties;
|
|
|
|
VkDevice logicalDevice;
|
|
|
|
|
|
|
|
void* deviceWindowHandle;
|
|
|
|
|
2020-12-17 03:28:02 +00:00
|
|
|
uint8_t supportsDebugUtils;
|
|
|
|
uint8_t debugMode;
|
2020-12-17 03:50:31 +00:00
|
|
|
uint8_t headless;
|
2020-12-17 03:28:02 +00:00
|
|
|
|
2020-12-17 03:50:31 +00:00
|
|
|
REFRESH_PresentMode presentMode;
|
2020-12-17 03:28:02 +00:00
|
|
|
VkSurfaceKHR surface;
|
2020-12-17 03:50:31 +00:00
|
|
|
VkSwapchainKHR swapChain;
|
|
|
|
VkFormat swapChainFormat;
|
|
|
|
VkComponentMapping swapChainSwizzle;
|
|
|
|
VkImage *swapChainImages;
|
|
|
|
VkImageView *swapChainImageViews;
|
2020-12-17 03:28:02 +00:00
|
|
|
VulkanResourceAccessType *swapChainResourceAccessTypes;
|
2020-12-17 03:50:31 +00:00
|
|
|
uint32_t swapChainImageCount;
|
|
|
|
VkExtent2D swapChainExtent;
|
2020-12-17 03:28:02 +00:00
|
|
|
|
2020-12-17 02:38:22 +00:00
|
|
|
QueueFamilyIndices queueFamilyIndices;
|
|
|
|
VkQueue graphicsQueue;
|
|
|
|
VkQueue presentQueue;
|
|
|
|
|
2020-12-17 04:04:47 +00:00
|
|
|
VkFence inFlightFence;
|
|
|
|
VkSemaphore imageAvailableSemaphore;
|
|
|
|
VkSemaphore renderFinishedSemaphore;
|
|
|
|
|
2020-12-17 02:38:22 +00:00
|
|
|
VkCommandPool commandPool;
|
|
|
|
VkCommandBuffer *inactiveCommandBuffers;
|
|
|
|
VkCommandBuffer *activeCommandBuffers;
|
|
|
|
VkCommandBuffer *submittedCommandBuffers;
|
|
|
|
uint32_t inactiveCommandBufferCount;
|
|
|
|
uint32_t activeCommandBufferCount;
|
|
|
|
uint32_t submittedCommandBufferCount;
|
|
|
|
uint32_t allocatedCommandBufferCount;
|
|
|
|
uint32_t currentCommandCount;
|
|
|
|
VkCommandBuffer currentCommandBuffer;
|
|
|
|
uint32_t numActiveCommands;
|
|
|
|
|
|
|
|
#define VULKAN_INSTANCE_FUNCTION(ext, ret, func, params) \
|
|
|
|
vkfntype_##func func;
|
|
|
|
#define VULKAN_DEVICE_FUNCTION(ext, ret, func, params) \
|
|
|
|
vkfntype_##func func;
|
|
|
|
#include "Refresh_Driver_Vulkan_vkfuncs.h"
|
2020-12-17 03:28:02 +00:00
|
|
|
} VulkanRenderer;
|
|
|
|
|
|
|
|
/* Error Handling */
|
|
|
|
|
|
|
|
static inline const char* VkErrorMessages(VkResult code)
|
|
|
|
{
|
|
|
|
#define ERR_TO_STR(e) \
|
|
|
|
case e: return #e;
|
|
|
|
switch (code)
|
|
|
|
{
|
|
|
|
ERR_TO_STR(VK_ERROR_OUT_OF_HOST_MEMORY)
|
|
|
|
ERR_TO_STR(VK_ERROR_OUT_OF_DEVICE_MEMORY)
|
|
|
|
ERR_TO_STR(VK_ERROR_FRAGMENTED_POOL)
|
|
|
|
ERR_TO_STR(VK_ERROR_OUT_OF_POOL_MEMORY)
|
|
|
|
ERR_TO_STR(VK_ERROR_INITIALIZATION_FAILED)
|
|
|
|
ERR_TO_STR(VK_ERROR_LAYER_NOT_PRESENT)
|
|
|
|
ERR_TO_STR(VK_ERROR_EXTENSION_NOT_PRESENT)
|
|
|
|
ERR_TO_STR(VK_ERROR_FEATURE_NOT_PRESENT)
|
|
|
|
ERR_TO_STR(VK_ERROR_TOO_MANY_OBJECTS)
|
|
|
|
ERR_TO_STR(VK_ERROR_DEVICE_LOST)
|
|
|
|
ERR_TO_STR(VK_ERROR_INCOMPATIBLE_DRIVER)
|
|
|
|
ERR_TO_STR(VK_ERROR_OUT_OF_DATE_KHR)
|
|
|
|
ERR_TO_STR(VK_ERROR_SURFACE_LOST_KHR)
|
|
|
|
ERR_TO_STR(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT)
|
|
|
|
ERR_TO_STR(VK_SUBOPTIMAL_KHR)
|
|
|
|
default: return "Unhandled VkResult!";
|
|
|
|
}
|
|
|
|
#undef ERR_TO_STR
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void LogVulkanResult(
|
|
|
|
const char* vulkanFunctionName,
|
|
|
|
VkResult result
|
|
|
|
) {
|
|
|
|
if (result != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
REFRESH_LogError(
|
|
|
|
"%s: %s",
|
|
|
|
vulkanFunctionName,
|
|
|
|
VkErrorMessages(result)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2020-12-17 01:23:49 +00:00
|
|
|
|
2020-12-17 04:04:47 +00:00
|
|
|
/* Command Buffers */
|
|
|
|
|
|
|
|
/* Vulkan: Command Buffers */
|
|
|
|
|
|
|
|
static void VULKAN_INTERNAL_BeginCommandBuffer(VulkanRenderer *renderer)
|
|
|
|
{
|
|
|
|
VkCommandBufferAllocateInfo allocateInfo;
|
|
|
|
VkCommandBufferBeginInfo beginInfo;
|
|
|
|
VkResult result;
|
|
|
|
|
|
|
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
|
|
beginInfo.pNext = NULL;
|
|
|
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
|
|
beginInfo.pInheritanceInfo = NULL;
|
|
|
|
|
|
|
|
/* If we are out of unused command buffers, allocate some more */
|
|
|
|
if (renderer->inactiveCommandBufferCount == 0)
|
|
|
|
{
|
|
|
|
renderer->activeCommandBuffers = SDL_realloc(
|
|
|
|
renderer->activeCommandBuffers,
|
|
|
|
sizeof(VkCommandBuffer) * renderer->allocatedCommandBufferCount * 2
|
|
|
|
);
|
|
|
|
|
|
|
|
renderer->inactiveCommandBuffers = SDL_realloc(
|
|
|
|
renderer->inactiveCommandBuffers,
|
|
|
|
sizeof(VkCommandBuffer) * renderer->allocatedCommandBufferCount * 2
|
|
|
|
);
|
|
|
|
|
|
|
|
renderer->submittedCommandBuffers = SDL_realloc(
|
|
|
|
renderer->submittedCommandBuffers,
|
|
|
|
sizeof(VkCommandBuffer) * renderer->allocatedCommandBufferCount * 2
|
|
|
|
);
|
|
|
|
|
|
|
|
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
|
|
allocateInfo.pNext = NULL;
|
|
|
|
allocateInfo.commandPool = renderer->commandPool;
|
|
|
|
allocateInfo.commandBufferCount = renderer->allocatedCommandBufferCount;
|
|
|
|
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
|
|
|
|
|
|
result = renderer->vkAllocateCommandBuffers(
|
|
|
|
renderer->logicalDevice,
|
|
|
|
&allocateInfo,
|
|
|
|
renderer->inactiveCommandBuffers
|
|
|
|
);
|
|
|
|
|
|
|
|
if (result != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
LogVulkanResult("vkAllocateCommandBuffers", result);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer->inactiveCommandBufferCount = renderer->allocatedCommandBufferCount;
|
|
|
|
renderer->allocatedCommandBufferCount *= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer->currentCommandBuffer =
|
|
|
|
renderer->inactiveCommandBuffers[renderer->inactiveCommandBufferCount - 1];
|
|
|
|
|
|
|
|
renderer->activeCommandBuffers[renderer->activeCommandBufferCount] = renderer->currentCommandBuffer;
|
|
|
|
|
|
|
|
renderer->activeCommandBufferCount += 1;
|
|
|
|
renderer->inactiveCommandBufferCount -= 1;
|
|
|
|
|
|
|
|
result = renderer->vkBeginCommandBuffer(
|
|
|
|
renderer->currentCommandBuffer,
|
|
|
|
&beginInfo
|
|
|
|
);
|
|
|
|
|
|
|
|
if (result != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
LogVulkanResult("vkBeginCommandBuffer", result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Public API */
|
|
|
|
|
2020-12-17 01:23:49 +00:00
|
|
|
static void VULKAN_DestroyDevice(
|
|
|
|
REFRESH_Device *device
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_Clear(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_ClearOptions options,
|
|
|
|
REFRESH_Vec4 **colors,
|
|
|
|
uint32_t colorCount,
|
|
|
|
float depth,
|
|
|
|
int32_t stencil
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_DrawIndexedPrimitives(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_PrimitiveType primitiveType,
|
|
|
|
uint32_t baseVertex,
|
|
|
|
uint32_t minVertexIndex,
|
|
|
|
uint32_t numVertices,
|
|
|
|
uint32_t startIndex,
|
|
|
|
uint32_t primitiveCount,
|
|
|
|
REFRESH_Buffer *indices,
|
|
|
|
REFRESH_IndexElementSize indexElementSize
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_DrawInstancedPrimitives(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_PrimitiveType primitiveType,
|
|
|
|
uint32_t baseVertex,
|
|
|
|
uint32_t minVertexIndex,
|
|
|
|
uint32_t numVertices,
|
|
|
|
uint32_t startIndex,
|
|
|
|
uint32_t primitiveCount,
|
|
|
|
uint32_t instanceCount,
|
|
|
|
REFRESH_Buffer *indices,
|
|
|
|
REFRESH_IndexElementSize indexElementSize
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_DrawPrimitives(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_PrimitiveType primitiveType,
|
|
|
|
uint32_t vertexStart,
|
|
|
|
uint32_t primitiveCount
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static REFRESH_RenderPass* VULKAN_CreateRenderPass(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_RenderPassCreateInfo *renderPassCreateInfo
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static REFRESH_GraphicsPipeline* VULKAN_CreateGraphicsPipeline(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_GraphicsPipelineCreateInfo *pipelineCreateInfo
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static REFRESH_Sampler* VULKAN_CreateSampler(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_SamplerStateCreateInfo *samplerStateCreateInfo
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static REFRESH_Framebuffer* VULKAN_CreateFramebuffer(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_FramebufferCreateInfo *framebufferCreateInfo
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static REFRESH_ShaderModule* VULKAN_CreateShaderModule(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_ShaderModuleCreateInfo *shaderModuleCreateInfo
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static REFRESH_Texture* VULKAN_CreateTexture2D(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_SurfaceFormat format,
|
|
|
|
uint32_t width,
|
|
|
|
uint32_t height,
|
|
|
|
uint32_t levelCount
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static REFRESH_Texture* VULKAN_CreateTexture3D(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_SurfaceFormat format,
|
|
|
|
uint32_t width,
|
|
|
|
uint32_t height,
|
|
|
|
uint32_t depth,
|
|
|
|
uint32_t levelCount
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static REFRESH_Texture* VULKAN_CreateTextureCube(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_SurfaceFormat format,
|
|
|
|
uint32_t size,
|
|
|
|
uint32_t levelCount
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static REFRESH_ColorTarget* VULKAN_GenColorTarget(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
uint32_t width,
|
|
|
|
uint32_t height,
|
|
|
|
REFRESH_SurfaceFormat format,
|
|
|
|
uint32_t multisampleCount,
|
|
|
|
REFRESH_Texture *texture
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static REFRESH_DepthStencilTarget* VULKAN_GenDepthStencilTarget(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
uint32_t width,
|
|
|
|
uint32_t height,
|
|
|
|
REFRESH_DepthFormat format,
|
|
|
|
REFRESH_Texture *texture
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static REFRESH_Buffer* VULKAN_GenVertexBuffer(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
uint32_t sizeInBytes
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static REFRESH_Buffer* VULKAN_GenIndexBuffer(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
uint32_t sizeInBytes
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static REFRESH_Buffer* VULKAN_GenShaderParamBuffer(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
uint32_t sizeInBytes
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_SetTextureData2D(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Texture *texture,
|
|
|
|
uint32_t x,
|
|
|
|
uint32_t y,
|
|
|
|
uint32_t w,
|
|
|
|
uint32_t h,
|
|
|
|
uint32_t level,
|
|
|
|
void *data,
|
|
|
|
uint32_t dataLengthInBytes
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_SetTextureData3D(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Texture *texture,
|
|
|
|
uint32_t x,
|
|
|
|
uint32_t y,
|
|
|
|
uint32_t z,
|
|
|
|
uint32_t w,
|
|
|
|
uint32_t h,
|
|
|
|
uint32_t d,
|
|
|
|
uint32_t level,
|
|
|
|
void* data,
|
|
|
|
uint32_t dataLength
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_SetTextureDataCube(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Texture *texture,
|
|
|
|
uint32_t x,
|
|
|
|
uint32_t y,
|
|
|
|
uint32_t w,
|
|
|
|
uint32_t h,
|
|
|
|
REFRESH_CubeMapFace cubeMapFace,
|
|
|
|
uint32_t level,
|
|
|
|
void* data,
|
|
|
|
uint32_t dataLength
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_SetTextureDataYUV(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Texture *y,
|
|
|
|
REFRESH_Texture *u,
|
|
|
|
REFRESH_Texture *v,
|
|
|
|
uint32_t yWidth,
|
|
|
|
uint32_t yHeight,
|
|
|
|
uint32_t uvWidth,
|
|
|
|
uint32_t uvHeight,
|
|
|
|
void* data,
|
|
|
|
uint32_t dataLength
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_SetVertexBufferData(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Buffer *buffer,
|
|
|
|
uint32_t offsetInBytes,
|
|
|
|
void* data,
|
|
|
|
uint32_t elementCount,
|
|
|
|
uint32_t elementSizeInBytes
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_SetIndexBufferData(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Buffer *buffer,
|
|
|
|
uint32_t offsetInBytes,
|
|
|
|
void* data,
|
|
|
|
uint32_t dataLength
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_SetShaderParamData(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Buffer *shaderParamBuffer,
|
|
|
|
uint32_t offsetInBytes,
|
|
|
|
void *data,
|
|
|
|
uint32_t elementCount,
|
|
|
|
uint32_t elementSizeInBytes
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_SetVertexSamplers(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
uint32_t startIndex,
|
|
|
|
REFRESH_Texture *pTextures,
|
|
|
|
REFRESH_Sampler *pSamplers,
|
|
|
|
uint32_t count
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_SetFragmentSamplers(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
uint32_t startIndex,
|
|
|
|
REFRESH_Texture *pTextures,
|
|
|
|
REFRESH_Sampler *pSamplers,
|
|
|
|
uint32_t count
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_GetTextureData2D(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Texture *texture,
|
|
|
|
uint32_t x,
|
|
|
|
uint32_t y,
|
|
|
|
uint32_t w,
|
|
|
|
uint32_t h,
|
|
|
|
uint32_t level,
|
|
|
|
void* data,
|
|
|
|
uint32_t dataLength
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_GetTextureDataCube(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Texture *texture,
|
|
|
|
uint32_t x,
|
|
|
|
uint32_t y,
|
|
|
|
uint32_t w,
|
|
|
|
uint32_t h,
|
|
|
|
REFRESH_CubeMapFace cubeMapFace,
|
|
|
|
uint32_t level,
|
|
|
|
void* data,
|
|
|
|
uint32_t dataLength
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_AddDisposeTexture(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Texture *texture
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_AddDisposeSampler(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Sampler *sampler
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_AddDisposeVertexBuffer(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Buffer *buffer
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_AddDisposeIndexBuffer(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Buffer *buffer
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_AddDisposeShaderParamBuffer(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Buffer *buffer
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_AddDisposeColorTarget(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_ColorTarget *colorTarget
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_AddDisposeDepthStencilTarget(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_DepthStencilTarget *depthStencilTarget
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_AddDisposeFramebuffer(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Framebuffer *frameBuffer
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_AddDisposeShaderModule(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_ShaderModule *shaderModule
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_AddDisposeRenderPass(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_RenderPass *renderPass
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_AddDisposeGraphicsPipeline(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_GraphicsPipeline *graphicsPipeline
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_BeginRenderPass(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_RenderPass *renderPass,
|
|
|
|
REFRESH_Framebuffer *framebuffer,
|
|
|
|
REFRESH_Rect renderArea,
|
|
|
|
REFRESH_ClearValue *pClearValues,
|
|
|
|
uint32_t clearCount
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_EndRenderPass(
|
|
|
|
REFRESH_Renderer *renderer
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VULKAN_BindGraphicsPipeline(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_GraphicsPipeline *graphicsPipeline
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
2020-12-17 04:19:11 +00:00
|
|
|
static void VULKAN_Present(
|
|
|
|
REFRESH_Renderer *renderer,
|
|
|
|
REFRESH_Rect *sourceRectangle,
|
|
|
|
REFRESH_Rect *destinationRectangle
|
|
|
|
) {
|
|
|
|
SDL_assert(0);
|
|
|
|
}
|
|
|
|
|
2020-12-17 03:50:31 +00:00
|
|
|
/* Swapchain */
|
|
|
|
|
|
|
|
static inline VkExtent2D VULKAN_INTERNAL_ChooseSwapExtent(
|
|
|
|
void* windowHandle,
|
|
|
|
const VkSurfaceCapabilitiesKHR capabilities
|
|
|
|
) {
|
|
|
|
VkExtent2D actualExtent;
|
|
|
|
int32_t drawableWidth, drawableHeight;
|
|
|
|
|
|
|
|
if (capabilities.currentExtent.width != UINT32_MAX)
|
|
|
|
{
|
|
|
|
return capabilities.currentExtent;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SDL_Vulkan_GetDrawableSize(
|
|
|
|
(SDL_Window*) windowHandle,
|
|
|
|
&drawableWidth,
|
|
|
|
&drawableHeight
|
|
|
|
);
|
|
|
|
|
|
|
|
actualExtent.width = drawableWidth;
|
|
|
|
actualExtent.height = drawableHeight;
|
|
|
|
|
|
|
|
return actualExtent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t VULKAN_INTERNAL_QuerySwapChainSupport(
|
|
|
|
VulkanRenderer *renderer,
|
|
|
|
VkPhysicalDevice physicalDevice,
|
|
|
|
VkSurfaceKHR surface,
|
|
|
|
SwapChainSupportDetails *outputDetails
|
|
|
|
) {
|
|
|
|
VkResult result;
|
|
|
|
uint32_t formatCount;
|
|
|
|
uint32_t presentModeCount;
|
|
|
|
|
|
|
|
result = renderer->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
|
|
|
|
physicalDevice,
|
|
|
|
surface,
|
|
|
|
&outputDetails->capabilities
|
|
|
|
);
|
|
|
|
if (result != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
REFRESH_LogError(
|
|
|
|
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR: %s",
|
|
|
|
VkErrorMessages(result)
|
|
|
|
);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer->vkGetPhysicalDeviceSurfaceFormatsKHR(
|
|
|
|
physicalDevice,
|
|
|
|
surface,
|
|
|
|
&formatCount,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
|
|
|
|
if (formatCount != 0)
|
|
|
|
{
|
|
|
|
outputDetails->formats = (VkSurfaceFormatKHR*) SDL_malloc(
|
|
|
|
sizeof(VkSurfaceFormatKHR) * formatCount
|
|
|
|
);
|
|
|
|
outputDetails->formatsLength = formatCount;
|
|
|
|
|
|
|
|
if (!outputDetails->formats)
|
|
|
|
{
|
|
|
|
SDL_OutOfMemory();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = renderer->vkGetPhysicalDeviceSurfaceFormatsKHR(
|
|
|
|
physicalDevice,
|
|
|
|
surface,
|
|
|
|
&formatCount,
|
|
|
|
outputDetails->formats
|
|
|
|
);
|
|
|
|
if (result != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
REFRESH_LogError(
|
|
|
|
"vkGetPhysicalDeviceSurfaceFormatsKHR: %s",
|
|
|
|
VkErrorMessages(result)
|
|
|
|
);
|
|
|
|
|
|
|
|
SDL_free(outputDetails->formats);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer->vkGetPhysicalDeviceSurfacePresentModesKHR(
|
|
|
|
physicalDevice,
|
|
|
|
surface,
|
|
|
|
&presentModeCount,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
|
|
|
|
if (presentModeCount != 0)
|
|
|
|
{
|
|
|
|
outputDetails->presentModes = (VkPresentModeKHR*) SDL_malloc(
|
|
|
|
sizeof(VkPresentModeKHR) * presentModeCount
|
|
|
|
);
|
|
|
|
outputDetails->presentModesLength = presentModeCount;
|
|
|
|
|
|
|
|
if (!outputDetails->presentModes)
|
|
|
|
{
|
|
|
|
SDL_OutOfMemory();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = renderer->vkGetPhysicalDeviceSurfacePresentModesKHR(
|
|
|
|
physicalDevice,
|
|
|
|
surface,
|
|
|
|
&presentModeCount,
|
|
|
|
outputDetails->presentModes
|
|
|
|
);
|
|
|
|
if (result != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
REFRESH_LogError(
|
|
|
|
"vkGetPhysicalDeviceSurfacePresentModesKHR: %s",
|
|
|
|
VkErrorMessages(result)
|
|
|
|
);
|
|
|
|
|
|
|
|
SDL_free(outputDetails->formats);
|
|
|
|
SDL_free(outputDetails->presentModes);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t VULKAN_INTERNAL_ChooseSwapSurfaceFormat(
|
|
|
|
VkFormat desiredFormat,
|
|
|
|
VkSurfaceFormatKHR *availableFormats,
|
|
|
|
uint32_t availableFormatsLength,
|
|
|
|
VkSurfaceFormatKHR *outputFormat
|
|
|
|
) {
|
|
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < availableFormatsLength; i += 1)
|
|
|
|
{
|
|
|
|
if ( availableFormats[i].format == desiredFormat &&
|
|
|
|
availableFormats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR )
|
|
|
|
{
|
|
|
|
*outputFormat = availableFormats[i];
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
REFRESH_LogError("Desired surface format is unavailable.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t VULKAN_INTERNAL_ChooseSwapPresentMode(
|
|
|
|
REFRESH_PresentMode desiredPresentInterval,
|
|
|
|
VkPresentModeKHR *availablePresentModes,
|
|
|
|
uint32_t availablePresentModesLength,
|
|
|
|
VkPresentModeKHR *outputPresentMode
|
|
|
|
) {
|
|
|
|
#define CHECK_MODE(m) \
|
|
|
|
for (i = 0; i < availablePresentModesLength; i += 1) \
|
|
|
|
{ \
|
|
|
|
if (availablePresentModes[i] == m) \
|
|
|
|
{ \
|
|
|
|
*outputPresentMode = m; \
|
|
|
|
REFRESH_LogInfo("Using " #m "!"); \
|
|
|
|
return 1; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
REFRESH_LogInfo(#m " unsupported.");
|
|
|
|
|
|
|
|
uint32_t i;
|
|
|
|
if (desiredPresentInterval == REFRESH_PRESENTMODE_IMMEDIATE)
|
|
|
|
{
|
|
|
|
CHECK_MODE(VK_PRESENT_MODE_IMMEDIATE_KHR)
|
|
|
|
}
|
|
|
|
else if (desiredPresentInterval == REFRESH_PRESENTMODE_MAILBOX)
|
|
|
|
{
|
|
|
|
CHECK_MODE(VK_PRESENT_MODE_MAILBOX_KHR)
|
|
|
|
}
|
|
|
|
else if (desiredPresentInterval == REFRESH_PRESENTMODE_FIFO)
|
|
|
|
{
|
|
|
|
CHECK_MODE(VK_PRESENT_MODE_FIFO_KHR)
|
|
|
|
}
|
|
|
|
else if (desiredPresentInterval == REFRESH_PRESENTMODE_FIFO_RELAXED)
|
|
|
|
{
|
|
|
|
CHECK_MODE(VK_PRESENT_MODE_FIFO_RELAXED_KHR)
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
REFRESH_LogError(
|
|
|
|
"Unrecognized PresentInterval: %d",
|
|
|
|
desiredPresentInterval
|
|
|
|
);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef CHECK_MODE
|
|
|
|
|
|
|
|
REFRESH_LogInfo("Fall back to VK_PRESENT_MODE_FIFO_KHR.");
|
|
|
|
*outputPresentMode = VK_PRESENT_MODE_FIFO_KHR;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CreateSwapchainResult VULKAN_INTERNAL_CreateSwapchain(
|
|
|
|
VulkanRenderer *renderer
|
|
|
|
) {
|
|
|
|
VkResult vulkanResult;
|
|
|
|
SwapChainSupportDetails swapChainSupportDetails;
|
|
|
|
VkSurfaceFormatKHR surfaceFormat;
|
|
|
|
VkPresentModeKHR presentMode;
|
|
|
|
VkExtent2D extent;
|
|
|
|
uint32_t imageCount, swapChainImageCount, i;
|
|
|
|
VkSwapchainCreateInfoKHR swapChainCreateInfo;
|
|
|
|
VkImage *swapChainImages;
|
|
|
|
VkImageViewCreateInfo createInfo;
|
|
|
|
VkImageView swapChainImageView;
|
|
|
|
|
|
|
|
if (!VULKAN_INTERNAL_QuerySwapChainSupport(
|
|
|
|
renderer,
|
|
|
|
renderer->physicalDevice,
|
|
|
|
renderer->surface,
|
|
|
|
&swapChainSupportDetails
|
|
|
|
)) {
|
|
|
|
REFRESH_LogError("Device does not support swap chain creation");
|
|
|
|
return CREATE_SWAPCHAIN_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer->swapChainFormat = VK_FORMAT_B8G8R8A8_UNORM;
|
|
|
|
renderer->swapChainSwizzle.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
|
|
renderer->swapChainSwizzle.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
|
|
renderer->swapChainSwizzle.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
|
|
renderer->swapChainSwizzle.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
|
|
if (!VULKAN_INTERNAL_ChooseSwapSurfaceFormat(
|
|
|
|
renderer->swapChainFormat,
|
|
|
|
swapChainSupportDetails.formats,
|
|
|
|
swapChainSupportDetails.formatsLength,
|
|
|
|
&surfaceFormat
|
|
|
|
)) {
|
|
|
|
SDL_free(swapChainSupportDetails.formats);
|
|
|
|
SDL_free(swapChainSupportDetails.presentModes);
|
|
|
|
REFRESH_LogError("Device does not support swap chain format");
|
|
|
|
return CREATE_SWAPCHAIN_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!VULKAN_INTERNAL_ChooseSwapPresentMode(
|
|
|
|
renderer->presentMode,
|
|
|
|
swapChainSupportDetails.presentModes,
|
|
|
|
swapChainSupportDetails.presentModesLength,
|
|
|
|
&presentMode
|
|
|
|
)) {
|
|
|
|
SDL_free(swapChainSupportDetails.formats);
|
|
|
|
SDL_free(swapChainSupportDetails.presentModes);
|
|
|
|
REFRESH_LogError("Device does not support swap chain present mode");
|
|
|
|
return CREATE_SWAPCHAIN_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
extent = VULKAN_INTERNAL_ChooseSwapExtent(
|
|
|
|
renderer->deviceWindowHandle,
|
|
|
|
swapChainSupportDetails.capabilities
|
|
|
|
);
|
|
|
|
|
|
|
|
if (extent.width == 0 || extent.height == 0)
|
|
|
|
{
|
|
|
|
return CREATE_SWAPCHAIN_SURFACE_ZERO;
|
|
|
|
}
|
|
|
|
|
|
|
|
imageCount = swapChainSupportDetails.capabilities.minImageCount + 1;
|
|
|
|
|
|
|
|
if ( swapChainSupportDetails.capabilities.maxImageCount > 0 &&
|
|
|
|
imageCount > swapChainSupportDetails.capabilities.maxImageCount )
|
|
|
|
{
|
|
|
|
imageCount = swapChainSupportDetails.capabilities.maxImageCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (presentMode == VK_PRESENT_MODE_MAILBOX_KHR)
|
|
|
|
{
|
|
|
|
/* Required for proper triple-buffering.
|
|
|
|
* Note that this is below the above maxImageCount check!
|
|
|
|
* If the driver advertises MAILBOX but does not support 3 swap
|
|
|
|
* images, it's not real mailbox support, so let it fail hard.
|
|
|
|
* -flibit
|
|
|
|
*/
|
|
|
|
imageCount = SDL_max(imageCount, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
swapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
|
|
|
swapChainCreateInfo.pNext = NULL;
|
|
|
|
swapChainCreateInfo.flags = 0;
|
|
|
|
swapChainCreateInfo.surface = renderer->surface;
|
|
|
|
swapChainCreateInfo.minImageCount = imageCount;
|
|
|
|
swapChainCreateInfo.imageFormat = surfaceFormat.format;
|
|
|
|
swapChainCreateInfo.imageColorSpace = surfaceFormat.colorSpace;
|
|
|
|
swapChainCreateInfo.imageExtent = extent;
|
|
|
|
swapChainCreateInfo.imageArrayLayers = 1;
|
|
|
|
swapChainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
|
|
swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
swapChainCreateInfo.queueFamilyIndexCount = 0;
|
|
|
|
swapChainCreateInfo.pQueueFamilyIndices = NULL;
|
|
|
|
swapChainCreateInfo.preTransform = swapChainSupportDetails.capabilities.currentTransform;
|
|
|
|
swapChainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
|
|
|
swapChainCreateInfo.presentMode = presentMode;
|
|
|
|
swapChainCreateInfo.clipped = VK_TRUE;
|
|
|
|
swapChainCreateInfo.oldSwapchain = VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
vulkanResult = renderer->vkCreateSwapchainKHR(
|
|
|
|
renderer->logicalDevice,
|
|
|
|
&swapChainCreateInfo,
|
|
|
|
NULL,
|
|
|
|
&renderer->swapChain
|
|
|
|
);
|
|
|
|
|
|
|
|
SDL_free(swapChainSupportDetails.formats);
|
|
|
|
SDL_free(swapChainSupportDetails.presentModes);
|
|
|
|
|
|
|
|
if (vulkanResult != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
LogVulkanResult("vkCreateSwapchainKHR", vulkanResult);
|
|
|
|
|
|
|
|
return CREATE_SWAPCHAIN_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer->vkGetSwapchainImagesKHR(
|
|
|
|
renderer->logicalDevice,
|
|
|
|
renderer->swapChain,
|
|
|
|
&swapChainImageCount,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
|
|
|
|
renderer->swapChainImages = (VkImage*) SDL_malloc(
|
|
|
|
sizeof(VkImage) * swapChainImageCount
|
|
|
|
);
|
|
|
|
if (!renderer->swapChainImages)
|
|
|
|
{
|
|
|
|
SDL_OutOfMemory();
|
|
|
|
return CREATE_SWAPCHAIN_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer->swapChainImageViews = (VkImageView*) SDL_malloc(
|
|
|
|
sizeof(VkImageView) * swapChainImageCount
|
|
|
|
);
|
|
|
|
if (!renderer->swapChainImageViews)
|
|
|
|
{
|
|
|
|
SDL_OutOfMemory();
|
|
|
|
return CREATE_SWAPCHAIN_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer->swapChainResourceAccessTypes = (VulkanResourceAccessType*) SDL_malloc(
|
|
|
|
sizeof(VulkanResourceAccessType) * swapChainImageCount
|
|
|
|
);
|
|
|
|
if (!renderer->swapChainResourceAccessTypes)
|
|
|
|
{
|
|
|
|
SDL_OutOfMemory();
|
|
|
|
return CREATE_SWAPCHAIN_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
swapChainImages = SDL_stack_alloc(VkImage, swapChainImageCount);
|
|
|
|
renderer->vkGetSwapchainImagesKHR(
|
|
|
|
renderer->logicalDevice,
|
|
|
|
renderer->swapChain,
|
|
|
|
&swapChainImageCount,
|
|
|
|
swapChainImages
|
|
|
|
);
|
|
|
|
renderer->swapChainImageCount = swapChainImageCount;
|
|
|
|
renderer->swapChainExtent = extent;
|
|
|
|
|
|
|
|
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
|
|
createInfo.pNext = NULL;
|
|
|
|
createInfo.flags = 0;
|
|
|
|
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
createInfo.format = surfaceFormat.format;
|
|
|
|
createInfo.components = renderer->swapChainSwizzle;
|
|
|
|
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
createInfo.subresourceRange.baseMipLevel = 0;
|
|
|
|
createInfo.subresourceRange.levelCount = 1;
|
|
|
|
createInfo.subresourceRange.baseArrayLayer = 0;
|
|
|
|
createInfo.subresourceRange.layerCount = 1;
|
|
|
|
for (i = 0; i < swapChainImageCount; i += 1)
|
|
|
|
{
|
|
|
|
createInfo.image = swapChainImages[i];
|
|
|
|
|
|
|
|
vulkanResult = renderer->vkCreateImageView(
|
|
|
|
renderer->logicalDevice,
|
|
|
|
&createInfo,
|
|
|
|
NULL,
|
|
|
|
&swapChainImageView
|
|
|
|
);
|
|
|
|
|
|
|
|
if (vulkanResult != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
LogVulkanResult("vkCreateImageView", vulkanResult);
|
|
|
|
SDL_stack_free(swapChainImages);
|
|
|
|
return CREATE_SWAPCHAIN_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer->swapChainImages[i] = swapChainImages[i];
|
|
|
|
renderer->swapChainImageViews[i] = swapChainImageView;
|
|
|
|
renderer->swapChainResourceAccessTypes[i] = RESOURCE_ACCESS_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_stack_free(swapChainImages);
|
|
|
|
return CREATE_SWAPCHAIN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2020-12-17 03:28:02 +00:00
|
|
|
/* Device instantiation */
|
|
|
|
|
|
|
|
static inline uint8_t VULKAN_INTERNAL_SupportsExtension(
|
|
|
|
const char *ext,
|
|
|
|
VkExtensionProperties *availableExtensions,
|
|
|
|
uint32_t numAvailableExtensions
|
|
|
|
) {
|
|
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < numAvailableExtensions; i += 1)
|
|
|
|
{
|
|
|
|
if (SDL_strcmp(ext, availableExtensions[i].extensionName) == 0)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t VULKAN_INTERNAL_CheckInstanceExtensions(
|
|
|
|
const char **requiredExtensions,
|
|
|
|
uint32_t requiredExtensionsLength,
|
|
|
|
uint8_t *supportsDebugUtils
|
|
|
|
) {
|
|
|
|
uint32_t extensionCount, i;
|
|
|
|
VkExtensionProperties *availableExtensions;
|
|
|
|
uint8_t allExtensionsSupported = 1;
|
|
|
|
|
|
|
|
vkEnumerateInstanceExtensionProperties(
|
|
|
|
NULL,
|
|
|
|
&extensionCount,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
availableExtensions = SDL_stack_alloc(
|
|
|
|
VkExtensionProperties,
|
|
|
|
extensionCount
|
|
|
|
);
|
|
|
|
vkEnumerateInstanceExtensionProperties(
|
|
|
|
NULL,
|
|
|
|
&extensionCount,
|
|
|
|
availableExtensions
|
|
|
|
);
|
|
|
|
|
|
|
|
for (i = 0; i < requiredExtensionsLength; i += 1)
|
|
|
|
{
|
|
|
|
if (!VULKAN_INTERNAL_SupportsExtension(
|
|
|
|
requiredExtensions[i],
|
|
|
|
availableExtensions,
|
|
|
|
extensionCount
|
|
|
|
)) {
|
|
|
|
allExtensionsSupported = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is optional, but nice to have! */
|
|
|
|
*supportsDebugUtils = VULKAN_INTERNAL_SupportsExtension(
|
|
|
|
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
|
|
|
|
availableExtensions,
|
|
|
|
extensionCount
|
|
|
|
);
|
|
|
|
|
|
|
|
SDL_stack_free(availableExtensions);
|
|
|
|
return allExtensionsSupported;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t VULKAN_INTERNAL_CheckValidationLayers(
|
|
|
|
const char** validationLayers,
|
|
|
|
uint32_t validationLayersLength
|
|
|
|
) {
|
|
|
|
uint32_t layerCount;
|
|
|
|
VkLayerProperties *availableLayers;
|
|
|
|
uint32_t i, j;
|
|
|
|
uint8_t layerFound;
|
|
|
|
|
|
|
|
vkEnumerateInstanceLayerProperties(&layerCount, NULL);
|
|
|
|
availableLayers = SDL_stack_alloc(VkLayerProperties, layerCount);
|
|
|
|
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers);
|
|
|
|
|
|
|
|
for (i = 0; i < validationLayersLength; i += 1)
|
|
|
|
{
|
|
|
|
layerFound = 0;
|
|
|
|
|
|
|
|
for (j = 0; j < layerCount; j += 1)
|
|
|
|
{
|
|
|
|
if (SDL_strcmp(validationLayers[i], availableLayers[j].layerName) == 0)
|
|
|
|
{
|
|
|
|
layerFound = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!layerFound)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_stack_free(availableLayers);
|
|
|
|
return layerFound;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t VULKAN_INTERNAL_CreateInstance(
|
|
|
|
VulkanRenderer *renderer,
|
2020-12-17 02:12:20 +00:00
|
|
|
void *deviceWindowHandle
|
2020-12-17 03:28:02 +00:00
|
|
|
) {
|
|
|
|
VkResult vulkanResult;
|
|
|
|
VkApplicationInfo appInfo;
|
|
|
|
const char **instanceExtensionNames;
|
|
|
|
uint32_t instanceExtensionCount;
|
|
|
|
VkInstanceCreateInfo createInfo;
|
|
|
|
static const char *layerNames[] = { "VK_LAYER_KHRONOS_validation" };
|
|
|
|
|
|
|
|
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
|
|
|
appInfo.pNext = NULL;
|
|
|
|
appInfo.pApplicationName = NULL;
|
|
|
|
appInfo.applicationVersion = 0;
|
|
|
|
appInfo.pEngineName = "REFRESH";
|
|
|
|
appInfo.engineVersion = REFRESH_COMPILED_VERSION;
|
|
|
|
appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0);
|
|
|
|
|
|
|
|
if (!SDL_Vulkan_GetInstanceExtensions(
|
|
|
|
(SDL_Window*) deviceWindowHandle,
|
|
|
|
&instanceExtensionCount,
|
|
|
|
NULL
|
|
|
|
)) {
|
|
|
|
REFRESH_LogError(
|
|
|
|
"SDL_Vulkan_GetInstanceExtensions(): getExtensionCount: %s",
|
|
|
|
SDL_GetError()
|
|
|
|
);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Extra space for the following extensions:
|
|
|
|
* VK_KHR_get_physical_device_properties2
|
|
|
|
* VK_EXT_debug_utils
|
|
|
|
*/
|
|
|
|
instanceExtensionNames = SDL_stack_alloc(
|
|
|
|
const char*,
|
|
|
|
instanceExtensionCount + 2
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!SDL_Vulkan_GetInstanceExtensions(
|
|
|
|
(SDL_Window*) deviceWindowHandle,
|
|
|
|
&instanceExtensionCount,
|
|
|
|
instanceExtensionNames
|
|
|
|
)) {
|
|
|
|
REFRESH_LogError(
|
|
|
|
"SDL_Vulkan_GetInstanceExtensions(): %s",
|
|
|
|
SDL_GetError()
|
|
|
|
);
|
|
|
|
|
|
|
|
SDL_stack_free((char*) instanceExtensionNames);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Core since 1.1 */
|
|
|
|
instanceExtensionNames[instanceExtensionCount++] =
|
|
|
|
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
|
|
|
|
|
|
|
|
if (!VULKAN_INTERNAL_CheckInstanceExtensions(
|
|
|
|
instanceExtensionNames,
|
|
|
|
instanceExtensionCount,
|
|
|
|
&renderer->supportsDebugUtils
|
|
|
|
)) {
|
|
|
|
REFRESH_LogError(
|
|
|
|
"Required Vulkan instance extensions not supported"
|
|
|
|
);
|
|
|
|
|
|
|
|
SDL_stack_free((char*) instanceExtensionNames);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (renderer->supportsDebugUtils)
|
|
|
|
{
|
|
|
|
/* Append the debug extension to the end */
|
|
|
|
instanceExtensionNames[instanceExtensionCount++] =
|
|
|
|
VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
REFRESH_LogWarn(
|
|
|
|
"%s is not supported!",
|
|
|
|
VK_EXT_DEBUG_UTILS_EXTENSION_NAME
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
|
|
|
createInfo.pNext = NULL;
|
|
|
|
createInfo.flags = 0;
|
|
|
|
createInfo.pApplicationInfo = &appInfo;
|
|
|
|
createInfo.ppEnabledLayerNames = layerNames;
|
|
|
|
createInfo.enabledExtensionCount = instanceExtensionCount;
|
|
|
|
createInfo.ppEnabledExtensionNames = instanceExtensionNames;
|
|
|
|
if (renderer->debugMode)
|
|
|
|
{
|
|
|
|
createInfo.enabledLayerCount = SDL_arraysize(layerNames);
|
|
|
|
if (!VULKAN_INTERNAL_CheckValidationLayers(
|
|
|
|
layerNames,
|
|
|
|
createInfo.enabledLayerCount
|
|
|
|
)) {
|
|
|
|
REFRESH_LogWarn("Validation layers not found, continuing without validation");
|
|
|
|
createInfo.enabledLayerCount = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
createInfo.enabledLayerCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
vulkanResult = vkCreateInstance(&createInfo, NULL, &renderer->instance);
|
|
|
|
if (vulkanResult != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
REFRESH_LogError(
|
|
|
|
"vkCreateInstance failed: %s",
|
|
|
|
VkErrorMessages(vulkanResult)
|
|
|
|
);
|
|
|
|
|
|
|
|
SDL_stack_free((char*) instanceExtensionNames);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_stack_free((char*) instanceExtensionNames);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t VULKAN_INTERNAL_CheckDeviceExtensions(
|
|
|
|
VulkanRenderer *renderer,
|
|
|
|
VkPhysicalDevice physicalDevice,
|
|
|
|
const char** requiredExtensions,
|
|
|
|
uint32_t requiredExtensionsLength
|
|
|
|
) {
|
|
|
|
uint32_t extensionCount, i;
|
|
|
|
VkExtensionProperties *availableExtensions;
|
|
|
|
uint8_t allExtensionsSupported = 1;
|
|
|
|
|
|
|
|
renderer->vkEnumerateDeviceExtensionProperties(
|
|
|
|
physicalDevice,
|
|
|
|
NULL,
|
|
|
|
&extensionCount,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
availableExtensions = SDL_stack_alloc(
|
|
|
|
VkExtensionProperties,
|
|
|
|
extensionCount
|
|
|
|
);
|
|
|
|
renderer->vkEnumerateDeviceExtensionProperties(
|
|
|
|
physicalDevice,
|
|
|
|
NULL,
|
|
|
|
&extensionCount,
|
|
|
|
availableExtensions
|
|
|
|
);
|
|
|
|
|
|
|
|
for (i = 0; i < requiredExtensionsLength; i += 1)
|
|
|
|
{
|
|
|
|
if (!VULKAN_INTERNAL_SupportsExtension(
|
|
|
|
requiredExtensions[i],
|
|
|
|
availableExtensions,
|
|
|
|
extensionCount
|
|
|
|
)) {
|
|
|
|
allExtensionsSupported = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_stack_free(availableExtensions);
|
|
|
|
return allExtensionsSupported;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t VULKAN_INTERNAL_IsDeviceSuitable(
|
|
|
|
VulkanRenderer *renderer,
|
|
|
|
VkPhysicalDevice physicalDevice,
|
|
|
|
const char** requiredExtensionNames,
|
|
|
|
uint32_t requiredExtensionNamesLength,
|
|
|
|
VkSurfaceKHR surface,
|
|
|
|
QueueFamilyIndices *queueFamilyIndices,
|
|
|
|
uint8_t *isIdeal
|
|
|
|
) {
|
|
|
|
uint32_t queueFamilyCount, i;
|
|
|
|
SwapChainSupportDetails swapChainSupportDetails;
|
|
|
|
VkQueueFamilyProperties *queueProps;
|
|
|
|
VkBool32 supportsPresent;
|
|
|
|
uint8_t querySuccess, foundSuitableDevice = 0;
|
|
|
|
VkPhysicalDeviceProperties deviceProperties;
|
|
|
|
|
|
|
|
queueFamilyIndices->graphicsFamily = UINT32_MAX;
|
|
|
|
queueFamilyIndices->presentFamily = UINT32_MAX;
|
|
|
|
*isIdeal = 0;
|
|
|
|
|
|
|
|
/* Note: If no dedicated device exists,
|
|
|
|
* one that supports our features would be fine
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!VULKAN_INTERNAL_CheckDeviceExtensions(
|
|
|
|
renderer,
|
|
|
|
physicalDevice,
|
|
|
|
requiredExtensionNames,
|
|
|
|
requiredExtensionNamesLength
|
|
|
|
)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer->vkGetPhysicalDeviceQueueFamilyProperties(
|
|
|
|
physicalDevice,
|
|
|
|
&queueFamilyCount,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
|
|
|
|
/* FIXME: Need better structure for checking vs storing support details */
|
|
|
|
querySuccess = VULKAN_INTERNAL_QuerySwapChainSupport(
|
|
|
|
renderer,
|
|
|
|
physicalDevice,
|
|
|
|
surface,
|
|
|
|
&swapChainSupportDetails
|
|
|
|
);
|
|
|
|
SDL_free(swapChainSupportDetails.formats);
|
|
|
|
SDL_free(swapChainSupportDetails.presentModes);
|
|
|
|
if ( querySuccess == 0 ||
|
|
|
|
swapChainSupportDetails.formatsLength == 0 ||
|
|
|
|
swapChainSupportDetails.presentModesLength == 0 )
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
queueProps = (VkQueueFamilyProperties*) SDL_stack_alloc(
|
|
|
|
VkQueueFamilyProperties,
|
|
|
|
queueFamilyCount
|
|
|
|
);
|
|
|
|
renderer->vkGetPhysicalDeviceQueueFamilyProperties(
|
|
|
|
physicalDevice,
|
|
|
|
&queueFamilyCount,
|
|
|
|
queueProps
|
|
|
|
);
|
|
|
|
|
|
|
|
for (i = 0; i < queueFamilyCount; i += 1)
|
|
|
|
{
|
|
|
|
renderer->vkGetPhysicalDeviceSurfaceSupportKHR(
|
|
|
|
physicalDevice,
|
|
|
|
i,
|
|
|
|
surface,
|
|
|
|
&supportsPresent
|
|
|
|
);
|
|
|
|
if ( supportsPresent &&
|
|
|
|
(queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0 )
|
|
|
|
{
|
|
|
|
queueFamilyIndices->graphicsFamily = i;
|
|
|
|
queueFamilyIndices->presentFamily = i;
|
|
|
|
foundSuitableDevice = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_stack_free(queueProps);
|
|
|
|
|
|
|
|
if (foundSuitableDevice)
|
|
|
|
{
|
|
|
|
/* We'd really like a discrete GPU, but it's OK either way! */
|
|
|
|
renderer->vkGetPhysicalDeviceProperties(
|
|
|
|
physicalDevice,
|
|
|
|
&deviceProperties
|
|
|
|
);
|
|
|
|
if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
|
|
|
|
{
|
|
|
|
*isIdeal = 1;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This device is useless for us, next! */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t VULKAN_INTERNAL_DeterminePhysicalDevice(
|
|
|
|
VulkanRenderer *renderer,
|
|
|
|
const char **deviceExtensionNames,
|
|
|
|
uint32_t deviceExtensionCount
|
|
|
|
) {
|
|
|
|
VkResult vulkanResult;
|
|
|
|
VkPhysicalDevice *physicalDevices;
|
|
|
|
uint32_t physicalDeviceCount, i, suitableIndex;
|
|
|
|
VkPhysicalDevice physicalDevice;
|
|
|
|
QueueFamilyIndices queueFamilyIndices;
|
|
|
|
uint8_t isIdeal;
|
|
|
|
|
|
|
|
vulkanResult = renderer->vkEnumeratePhysicalDevices(
|
|
|
|
renderer->instance,
|
|
|
|
&physicalDeviceCount,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
|
|
|
|
if (vulkanResult != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
REFRESH_LogError(
|
|
|
|
"vkEnumeratePhysicalDevices failed: %s",
|
|
|
|
VkErrorMessages(vulkanResult)
|
|
|
|
);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (physicalDeviceCount == 0)
|
|
|
|
{
|
|
|
|
REFRESH_LogError("Failed to find any GPUs with Vulkan support");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
physicalDevices = SDL_stack_alloc(VkPhysicalDevice, physicalDeviceCount);
|
|
|
|
|
|
|
|
vulkanResult = renderer->vkEnumeratePhysicalDevices(
|
|
|
|
renderer->instance,
|
|
|
|
&physicalDeviceCount,
|
|
|
|
physicalDevices
|
|
|
|
);
|
|
|
|
|
|
|
|
if (vulkanResult != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
REFRESH_LogError(
|
|
|
|
"vkEnumeratePhysicalDevices failed: %s",
|
|
|
|
VkErrorMessages(vulkanResult)
|
|
|
|
);
|
|
|
|
SDL_stack_free(physicalDevices);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Any suitable device will do, but we'd like the best */
|
|
|
|
suitableIndex = -1;
|
|
|
|
for (i = 0; i < physicalDeviceCount; i += 1)
|
|
|
|
{
|
|
|
|
if (VULKAN_INTERNAL_IsDeviceSuitable(
|
|
|
|
renderer,
|
|
|
|
physicalDevices[i],
|
|
|
|
deviceExtensionNames,
|
|
|
|
deviceExtensionCount,
|
|
|
|
renderer->surface,
|
|
|
|
&queueFamilyIndices,
|
|
|
|
&isIdeal
|
|
|
|
)) {
|
|
|
|
suitableIndex = i;
|
|
|
|
if (isIdeal)
|
|
|
|
{
|
|
|
|
/* This is the one we want! */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (suitableIndex != -1)
|
|
|
|
{
|
|
|
|
physicalDevice = physicalDevices[suitableIndex];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
REFRESH_LogError("No suitable physical devices found");
|
|
|
|
SDL_stack_free(physicalDevices);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
);
|
|
|
|
|
|
|
|
SDL_stack_free(physicalDevices);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t VULKAN_INTERNAL_CreateLogicalDevice(
|
|
|
|
VulkanRenderer *renderer,
|
|
|
|
const char **deviceExtensionNames,
|
|
|
|
uint32_t deviceExtensionCount
|
|
|
|
) {
|
|
|
|
VkResult vulkanResult;
|
2020-12-17 04:04:47 +00:00
|
|
|
|
2020-12-17 03:28:02 +00:00
|
|
|
VkDeviceCreateInfo deviceCreateInfo;
|
|
|
|
VkPhysicalDeviceFeatures deviceFeatures;
|
|
|
|
|
|
|
|
VkDeviceQueueCreateInfo *queueCreateInfos = SDL_stack_alloc(
|
|
|
|
VkDeviceQueueCreateInfo,
|
|
|
|
2
|
|
|
|
);
|
|
|
|
VkDeviceQueueCreateInfo queueCreateInfoGraphics;
|
|
|
|
VkDeviceQueueCreateInfo queueCreateInfoPresent;
|
|
|
|
|
|
|
|
int32_t queueInfoCount = 1;
|
|
|
|
float queuePriority = 1.0f;
|
|
|
|
|
|
|
|
queueCreateInfoGraphics.sType =
|
|
|
|
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
|
|
queueCreateInfoGraphics.pNext = NULL;
|
|
|
|
queueCreateInfoGraphics.flags = 0;
|
|
|
|
queueCreateInfoGraphics.queueFamilyIndex =
|
|
|
|
renderer->queueFamilyIndices.graphicsFamily;
|
|
|
|
queueCreateInfoGraphics.queueCount = 1;
|
|
|
|
queueCreateInfoGraphics.pQueuePriorities = &queuePriority;
|
|
|
|
|
|
|
|
queueCreateInfos[0] = queueCreateInfoGraphics;
|
|
|
|
|
|
|
|
if (renderer->queueFamilyIndices.presentFamily != renderer->queueFamilyIndices.graphicsFamily)
|
|
|
|
{
|
|
|
|
queueCreateInfoPresent.sType =
|
|
|
|
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
|
|
queueCreateInfoPresent.pNext = NULL;
|
|
|
|
queueCreateInfoPresent.flags = 0;
|
|
|
|
queueCreateInfoPresent.queueFamilyIndex =
|
|
|
|
renderer->queueFamilyIndices.presentFamily;
|
|
|
|
queueCreateInfoPresent.queueCount = 1;
|
|
|
|
queueCreateInfoPresent.pQueuePriorities = &queuePriority;
|
|
|
|
|
|
|
|
queueCreateInfos[1] = queueCreateInfoPresent;
|
|
|
|
queueInfoCount += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* specifying used device features */
|
|
|
|
|
|
|
|
SDL_zero(deviceFeatures);
|
|
|
|
deviceFeatures.occlusionQueryPrecise = VK_TRUE;
|
|
|
|
deviceFeatures.fillModeNonSolid = VK_TRUE;
|
|
|
|
|
|
|
|
/* creating the logical device */
|
|
|
|
|
|
|
|
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
|
|
|
deviceCreateInfo.pNext = NULL;
|
|
|
|
deviceCreateInfo.flags = 0;
|
|
|
|
deviceCreateInfo.queueCreateInfoCount = queueInfoCount;
|
|
|
|
deviceCreateInfo.pQueueCreateInfos = queueCreateInfos;
|
|
|
|
deviceCreateInfo.enabledLayerCount = 0;
|
|
|
|
deviceCreateInfo.ppEnabledLayerNames = NULL;
|
|
|
|
deviceCreateInfo.enabledExtensionCount = deviceExtensionCount;
|
|
|
|
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames;
|
|
|
|
deviceCreateInfo.pEnabledFeatures = &deviceFeatures;
|
|
|
|
|
|
|
|
vulkanResult = renderer->vkCreateDevice(
|
|
|
|
renderer->physicalDevice,
|
|
|
|
&deviceCreateInfo,
|
|
|
|
NULL,
|
|
|
|
&renderer->logicalDevice
|
|
|
|
);
|
|
|
|
if (vulkanResult != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
REFRESH_LogError(
|
|
|
|
"vkCreateDevice failed: %s",
|
|
|
|
VkErrorMessages(vulkanResult)
|
|
|
|
);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
);
|
|
|
|
|
|
|
|
SDL_stack_free(queueCreateInfos);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static REFRESH_Device* VULKAN_CreateDevice(
|
|
|
|
void *deviceWindowHandle,
|
|
|
|
uint8_t debugMode
|
2020-12-17 01:23:49 +00:00
|
|
|
) {
|
|
|
|
REFRESH_Device *result;
|
2020-12-17 03:28:02 +00:00
|
|
|
VulkanRenderer *renderer;
|
2020-12-17 01:23:49 +00:00
|
|
|
|
2020-12-17 04:04:47 +00:00
|
|
|
VkResult vulkanResult;
|
|
|
|
|
|
|
|
/* Variables: Create fence and semaphores */
|
|
|
|
VkFenceCreateInfo fenceInfo;
|
|
|
|
VkSemaphoreCreateInfo semaphoreInfo;
|
|
|
|
|
|
|
|
/* Variables: Create command pool and command buffer */
|
|
|
|
VkCommandPoolCreateInfo commandPoolCreateInfo;
|
|
|
|
VkCommandBufferAllocateInfo commandBufferAllocateInfo;
|
|
|
|
|
2020-12-17 01:23:49 +00:00
|
|
|
result = (REFRESH_Device*) SDL_malloc(sizeof(REFRESH_Device));
|
|
|
|
ASSIGN_DRIVER(VULKAN)
|
|
|
|
|
2020-12-17 03:28:02 +00:00
|
|
|
renderer = (VulkanRenderer*) SDL_malloc(sizeof(VulkanRenderer));
|
2020-12-17 02:12:20 +00:00
|
|
|
result->driverData = (REFRESH_Renderer*) renderer;
|
2020-12-17 03:28:02 +00:00
|
|
|
renderer->debugMode = debugMode;
|
2020-12-17 03:50:31 +00:00
|
|
|
renderer->headless = deviceWindowHandle == NULL;
|
2020-12-17 03:28:02 +00:00
|
|
|
|
|
|
|
/* Create the VkInstance */
|
|
|
|
if (!VULKAN_INTERNAL_CreateInstance(renderer, deviceWindowHandle))
|
|
|
|
{
|
|
|
|
REFRESH_LogError("Error creating vulkan instance");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer->deviceWindowHandle = deviceWindowHandle;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create the WSI vkSurface
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!SDL_Vulkan_CreateSurface(
|
|
|
|
(SDL_Window*) 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"
|
|
|
|
"! 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;
|
|
|
|
}
|
2020-12-17 01:23:49 +00:00
|
|
|
|
2020-12-17 03:50:31 +00:00
|
|
|
/*
|
|
|
|
* Create initial swapchain
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!renderer->headless)
|
|
|
|
{
|
|
|
|
if (VULKAN_INTERNAL_CreateSwapchain(renderer) != CREATE_SWAPCHAIN_SUCCESS)
|
|
|
|
{
|
|
|
|
REFRESH_LogError("Failed to create swap chain");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-17 04:04:47 +00:00
|
|
|
/*
|
|
|
|
* Create fence and semaphores
|
|
|
|
*/
|
|
|
|
|
|
|
|
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
|
|
fenceInfo.pNext = NULL;
|
|
|
|
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
|
|
|
|
|
|
|
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
|
|
semaphoreInfo.pNext = NULL;
|
|
|
|
semaphoreInfo.flags = 0;
|
|
|
|
|
|
|
|
vulkanResult = renderer->vkCreateSemaphore(
|
|
|
|
renderer->logicalDevice,
|
|
|
|
&semaphoreInfo,
|
|
|
|
NULL,
|
|
|
|
&renderer->imageAvailableSemaphore
|
|
|
|
);
|
|
|
|
|
|
|
|
if (vulkanResult != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
LogVulkanResult("vkCreateFence", vulkanResult);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
vulkanResult = renderer->vkCreateSemaphore(
|
|
|
|
renderer->logicalDevice,
|
|
|
|
&semaphoreInfo,
|
|
|
|
NULL,
|
|
|
|
&renderer->renderFinishedSemaphore
|
|
|
|
);
|
|
|
|
|
|
|
|
if (vulkanResult != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
LogVulkanResult("vkCreateSemaphore", vulkanResult);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
vulkanResult = renderer->vkCreateFence(
|
|
|
|
renderer->logicalDevice,
|
|
|
|
&fenceInfo,
|
|
|
|
NULL,
|
|
|
|
&renderer->inFlightFence
|
|
|
|
);
|
|
|
|
|
|
|
|
if (vulkanResult != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
LogVulkanResult("vkCreateSemaphore", vulkanResult);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create command pool and buffers
|
|
|
|
*/
|
|
|
|
|
|
|
|
commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
|
|
commandPoolCreateInfo.pNext = NULL;
|
|
|
|
commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
|
|
commandPoolCreateInfo.queueFamilyIndex = renderer->queueFamilyIndices.graphicsFamily;
|
|
|
|
vulkanResult = renderer->vkCreateCommandPool(
|
|
|
|
renderer->logicalDevice,
|
|
|
|
&commandPoolCreateInfo,
|
|
|
|
NULL,
|
|
|
|
&renderer->commandPool
|
|
|
|
);
|
|
|
|
if (vulkanResult != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
LogVulkanResult("vkCreateCommandPool", vulkanResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer->allocatedCommandBufferCount = 4;
|
|
|
|
renderer->inactiveCommandBuffers = SDL_malloc(sizeof(VkCommandBuffer) * renderer->allocatedCommandBufferCount);
|
|
|
|
renderer->activeCommandBuffers = SDL_malloc(sizeof(VkCommandBuffer) * renderer->allocatedCommandBufferCount);
|
|
|
|
renderer->submittedCommandBuffers = SDL_malloc(sizeof(VkCommandBuffer) * renderer->allocatedCommandBufferCount);
|
|
|
|
renderer->inactiveCommandBufferCount = renderer->allocatedCommandBufferCount;
|
|
|
|
renderer->activeCommandBufferCount = 0;
|
|
|
|
renderer->submittedCommandBufferCount = 0;
|
|
|
|
|
|
|
|
commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
|
|
commandBufferAllocateInfo.pNext = NULL;
|
|
|
|
commandBufferAllocateInfo.commandPool = renderer->commandPool;
|
|
|
|
commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
|
|
commandBufferAllocateInfo.commandBufferCount = renderer->allocatedCommandBufferCount;
|
|
|
|
vulkanResult = renderer->vkAllocateCommandBuffers(
|
|
|
|
renderer->logicalDevice,
|
|
|
|
&commandBufferAllocateInfo,
|
|
|
|
renderer->inactiveCommandBuffers
|
|
|
|
);
|
|
|
|
if (vulkanResult != VK_SUCCESS)
|
|
|
|
{
|
|
|
|
LogVulkanResult("vkAllocateCommandBuffers", vulkanResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer->currentCommandCount = 0;
|
|
|
|
|
|
|
|
VULKAN_INTERNAL_BeginCommandBuffer(renderer);
|
|
|
|
|
2020-12-17 01:23:49 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-12-17 00:27:14 +00:00
|
|
|
REFRESH_Driver VulkanDriver = {
|
|
|
|
"Vulkan",
|
|
|
|
VULKAN_CreateDevice
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif //REFRESH_DRIVER_VULKAN
|