Merge branch 'parallel-frames' into better-sampling-passes-parallel

includes a few initial compilation fixes for parallel ray tracing. does not work yet
This commit is contained in:
Ivan 'provod' Avdeev 2022-02-20 16:00:33 -08:00
commit 955f36a423
18 changed files with 976 additions and 562 deletions

View File

@ -93,6 +93,7 @@ void R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, f
}
{
// TODO do this in shader bro
const float vw = vk_frame.width;
const float vh = vk_frame.height;
const float x1 = (x / vw)*2.f - 1.f;

View File

@ -655,7 +655,7 @@ static qboolean loadBrushSurfaces( model_sizes_t sizes, const model_t *mod ) {
return true;
}
qboolean VK_BrushModelLoad( model_t *mod, qboolean map )
qboolean VK_BrushModelLoad( VkCommandBuffer cmdbuf, model_t *mod, qboolean map )
{
if (mod->cache.data)
{
@ -685,7 +685,7 @@ qboolean VK_BrushModelLoad( model_t *mod, qboolean map )
if (!map && sizes.emissive_surfaces)
bmodel->polylights = Mem_Malloc(vk_core.pool, sizeof(bmodel->polylights[0]) * sizes.emissive_surfaces);
if (!loadBrushSurfaces(sizes, mod) || !VK_RenderModelInit(&bmodel->render_model)) {
if (!loadBrushSurfaces(sizes, mod) || !VK_RenderModelInit(cmdbuf, &bmodel->render_model)) {
gEngine.Con_Printf(S_ERROR "Could not load model %s\n", mod->name);
Mem_Free(bmodel);
return false;

View File

@ -11,7 +11,7 @@ struct cl_entity_s;
qboolean VK_BrushInit( void );
void VK_BrushShutdown( void );
qboolean VK_BrushModelLoad( struct model_s *mod, qboolean map);
qboolean VK_BrushModelLoad( VkCommandBuffer cmdbuf, struct model_s *mod, qboolean map);
void VK_BrushModelDestroy( struct model_s *mod );
void VK_BrushModelDraw( const cl_entity_t *ent, int render_mode, const matrix4x4 model );

View File

@ -80,51 +80,6 @@ static dllfunc_t device_funcs_rtx[] = {
#undef X
};
const char *resultName(VkResult result) {
switch (result) {
case VK_SUCCESS: return "VK_SUCCESS";
case VK_NOT_READY: return "VK_NOT_READY";
case VK_TIMEOUT: return "VK_TIMEOUT";
case VK_EVENT_SET: return "VK_EVENT_SET";
case VK_EVENT_RESET: return "VK_EVENT_RESET";
case VK_INCOMPLETE: return "VK_INCOMPLETE";
case VK_ERROR_OUT_OF_HOST_MEMORY: return "VK_ERROR_OUT_OF_HOST_MEMORY";
case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
case VK_ERROR_INITIALIZATION_FAILED: return "VK_ERROR_INITIALIZATION_FAILED";
case VK_ERROR_DEVICE_LOST: return "VK_ERROR_DEVICE_LOST";
case VK_ERROR_MEMORY_MAP_FAILED: return "VK_ERROR_MEMORY_MAP_FAILED";
case VK_ERROR_LAYER_NOT_PRESENT: return "VK_ERROR_LAYER_NOT_PRESENT";
case VK_ERROR_EXTENSION_NOT_PRESENT: return "VK_ERROR_EXTENSION_NOT_PRESENT";
case VK_ERROR_FEATURE_NOT_PRESENT: return "VK_ERROR_FEATURE_NOT_PRESENT";
case VK_ERROR_INCOMPATIBLE_DRIVER: return "VK_ERROR_INCOMPATIBLE_DRIVER";
case VK_ERROR_TOO_MANY_OBJECTS: return "VK_ERROR_TOO_MANY_OBJECTS";
case VK_ERROR_FORMAT_NOT_SUPPORTED: return "VK_ERROR_FORMAT_NOT_SUPPORTED";
case VK_ERROR_FRAGMENTED_POOL: return "VK_ERROR_FRAGMENTED_POOL";
case VK_ERROR_UNKNOWN: return "VK_ERROR_UNKNOWN";
case VK_ERROR_OUT_OF_POOL_MEMORY: return "VK_ERROR_OUT_OF_POOL_MEMORY";
case VK_ERROR_INVALID_EXTERNAL_HANDLE: return "VK_ERROR_INVALID_EXTERNAL_HANDLE";
case VK_ERROR_FRAGMENTATION: return "VK_ERROR_FRAGMENTATION";
case VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS: return "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS";
case VK_ERROR_SURFACE_LOST_KHR: return "VK_ERROR_SURFACE_LOST_KHR";
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
case VK_SUBOPTIMAL_KHR: return "VK_SUBOPTIMAL_KHR";
case VK_ERROR_OUT_OF_DATE_KHR: return "VK_ERROR_OUT_OF_DATE_KHR";
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
case VK_ERROR_VALIDATION_FAILED_EXT: return "VK_ERROR_VALIDATION_FAILED_EXT";
case VK_ERROR_INVALID_SHADER_NV: return "VK_ERROR_INVALID_SHADER_NV";
case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
case VK_ERROR_NOT_PERMITTED_EXT: return "VK_ERROR_NOT_PERMITTED_EXT";
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
case VK_THREAD_IDLE_KHR: return "VK_THREAD_IDLE_KHR";
case VK_THREAD_DONE_KHR: return "VK_THREAD_DONE_KHR";
case VK_OPERATION_DEFERRED_KHR: return "VK_OPERATION_DEFERRED_KHR";
case VK_OPERATION_NOT_DEFERRED_KHR: return "VK_OPERATION_NOT_DEFERRED_KHR";
case VK_PIPELINE_COMPILE_REQUIRED_EXT: return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
default: return "UNKNOWN";
}
}
static const char *validation_layers[] = {
"VK_LAYER_KHRONOS_validation",
};
@ -586,7 +541,7 @@ static qboolean createDevice( void ) {
const VkResult result = vkCreateDevice(candidate_device->device, &create_info, NULL, &vk_core.device);
if (result != VK_SUCCESS) {
gEngine.Con_Printf( S_ERROR "%s:%d vkCreateDevice failed (%d): %s\n",
__FILE__, __LINE__, result, resultName(result));
__FILE__, __LINE__, result, R_VkResultName(result));
continue;
}
}
@ -623,21 +578,6 @@ static qboolean createDevice( void ) {
gEngine.Con_Printf( S_ERROR "No compatibe Vulkan devices found. Vulkan render will not be available\n" );
return false;
}
static const char *presentModeName(VkPresentModeKHR present_mode)
{
switch (present_mode)
{
case VK_PRESENT_MODE_IMMEDIATE_KHR: return "VK_PRESENT_MODE_IMMEDIATE_KHR";
case VK_PRESENT_MODE_MAILBOX_KHR: return "VK_PRESENT_MODE_MAILBOX_KHR";
case VK_PRESENT_MODE_FIFO_KHR: return "VK_PRESENT_MODE_FIFO_KHR";
case VK_PRESENT_MODE_FIFO_RELAXED_KHR: return "VK_PRESENT_MODE_FIFO_RELAXED_KHR";
case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: return "VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR";
case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR: return "VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR";
default: return "UNKNOWN";
}
}
static qboolean initSurface( void )
{
XVK_CHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(vk_core.physical_device.device, vk_core.surface.surface, &vk_core.surface.num_present_modes, vk_core.surface.present_modes));
@ -647,7 +587,7 @@ static qboolean initSurface( void )
gEngine.Con_Printf("Supported surface present modes: %u\n", vk_core.surface.num_present_modes);
for (uint32_t i = 0; i < vk_core.surface.num_present_modes; ++i)
{
gEngine.Con_Reportf("\t%u: %s (%u)\n", i, presentModeName(vk_core.surface.present_modes[i]), vk_core.surface.present_modes[i]);
gEngine.Con_Reportf("\t%u: %s (%u)\n", i, R_VkPresentModeName(vk_core.surface.present_modes[i]), vk_core.surface.present_modes[i]);
}
XVK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_core.physical_device.device, vk_core.surface.surface, &vk_core.surface.num_surface_formats, vk_core.surface.surface_formats));
@ -658,36 +598,43 @@ static qboolean initSurface( void )
for (uint32_t i = 0; i < vk_core.surface.num_surface_formats; ++i)
{
// TODO symbolicate
gEngine.Con_Reportf("\t%u: %u %u\n", i, vk_core.surface.surface_formats[i].format, vk_core.surface.surface_formats[i].colorSpace);
gEngine.Con_Reportf("\t%u: %s(%u) %s(%u)\n", i,
R_VkFormatName(vk_core.surface.surface_formats[i].format), vk_core.surface.surface_formats[i].format,
R_VkColorSpaceName(vk_core.surface.surface_formats[i].colorSpace), vk_core.surface.surface_formats[i].colorSpace);
}
return true;
}
static qboolean createCommandPool( void ) {
VkCommandPoolCreateInfo cpci = {
vk_command_pool_t R_VkCommandPoolCreate( int count ) {
vk_command_pool_t ret = {0};
const VkCommandPoolCreateInfo cpci = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.queueFamilyIndex = 0,
.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
};
VkCommandBuffer bufs[2];
VkCommandBufferAllocateInfo cbai = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandBufferCount = ARRAYSIZE(bufs),
.commandBufferCount = count,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
};
XVK_CHECK(vkCreateCommandPool(vk_core.device, &cpci, NULL, &ret.pool));
XVK_CHECK(vkCreateCommandPool(vk_core.device, &cpci, NULL, &vk_core.command_pool));
cbai.commandPool = vk_core.command_pool;
XVK_CHECK(vkAllocateCommandBuffers(vk_core.device, &cbai, bufs));
cbai.commandPool = ret.pool;
ret.buffers = Mem_Malloc(vk_core.pool, sizeof(VkCommandBuffer) * count);
ret.buffers_count = count;
XVK_CHECK(vkAllocateCommandBuffers(vk_core.device, &cbai, ret.buffers));
vk_core.cb = bufs[0];
vk_core.cb_tex = bufs[1];
return ret;
}
return true;
void R_VkCommandPoolDestroy( vk_command_pool_t *pool ) {
ASSERT(pool->buffers);
vkDestroyCommandPool(vk_core.device, pool->pool, NULL);
Mem_Free(pool->buffers);
}
qboolean R_VkInit( void )
@ -750,8 +697,7 @@ qboolean R_VkInit( void )
if (!initSurface())
return false;
if (!createCommandPool())
return false;
vk_core.upload_pool = R_VkCommandPoolCreate( 1 );
if (!VK_DevMemInit())
return false;
@ -818,8 +764,9 @@ qboolean R_VkInit( void )
return true;
}
void R_VkShutdown( void )
{
void R_VkShutdown( void ) {
XVK_CHECK(vkDeviceWaitIdle(vk_core.device));
if (vk_core.rtx)
{
VK_LightsShutdown();
@ -845,7 +792,7 @@ void R_VkShutdown( void )
VK_DevMemDestroy();
vkDestroyCommandPool(vk_core.device, vk_core.command_pool, NULL);
R_VkCommandPoolDestroy( &vk_core.upload_pool );
vkDestroyDevice(vk_core.device, NULL);
@ -896,7 +843,7 @@ VkShaderModule loadShader(const char *filename) {
return shader;
}
VkSemaphore createSemaphore( void ) {
VkSemaphore R_VkSemaphoreCreate( void ) {
VkSemaphore sema;
VkSemaphoreCreateInfo sci = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
@ -906,20 +853,20 @@ VkSemaphore createSemaphore( void ) {
return sema;
}
void destroySemaphore(VkSemaphore sema) {
void R_VkSemaphoreDestroy(VkSemaphore sema) {
vkDestroySemaphore(vk_core.device, sema, NULL);
}
VkFence createFence( void ) {
VkFence R_VkFenceCreate( qboolean signaled ) {
VkFence fence;
VkFenceCreateInfo fci = {
const VkFenceCreateInfo fci = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = 0,
.flags = signaled ? VK_FENCE_CREATE_SIGNALED_BIT : 0,
};
XVK_CHECK(vkCreateFence(vk_core.device, &fci, NULL, &fence));
return fence;
}
void destroyFence(VkFence fence) {
void R_VkFenceDestroy(VkFence fence) {
vkDestroyFence(vk_core.device, fence, NULL);
}

View File

@ -10,13 +10,25 @@
qboolean R_VkInit( void );
void R_VkShutdown( void );
// FIXME load from embedded static structs
VkShaderModule loadShader(const char *filename);
VkSemaphore createSemaphore( void );
void destroySemaphore(VkSemaphore sema);
VkFence createFence( void );
void destroyFence(VkFence fence);
typedef struct {
VkCommandPool pool;
VkCommandBuffer *buffers;
int buffers_count;
} vk_command_pool_t;
vk_command_pool_t R_VkCommandPoolCreate( int count );
void R_VkCommandPoolDestroy( vk_command_pool_t *pool );
// TODO load from embedded static structs
VkShaderModule loadShader(const char *filename);
VkSemaphore R_VkSemaphoreCreate( void );
void R_VkSemaphoreDestroy(VkSemaphore sema);
VkFence R_VkFenceCreate( qboolean signaled );
void R_VkFenceDestroy(VkFence fence);
// TODO move all these to vk_device.{h,c} or something
typedef struct physical_device_s {
VkPhysicalDevice device;
VkPhysicalDeviceMemoryProperties2 memory_properties2;
@ -52,9 +64,7 @@ typedef struct vulkan_core_s {
VkDevice device;
VkQueue queue;
VkCommandPool command_pool;
VkCommandBuffer cb;
VkCommandBuffer cb_tex;
vk_command_pool_t upload_pool;
VkSampler default_sampler;
@ -64,7 +74,10 @@ typedef struct vulkan_core_s {
extern vulkan_core_t vk_core;
const char *resultName(VkResult result);
const char *R_VkResultName(VkResult result);
const char *R_VkPresentModeName(VkPresentModeKHR present_mode);
const char *R_VkFormatName(VkFormat format);
const char *R_VkColorSpaceName(VkColorSpaceKHR colorspace);
#define SET_DEBUG_NAME(object, type, name) \
do { \
@ -131,9 +144,9 @@ do { \
const VkResult result = f; \
if (result != VK_SUCCESS) { \
gEngine.Con_Printf( S_ERROR "%s:%d " #f " failed (%d): %s\n", \
__FILE__, __LINE__, result, resultName(result)); \
__FILE__, __LINE__, result, R_VkResultName(result)); \
gEngine.Host_Error( S_ERROR "%s:%d " #f " failed (%d): %s\n", \
__FILE__, __LINE__, result, resultName(result)); \
__FILE__, __LINE__, result, R_VkResultName(result)); \
} \
} while(0)
@ -217,6 +230,7 @@ do { \
X(vkCmdPipelineBarrier) \
X(vkCmdCopyBufferToImage) \
X(vkQueueWaitIdle) \
X(vkDeviceWaitIdle) \
X(vkDestroyImage) \
X(vkCmdBindDescriptorSets) \
X(vkCreateSampler) \

View File

@ -6,40 +6,39 @@
#include "vk_rtx.h"
#include "vk_cvar.h"
#include "vk_devmem.h"
#include "vk_swapchain.h"
#include "vk_image.h"
#include "profiler.h"
#include "eiface.h"
#include "eiface.h" // ARRAYSIZE
#include <string.h>
extern ref_globals_t *gpGlobals;
vk_framectl_t vk_frame = {0};
typedef enum {
Phase_Idle,
Phase_FrameBegan,
Phase_RenderingEnqueued,
Phase_Submitted,
} frame_phase_t;
static struct {
struct {
VkFormat format;
vk_devmem_t device_memory;
VkImage image;
VkImageView image_view;
} depth;
VkSurfaceCapabilitiesKHR surface_caps;
VkSwapchainCreateInfoKHR create_info;
VkSwapchainKHR swapchain;
uint32_t num_images;
VkImage *images;
VkImageView *image_views;
VkFramebuffer *framebuffers;
VkSemaphore image_available;
VkSemaphore done;
VkFence fence;
uint32_t swapchain_image_index;
vk_command_pool_t command;
VkSemaphore sem_framebuffer_ready[MAX_CONCURRENT_FRAMES];
VkSemaphore sem_done[MAX_CONCURRENT_FRAMES];
VkFence fence_done[MAX_CONCURRENT_FRAMES];
qboolean rtx_enabled;
int last_frame_index; // Index of previous fully drawn frame into images array
struct {
int index;
r_vk_swapchain_framebuffer_t framebuffer;
frame_phase_t phase;
} current;
} g_frame;
#define PROFILER_SCOPES(X) \
@ -50,6 +49,7 @@ static struct {
PROFILER_SCOPES(SCOPE_DECLARE)
#undef SCOPE_DECLARE
// TODO move into vk_image
static VkFormat findSupportedImageFormat(const VkFormat *candidates, VkImageTiling tiling, VkFormatFeatureFlags features) {
for (int i = 0; candidates[i] != VK_FORMAT_UNDEFINED; ++i) {
VkFormatProperties props;
@ -70,27 +70,6 @@ static VkFormat findSupportedImageFormat(const VkFormat *candidates, VkImageTili
return VK_FORMAT_UNDEFINED;
}
static VkImage createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage) {
VkImage image;
VkImageCreateInfo ici = {.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
ici.imageType = VK_IMAGE_TYPE_2D;
ici.extent.width = width;
ici.extent.height = height;
ici.extent.depth = 1;
ici.mipLevels = 1;
ici.arrayLayers = 1;
ici.format = format;
ici.tiling = tiling;
ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ici.usage = usage;
ici.samples = VK_SAMPLE_COUNT_1_BIT;
ici.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
XVK_CHECK(vkCreateImage(vk_core.device, &ici, NULL, &image));
return image;
}
// TODO sort these based on ???
static const VkFormat depth_formats[] = {
VK_FORMAT_D32_SFLOAT,
@ -102,42 +81,11 @@ static const VkFormat depth_formats[] = {
VK_FORMAT_UNDEFINED
};
static void createDepthImage(int w, int h) {
const VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
const VkImageUsageFlags usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
VkMemoryRequirements memreq;
g_frame.depth.image = createImage(w, h, g_frame.depth.format, tiling, usage);
SET_DEBUG_NAME(g_frame.depth.image, VK_OBJECT_TYPE_IMAGE, "depth buffer");
vkGetImageMemoryRequirements(vk_core.device, g_frame.depth.image, &memreq);
g_frame.depth.device_memory = VK_DevMemAllocate("depth buffer", memreq, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0);
XVK_CHECK(vkBindImageMemory(vk_core.device, g_frame.depth.image, g_frame.depth.device_memory.device_memory, g_frame.depth.device_memory.offset));
{
VkImageViewCreateInfo ivci = {.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
ivci.format = g_frame.depth.format;
ivci.image = g_frame.depth.image;
ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
ivci.subresourceRange.levelCount = 1;
ivci.subresourceRange.layerCount = 1;
XVK_CHECK(vkCreateImageView(vk_core.device, &ivci, NULL, &g_frame.depth.image_view));
}
}
static void destroyDepthImage( void ) {
vkDestroyImageView(vk_core.device, g_frame.depth.image_view, NULL);
vkDestroyImage(vk_core.device, g_frame.depth.image, NULL);
VK_DevMemFree(&g_frame.depth.device_memory);
}
static VkRenderPass createRenderPass( qboolean ray_tracing ) {
static VkRenderPass createRenderPass( VkFormat depth_format, qboolean ray_tracing ) {
VkRenderPass render_pass;
VkAttachmentDescription attachments[] = {{
.format = VK_FORMAT_B8G8R8A8_UNORM, //SRGB,// FIXME too early swapchain.create_info.imageFormat;
const VkAttachmentDescription attachments[] = {{
.format = SWAPCHAIN_FORMAT,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = ray_tracing ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR /* TODO: prod renderer should not care VK_ATTACHMENT_LOAD_OP_DONT_CARE */,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
@ -147,7 +95,7 @@ static VkRenderPass createRenderPass( qboolean ray_tracing ) {
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
}, {
// Depth
.format = g_frame.depth.format = findSupportedImageFormat(depth_formats, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT),
.format = depth_format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
@ -157,24 +105,24 @@ static VkRenderPass createRenderPass( qboolean ray_tracing ) {
.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
}};
VkAttachmentReference color_attachment = {
const VkAttachmentReference color_attachment = {
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
};
VkAttachmentReference depth_attachment = {
const VkAttachmentReference depth_attachment = {
.attachment = 1,
.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
};
VkSubpassDescription subdesc = {
const VkSubpassDescription subdesc = {
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.colorAttachmentCount = 1,
.pColorAttachments = &color_attachment,
.pDepthStencilAttachment = &depth_attachment,
};
VkRenderPassCreateInfo rpci = {
const VkRenderPassCreateInfo rpci = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = ARRAYSIZE(attachments),
.pAttachments = attachments,
@ -186,190 +134,27 @@ static VkRenderPass createRenderPass( qboolean ray_tracing ) {
return render_pass;
}
static void destroySwapchain( VkSwapchainKHR swapchain )
{
for (uint32_t i = 0; i < g_frame.num_images; ++i)
{
vkDestroyImageView(vk_core.device, g_frame.image_views[i], NULL);
vkDestroyFramebuffer(vk_core.device, g_frame.framebuffers[i], NULL);
}
vkDestroySwapchainKHR(vk_core.device, swapchain, NULL);
destroyDepthImage();
}
extern ref_globals_t *gpGlobals;
static uint32_t clamp_u32(uint32_t v, uint32_t min, uint32_t max) {
if (v < min) v = min;
if (v > max) v = max;
return v;
}
static qboolean createSwapchain( void )
{
VkSwapchainCreateInfoKHR *create_info = &g_frame.create_info;
const uint32_t prev_num_images = g_frame.num_images;
// recreating swapchain means invalidating any previous frames
g_frame.last_frame_index = -1;
XVK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_core.physical_device.device, vk_core.surface.surface, &g_frame.surface_caps));
vk_frame.width = g_frame.surface_caps.currentExtent.width;
vk_frame.height = g_frame.surface_caps.currentExtent.height;
if (vk_frame.width == 0xfffffffful || vk_frame.width == 0)
vk_frame.width = gpGlobals->width;
if (vk_frame.height == 0xfffffffful || vk_frame.height == 0)
vk_frame.height = gpGlobals->height;
vk_frame.width = clamp_u32(vk_frame.width, g_frame.surface_caps.minImageExtent.width, g_frame.surface_caps.maxImageExtent.width);
vk_frame.height = clamp_u32(vk_frame.height, g_frame.surface_caps.minImageExtent.height, g_frame.surface_caps.maxImageExtent.height);
create_info->sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
create_info->pNext = NULL;
create_info->surface = vk_core.surface.surface;
create_info->imageFormat = VK_FORMAT_B8G8R8A8_UNORM;//SRGB; // TODO get from surface_formats
create_info->imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; // TODO get from surface_formats
create_info->imageExtent.width = vk_frame.width;
create_info->imageExtent.height = vk_frame.height;
create_info->imageArrayLayers = 1;
create_info->imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | (vk_core.rtx ? VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT : 0);
create_info->imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
create_info->preTransform = g_frame.surface_caps.currentTransform;
create_info->compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
create_info->presentMode = VK_PRESENT_MODE_FIFO_KHR; // TODO caps, MAILBOX is better
//create_info->presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; // TODO caps, MAILBOX is better
create_info->clipped = VK_TRUE;
create_info->oldSwapchain = g_frame.swapchain;
create_info->minImageCount = g_frame.surface_caps.minImageCount + 3;
if (g_frame.surface_caps.maxImageCount && create_info->minImageCount > g_frame.surface_caps.maxImageCount)
create_info->minImageCount = g_frame.surface_caps.maxImageCount;
XVK_CHECK(vkCreateSwapchainKHR(vk_core.device, create_info, NULL, &g_frame.swapchain));
if (create_info->oldSwapchain)
{
destroySwapchain( create_info->oldSwapchain );
}
createDepthImage(vk_frame.width, vk_frame.height);
g_frame.num_images = 0;
XVK_CHECK(vkGetSwapchainImagesKHR(vk_core.device, g_frame.swapchain, &g_frame.num_images, NULL));
if (prev_num_images != g_frame.num_images)
{
if (g_frame.images)
{
Mem_Free(g_frame.images);
Mem_Free(g_frame.image_views);
Mem_Free(g_frame.framebuffers);
}
g_frame.images = Mem_Malloc(vk_core.pool, sizeof(*g_frame.images) * g_frame.num_images);
g_frame.image_views = Mem_Malloc(vk_core.pool, sizeof(*g_frame.image_views) * g_frame.num_images);
g_frame.framebuffers = Mem_Malloc(vk_core.pool, sizeof(*g_frame.framebuffers) * g_frame.num_images);
}
XVK_CHECK(vkGetSwapchainImagesKHR(vk_core.device, g_frame.swapchain, &g_frame.num_images, g_frame.images));
for (uint32_t i = 0; i < g_frame.num_images; ++i) {
VkImageViewCreateInfo ivci = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = g_frame.create_info.imageFormat,
.image = g_frame.images[i],
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.subresourceRange.levelCount = 1,
.subresourceRange.layerCount = 1,
};
XVK_CHECK(vkCreateImageView(vk_core.device, &ivci, NULL, g_frame.image_views + i));
{
const VkImageView attachments[] = {
g_frame.image_views[i],
g_frame.depth.image_view
};
VkFramebufferCreateInfo fbci = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = vk_frame.render_pass.raster,
.attachmentCount = ARRAYSIZE(attachments),
.pAttachments = attachments,
.width = g_frame.create_info.imageExtent.width,
.height = g_frame.create_info.imageExtent.height,
.layers = 1,
};
XVK_CHECK(vkCreateFramebuffer(vk_core.device, &fbci, NULL, g_frame.framebuffers + i));
}
SET_DEBUG_NAMEF(g_frame.images[i], VK_OBJECT_TYPE_IMAGE, "swapchain image[%d]", i);
}
return true;
}
void R_BeginFrame( qboolean clearScene )
{
if (vk_core.rtx && FBitSet( vk_rtx->flags, FCVAR_CHANGED )) {
g_frame.rtx_enabled = CVAR_TO_BOOL( vk_rtx );
}
ClearBits( vk_rtx->flags, FCVAR_CHANGED );
{
gEngine.Con_NPrintf(5, "Perf scopes:");
for (int i = 0; i < g_aprof.num_scopes; ++i) {
const aprof_scope_t *const scope = g_aprof.scopes + i;
gEngine.Con_NPrintf(6 + i, "%s: c%d t%.03f(%.03f)ms s%.03f(%.03f)ms", scope->name,
scope->frame.count,
scope->frame.duration / 1e6,
(scope->frame.duration / 1e6) / scope->frame.count,
(scope->frame.duration - scope->frame.duration_children) / 1e6,
(scope->frame.duration - scope->frame.duration_children) / 1e6 / scope->frame.count);
}
aprof_scope_frame();
}
// TODO should we handle multiple R_BeginFrame w/o R_EndFrame gracefully?
ASSERT(g_frame.swapchain_image_index == -1);
// Check that swapchain has the same size
{
VkSurfaceCapabilitiesKHR surface_caps;
XVK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_core.physical_device.device, vk_core.surface.surface, &surface_caps));
if (surface_caps.currentExtent.width != g_frame.surface_caps.currentExtent.width
|| surface_caps.currentExtent.height != g_frame.surface_caps.currentExtent.height)
{
createSwapchain();
}
}
for (int i = 0;; ++i)
{
const VkResult acquire_result = vkAcquireNextImageKHR(vk_core.device, g_frame.swapchain, UINT64_MAX, g_frame.image_available,
VK_NULL_HANDLE, &g_frame.swapchain_image_index);
switch (acquire_result)
{
// TODO re-ask for swapchain size if it changed
case VK_ERROR_OUT_OF_DATE_KHR:
case VK_ERROR_SURFACE_LOST_KHR:
if (i == 0) {
createSwapchain();
continue;
}
gEngine.Con_Printf(S_WARN "vkAcquireNextImageKHR returned %s, frame will be lost\n", resultName(acquire_result));
return;
static void waitForFrameFence( void ) {
for(qboolean loop = true; loop; ) {
#define MAX_WAIT (10ull * 1000*1000*1000)
const VkResult fence_result = vkWaitForFences(vk_core.device, 1, g_frame.fence_done + g_frame.current.index, VK_TRUE, MAX_WAIT);
#undef MAX_WAIT
switch (fence_result) {
case VK_SUCCESS:
loop = false;
break;
case VK_TIMEOUT:
gEngine.Con_Printf(S_ERROR "Waitinf for frame fence to be signaled timed out after 10 seconds. Wat\n");
break;
default:
XVK_CHECK(acquire_result);
XVK_CHECK(fence_result);
}
break;
}
XVK_CHECK(vkResetFences(vk_core.device, 1, g_frame.fence_done + g_frame.current.index));
}
static void updateGamma( void ) {
// FIXME when
{
cvar_t* vid_gamma = gEngine.pfnGetCvarPointer( "gamma", 0 );
@ -389,16 +174,57 @@ void R_BeginFrame( qboolean clearScene )
// FIXME rebuild lightmaps
}
}
}
// FIXME move this to r print speeds or something like that
static void showProfilingData( void ) {
gEngine.Con_NPrintf(5, "Perf scopes:");
for (int i = 0; i < g_aprof.num_scopes; ++i) {
const aprof_scope_t *const scope = g_aprof.scopes + i;
gEngine.Con_NPrintf(6 + i, "%s: c%d t%.03f(%.03f)ms s%.03f(%.03f)ms", scope->name,
scope->frame.count,
scope->frame.duration / 1e6,
(scope->frame.duration / 1e6) / scope->frame.count,
(scope->frame.duration - scope->frame.duration_children) / 1e6,
(scope->frame.duration - scope->frame.duration_children) / 1e6 / scope->frame.count);
}
}
void R_BeginFrame( qboolean clearScene ) {
ASSERT(g_frame.current.phase == Phase_Submitted || g_frame.current.phase == Phase_Idle);
const int prev_frame_index = g_frame.current.index % MAX_CONCURRENT_FRAMES;
g_frame.current.index = (g_frame.current.index + 1) % MAX_CONCURRENT_FRAMES;
const VkCommandBuffer cmdbuf = vk_frame.cmdbuf = g_frame.command.buffers[g_frame.current.index];
showProfilingData();
aprof_scope_frame();
if (vk_core.rtx && FBitSet( vk_rtx->flags, FCVAR_CHANGED )) {
g_frame.rtx_enabled = CVAR_TO_BOOL( vk_rtx );
}
ClearBits( vk_rtx->flags, FCVAR_CHANGED );
updateGamma();
ASSERT(!g_frame.current.framebuffer.framebuffer);
waitForFrameFence();
g_frame.current.framebuffer = R_VkSwapchainAcquire( g_frame.sem_framebuffer_ready[g_frame.current.index] );
vk_frame.width = g_frame.current.framebuffer.width;
vk_frame.height = g_frame.current.framebuffer.height;
VK_RenderBegin( g_frame.rtx_enabled );
{
VkCommandBufferBeginInfo beginfo = {
const VkCommandBufferBeginInfo beginfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
XVK_CHECK(vkBeginCommandBuffer(vk_core.cb, &beginfo));
XVK_CHECK(vkBeginCommandBuffer(cmdbuf, &beginfo));
}
g_frame.current.phase = Phase_FrameBegan;
}
void VK_RenderFrame( const struct ref_viewpass_s *rvp )
@ -406,106 +232,112 @@ void VK_RenderFrame( const struct ref_viewpass_s *rvp )
VK_SceneRender( rvp );
}
void R_EndFrame( void )
{
APROF_SCOPE_BEGIN_EARLY(end_frame);
VkClearValue clear_value[] = {
static void enqueueRendering( VkCommandBuffer cmdbuf ) {
const VkClearValue clear_value[] = {
{.color = {{1., 0., 0., 0.}}},
{.depthStencil = {1., 0.}} // TODO reverse-z
};
VkPipelineStageFlags stageflags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
ASSERT(g_frame.current.phase == Phase_FrameBegan);
if (g_frame.rtx_enabled)
VK_RenderEndRTX( vk_core.cb, g_frame.image_views[g_frame.swapchain_image_index], g_frame.images[g_frame.swapchain_image_index], g_frame.create_info.imageExtent.width, g_frame.create_info.imageExtent.height );
VK_RenderEndRTX( cmdbuf, g_frame.current.framebuffer.view, g_frame.current.framebuffer.image, g_frame.current.framebuffer.width, g_frame.current.framebuffer.height );
{
VkRenderPassBeginInfo rpbi = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = g_frame.rtx_enabled ? vk_frame.render_pass.after_ray_tracing : vk_frame.render_pass.raster,
.renderArea.extent.width = g_frame.create_info.imageExtent.width,
.renderArea.extent.height = g_frame.create_info.imageExtent.height,
.renderArea.extent.width = g_frame.current.framebuffer.width,
.renderArea.extent.height = g_frame.current.framebuffer.height,
.clearValueCount = ARRAYSIZE(clear_value),
.pClearValues = clear_value,
.framebuffer = g_frame.framebuffers[g_frame.swapchain_image_index],
.framebuffer = g_frame.current.framebuffer.framebuffer,
};
vkCmdBeginRenderPass(vk_core.cb, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBeginRenderPass(cmdbuf, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
}
{
const VkViewport viewport[] = {
{0.f, 0.f, (float)vk_frame.width, (float)vk_frame.height, 0.f, 1.f},
{0.f, 0.f, (float)g_frame.current.framebuffer.width, (float)g_frame.current.framebuffer.height, 0.f, 1.f},
};
const VkRect2D scissor[] = {{
{0, 0},
{vk_frame.width, vk_frame.height},
{g_frame.current.framebuffer.width, g_frame.current.framebuffer.height},
}};
vkCmdSetViewport(vk_core.cb, 0, ARRAYSIZE(viewport), viewport);
vkCmdSetScissor(vk_core.cb, 0, ARRAYSIZE(scissor), scissor);
vkCmdSetViewport(cmdbuf, 0, ARRAYSIZE(viewport), viewport);
vkCmdSetScissor(cmdbuf, 0, ARRAYSIZE(scissor), scissor);
}
if (!g_frame.rtx_enabled)
VK_RenderEnd( vk_core.cb );
VK_RenderEnd( cmdbuf );
vk2dEnd( vk_core.cb );
vk2dEnd( cmdbuf );
vkCmdEndRenderPass(vk_core.cb);
XVK_CHECK(vkEndCommandBuffer(vk_core.cb));
vkCmdEndRenderPass(cmdbuf);
g_frame.current.phase = Phase_RenderingEnqueued;
}
static void submit( VkCommandBuffer cmdbuf, qboolean wait ) {
ASSERT(g_frame.current.phase == Phase_RenderingEnqueued);
XVK_CHECK(vkEndCommandBuffer(cmdbuf));
{
const VkPipelineStageFlags stageflags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
const VkSubmitInfo subinfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = NULL,
.commandBufferCount = 1,
.pCommandBuffers = &vk_core.cb,
.pCommandBuffers = &cmdbuf,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &g_frame.image_available,
.pWaitSemaphores = g_frame.sem_framebuffer_ready + g_frame.current.index,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &g_frame.done,
.pSignalSemaphores = g_frame.sem_done + g_frame.current.index,
.pWaitDstStageMask = &stageflags,
};
XVK_CHECK(vkQueueSubmit(vk_core.queue, 1, &subinfo, g_frame.fence));
XVK_CHECK(vkQueueSubmit(vk_core.queue, 1, &subinfo, g_frame.fence_done[g_frame.current.index]));
g_frame.current.phase = Phase_Submitted;
}
{
const VkPresentInfoKHR presinfo = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.pSwapchains = &g_frame.swapchain,
.pImageIndices = &g_frame.swapchain_image_index,
.swapchainCount = 1,
.pWaitSemaphores = &g_frame.done,
.waitSemaphoreCount = 1,
};
R_VkSwapchainPresent(g_frame.current.framebuffer.index, g_frame.sem_done[g_frame.current.index]);
g_frame.current.framebuffer = (r_vk_swapchain_framebuffer_t){0};
const VkResult present_result = vkQueuePresentKHR(vk_core.queue, &presinfo);
switch (present_result)
{
case VK_ERROR_OUT_OF_DATE_KHR:
case VK_ERROR_SURFACE_LOST_KHR:
gEngine.Con_Printf(S_WARN "vkQueuePresentKHR returned %s, frame will be lost\n", resultName(present_result));
break;
default:
XVK_CHECK(present_result);
if (wait) {
APROF_SCOPE_BEGIN(frame_gpu_wait);
XVK_CHECK(vkWaitForFences(vk_core.device, 1, g_frame.fence_done + g_frame.current.index, VK_TRUE, INT64_MAX));
APROF_SCOPE_END(frame_gpu_wait);
if (vk_core.debug) {
// FIXME more scopes
XVK_CHECK(vkQueueWaitIdle(vk_core.queue));
}
g_frame.current.phase = Phase_Idle;
}
APROF_SCOPE_BEGIN(frame_gpu_wait);
// TODO bad sync
XVK_CHECK(vkWaitForFences(vk_core.device, 1, &g_frame.fence, VK_TRUE, INT64_MAX));
XVK_CHECK(vkResetFences(vk_core.device, 1, &g_frame.fence));
APROF_SCOPE_END(frame_gpu_wait);
if (vk_core.debug)
XVK_CHECK(vkQueueWaitIdle(vk_core.queue));
// TODO better sync implies multiple frames in flight, which means that we must
// retain temporary (SingleFrame) buffer contents for longer, until all users are done.
// (this probably means that we should really have some kind of refcount going on...)
// For now we can just erase these buffers now because of sync with fence
XVK_RenderBufferFrameClear();
}
g_frame.last_frame_index = g_frame.swapchain_image_index;
g_frame.swapchain_image_index = -1;
inline static VkCommandBuffer currentCommandBuffer( void ) {
return g_frame.command.buffers[g_frame.current.index];
}
void R_EndFrame( void )
{
APROF_SCOPE_BEGIN_EARLY(end_frame);
if (g_frame.current.phase == Phase_FrameBegan) {
const VkCommandBuffer cmdbuf = currentCommandBuffer();
enqueueRendering( cmdbuf );
submit( cmdbuf, false );
vk_frame.cmdbuf = VK_NULL_HANDLE;
}
APROF_SCOPE_END(end_frame);
}
@ -520,17 +352,24 @@ static void toggleRaytracing( void ) {
qboolean VK_FrameCtlInit( void )
{
PROFILER_SCOPES(APROF_SCOPE_INIT);
vk_frame.render_pass.raster = createRenderPass(false);
if (vk_core.rtx)
vk_frame.render_pass.after_ray_tracing = createRenderPass(true);
if (!createSwapchain())
const VkFormat depth_format = findSupportedImageFormat(depth_formats, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
g_frame.command = R_VkCommandPoolCreate( MAX_CONCURRENT_FRAMES );
// FIXME move this out to renderers
vk_frame.render_pass.raster = createRenderPass(depth_format, false);
if (vk_core.rtx)
vk_frame.render_pass.after_ray_tracing = createRenderPass(depth_format, true);
if (!R_VkSwapchainInit(vk_frame.render_pass.raster, depth_format))
return false;
g_frame.image_available = createSemaphore();
g_frame.done = createSemaphore();
g_frame.fence = createFence();
g_frame.swapchain_image_index = -1;
for (int i = 0; i < MAX_CONCURRENT_FRAMES; ++i) {
g_frame.sem_framebuffer_ready[i] = R_VkSemaphoreCreate();
g_frame.sem_done[i] = R_VkSemaphoreCreate();
g_frame.fence_done[i] = R_VkFenceCreate(true);
}
g_frame.rtx_enabled = vk_core.rtx;
@ -541,23 +380,26 @@ qboolean VK_FrameCtlInit( void )
return true;
}
void VK_FrameCtlShutdown( void )
{
destroyFence(g_frame.fence);
destroySemaphore(g_frame.done);
destroySemaphore(g_frame.image_available);
void VK_FrameCtlShutdown( void ) {
for (int i = 0; i < MAX_CONCURRENT_FRAMES; ++i) {
R_VkSemaphoreDestroy(g_frame.sem_framebuffer_ready[i]);
R_VkSemaphoreDestroy(g_frame.sem_done[i]);
R_VkFenceDestroy(g_frame.fence_done[i]);
}
destroySwapchain( g_frame.swapchain );
R_VkSwapchainShutdown();
vkDestroyRenderPass(vk_core.device, vk_frame.render_pass.raster, NULL);
if (vk_core.rtx)
vkDestroyRenderPass(vk_core.device, vk_frame.render_pass.after_ray_tracing, NULL);
R_VkCommandPoolDestroy( &g_frame.command );
}
static qboolean canBlitFromSwapchainToFormat( VkFormat dest_format ) {
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(vk_core.physical_device.device, g_frame.create_info.imageFormat, &props);
vkGetPhysicalDeviceFormatProperties(vk_core.physical_device.device, SWAPCHAIN_FORMAT, &props);
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) {
gEngine.Con_Reportf(S_WARN "Swapchain source format doesn't support blit\n");
return false;
@ -574,60 +416,43 @@ static qboolean canBlitFromSwapchainToFormat( VkFormat dest_format ) {
static rgbdata_t *XVK_ReadPixels( void ) {
const VkFormat dest_format = VK_FORMAT_R8G8B8A8_UNORM;
VkImage dest_image;
VkImage frame_image;
vk_devmem_t dest_devmem;
xvk_image_t dest_image;
const VkImage frame_image = g_frame.current.framebuffer.image;
rgbdata_t *r_shot = NULL;
const int
width = vk_frame.width,
height = vk_frame.height;
qboolean blit = canBlitFromSwapchainToFormat( dest_format );
const VkCommandBuffer cmdbuf = currentCommandBuffer();
if (g_frame.last_frame_index < 0)
if (frame_image == VK_NULL_HANDLE) {
gEngine.Con_Printf(S_ERROR "no current image, can't take screenshot\n");
return NULL;
frame_image = g_frame.images[g_frame.last_frame_index];
}
// Create destination image to blit/copy framebuffer pixels to
{
VkImageCreateInfo image_create_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.extent.width = width,
.extent.height = height,
.extent.depth = 1,
const xvk_image_create_t xic = {
.debug_name = "screenshot",
.width = vk_frame.width,
.height = vk_frame.height,
.mips = 1,
.layers = 1,
.format = dest_format,
.mipLevels = 1,
.arrayLayers = 1,
.tiling = VK_IMAGE_TILING_LINEAR,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.samples = VK_SAMPLE_COUNT_1_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT,
.has_alpha = false,
.is_cubemap = false,
.memory_props = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
};
XVK_CHECK(vkCreateImage(vk_core.device, &image_create_info, NULL, &dest_image));
dest_image = XVK_ImageCreate(&xic);
}
{
VkMemoryRequirements memreq;
vkGetImageMemoryRequirements(vk_core.device, dest_image, &memreq);
dest_devmem = VK_DevMemAllocate("screenshot", memreq, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, 0);
XVK_CHECK(vkBindImageMemory(vk_core.device, dest_image, dest_devmem.device_memory, dest_devmem.offset));
}
{
VkCommandBufferBeginInfo beginfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
XVK_CHECK(vkBeginCommandBuffer(vk_core.cb_tex, &beginfo));
}
// Make sure that all rendering ops are enqueued
enqueueRendering( cmdbuf );
{
// Barrier 1: dest image
VkImageMemoryBarrier image_barrier[2] = {{
const VkImageMemoryBarrier image_barrier[2] = {{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.image = dest_image,
.image = dest_image.image,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
@ -641,7 +466,7 @@ static rgbdata_t *XVK_ReadPixels( void ) {
}}, { // Barrier 2: source swapchain image
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.image = frame_image,
.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT, // ?????
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
@ -653,8 +478,8 @@ static rgbdata_t *XVK_ReadPixels( void ) {
.layerCount = 1,
}}};
vkCmdPipelineBarrier(vk_core.cb_tex,
VK_PIPELINE_STAGE_TRANSFER_BIT,
vkCmdPipelineBarrier(cmdbuf,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, NULL, 0, NULL, ARRAYSIZE(image_barrier), image_barrier);
}
@ -666,26 +491,26 @@ static rgbdata_t *XVK_ReadPixels( void ) {
.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.srcSubresource.layerCount = 1,
.dstSubresource.layerCount = 1,
.srcOffsets = {{0}, {width, height, 1}},
.dstOffsets = {{0}, {width, height, 1}}
.srcOffsets = {{0}, {vk_frame.width, vk_frame.height, 1}},
.dstOffsets = {{0}, {vk_frame.width, vk_frame.height, 1}}
};
vkCmdBlitImage(vk_core.cb_tex,
vkCmdBlitImage(cmdbuf,
frame_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dest_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_NEAREST);
dest_image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_NEAREST);
} else {
const VkImageCopy copy = {
.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.srcSubresource.layerCount = 1,
.dstSubresource.layerCount = 1,
.extent.width = width,
.extent.height = height,
.extent.width = vk_frame.width,
.extent.height = vk_frame.height,
.extent.depth = 1,
};
vkCmdCopyImage(vk_core.cb_tex,
vkCmdCopyImage(cmdbuf,
frame_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dest_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy);
dest_image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy);
gEngine.Con_Printf(S_WARN "Blit is not supported, screenshot will likely have mixed components; TODO: swizzle in software\n");
}
@ -694,7 +519,7 @@ static rgbdata_t *XVK_ReadPixels( void ) {
// Barrier 1: dest image
VkImageMemoryBarrier image_barrier[2] = {{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.image = dest_image,
.image = dest_image.image,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
@ -720,23 +545,13 @@ static rgbdata_t *XVK_ReadPixels( void ) {
.layerCount = 1,
}}};
vkCmdPipelineBarrier(vk_core.cb_tex,
VK_PIPELINE_STAGE_TRANSFER_BIT,
vkCmdPipelineBarrier(cmdbuf,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0, 0, NULL, 0, NULL, ARRAYSIZE(image_barrier), image_barrier);
}
// submit command buffer to queue
XVK_CHECK(vkEndCommandBuffer(vk_core.cb_tex));
{
VkSubmitInfo subinfo = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO};
subinfo.commandBufferCount = 1;
subinfo.pCommandBuffers = &vk_core.cb_tex;
XVK_CHECK(vkQueueSubmit(vk_core.queue, 1, &subinfo, VK_NULL_HANDLE));
}
// wait for queue
XVK_CHECK(vkQueueWaitIdle(vk_core.queue));
submit( cmdbuf, true );
// copy bytes to buffer
{
@ -744,18 +559,18 @@ static rgbdata_t *XVK_ReadPixels( void ) {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
};
VkSubresourceLayout layout;
const char *mapped = dest_devmem.mapped;
vkGetImageSubresourceLayout(vk_core.device, dest_image, &subres, &layout);
const char *mapped = dest_image.devmem.mapped;
vkGetImageSubresourceLayout(vk_core.device, dest_image.image, &subres, &layout);
mapped += layout.offset;
{
const int row_size = 4 * width;
const int row_size = 4 * vk_frame.width;
poolhandle_t r_temppool = vk_core.pool; // TODO
r_shot = Mem_Calloc( r_temppool, sizeof( rgbdata_t ));
r_shot->width = width;
r_shot->height = height;
r_shot->width = vk_frame.width;
r_shot->height = vk_frame.height;
r_shot->flags = IMAGE_HAS_COLOR;
r_shot->type = PF_RGBA_32;
r_shot->size = r_shot->width * r_shot->height * gEngine.Image_GetPFDesc( r_shot->type )->bpp;
@ -763,14 +578,14 @@ static rgbdata_t *XVK_ReadPixels( void ) {
r_shot->buffer = Mem_Malloc( r_temppool, r_shot->size );
if (!blit) {
if (dest_format != VK_FORMAT_R8G8B8A8_UNORM || g_frame.create_info.imageFormat != VK_FORMAT_B8G8R8A8_UNORM) {
gEngine.Con_Printf(S_WARN "Don't have a blit function for this format pair, will save as-is w/o conversion; expect image to look wrong\n");
if (dest_format != VK_FORMAT_R8G8B8A8_UNORM || SWAPCHAIN_FORMAT != VK_FORMAT_B8G8R8A8_UNORM) {
gEngine.Con_Printf(S_WARN "Don't have a blit function for this format pair, will save as-is without conversion; expect image to look wrong\n");
blit = true;
} else {
char *dst = r_shot->buffer;
for (int y = 0; y < height; ++y, mapped += layout.rowPitch) {
const char *src = mapped;
for (int x = 0; x < width; ++x, dst += 4, src += 4) {
byte *dst = r_shot->buffer;
for (int y = 0; y < vk_frame.height; ++y, mapped += layout.rowPitch) {
const byte *src = (const byte*)mapped;
for (int x = 0; x < vk_frame.width; ++x, dst += 4, src += 4) {
dst[0] = src[2];
dst[1] = src[1];
dst[2] = src[0];
@ -781,15 +596,14 @@ static rgbdata_t *XVK_ReadPixels( void ) {
}
if (blit) {
for (int y = 0; y < height; ++y, mapped += layout.rowPitch) {
for (int y = 0; y < vk_frame.height; ++y, mapped += layout.rowPitch) {
memcpy(r_shot->buffer + row_size * y, mapped, row_size);
}
}
}
}
vkDestroyImage(vk_core.device, dest_image, NULL);
VK_DevMemFree(&dest_devmem);
XVK_ImageDestroy( &dest_image );
return r_shot;
}

View File

@ -3,9 +3,16 @@
#include "xash3d_types.h"
#define MAX_CONCURRENT_FRAMES 2
typedef struct vk_framectl_s {
// TODO only used from 2d, remove
uint32_t width, height;
// FIXME
VkCommandBuffer cmdbuf;
// TODO move these into renderer and 2d
struct {
// Used when the entire rendering is traditional triangle rasterization
// Discards and clears color buffer

View File

@ -1,9 +1,25 @@
#include "vk_image.h"
static const VkImageUsageFlags usage_bits_implying_views =
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_STORAGE_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT |
VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT |
VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR |
VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT;
/*
VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR |
VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR |
VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR |
VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR;
*/
xvk_image_t XVK_ImageCreate(const xvk_image_create_t *create) {
xvk_image_t image;
const qboolean is_depth = !!(create->usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
xvk_image_t image = {0};
VkMemoryRequirements memreq;
VkImageViewCreateInfo ivci = {.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
VkImageCreateInfo ici = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
@ -28,22 +44,27 @@ xvk_image_t XVK_ImageCreate(const xvk_image_create_t *create) {
SET_DEBUG_NAME(image.image, VK_OBJECT_TYPE_IMAGE, create->debug_name);
vkGetImageMemoryRequirements(vk_core.device, image.image, &memreq);
image.devmem = VK_DevMemAllocate(create->debug_name, memreq, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0);
image.devmem = VK_DevMemAllocate(create->debug_name, memreq, create->memory_props ? create->memory_props : VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0);
XVK_CHECK(vkBindImageMemory(vk_core.device, image.image, image.devmem.device_memory, image.devmem.offset));
ivci.viewType = create->is_cubemap ? VK_IMAGE_VIEW_TYPE_CUBE : VK_IMAGE_VIEW_TYPE_2D;
ivci.format = ici.format;
ivci.image = image.image;
ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
ivci.subresourceRange.baseMipLevel = 0;
ivci.subresourceRange.levelCount = ici.mipLevels;
ivci.subresourceRange.baseArrayLayer = 0;
ivci.subresourceRange.layerCount = ici.arrayLayers;
ivci.components = (VkComponentMapping){0, 0, 0, create->has_alpha ? 0 : VK_COMPONENT_SWIZZLE_ONE};
XVK_CHECK(vkCreateImageView(vk_core.device, &ivci, NULL, &image.view));
if (create->usage & usage_bits_implying_views) {
const VkImageViewCreateInfo ivci = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.viewType = create->is_cubemap ? VK_IMAGE_VIEW_TYPE_CUBE : VK_IMAGE_VIEW_TYPE_2D,
.format = ici.format,
.image = image.image,
.subresourceRange.aspectMask = is_depth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT,
.subresourceRange.baseMipLevel = 0,
.subresourceRange.levelCount = ici.mipLevels,
.subresourceRange.baseArrayLayer = 0,
.subresourceRange.layerCount = ici.arrayLayers,
.components = (VkComponentMapping){0, 0, 0, (is_depth || create->has_alpha) ? 0 : VK_COMPONENT_SWIZZLE_ONE},
};
XVK_CHECK(vkCreateImageView(vk_core.device, &ivci, NULL, &image.view));
if (create->debug_name)
SET_DEBUG_NAME(image.view, VK_OBJECT_TYPE_IMAGE_VIEW, create->debug_name);
if (create->debug_name)
SET_DEBUG_NAME(image.view, VK_OBJECT_TYPE_IMAGE_VIEW, create->debug_name);
}
image.width = create->width;
image.height = create->height;

View File

@ -3,8 +3,6 @@
#include "vk_devmem.h"
typedef struct {
// FIXME better memory allocation
// OCHEN PLOHO
vk_devmem_t devmem;
VkImage image;
VkImageView view;
@ -22,6 +20,7 @@ typedef struct {
VkImageUsageFlags usage;
qboolean has_alpha;
qboolean is_cubemap;
VkMemoryPropertyFlags memory_props;
} xvk_image_create_t;
xvk_image_t XVK_ImageCreate(const xvk_image_create_t *create);

333
ref_vk/vk_misc.c Normal file
View File

@ -0,0 +1,333 @@
#include "vk_core.h"
const char *R_VkResultName(VkResult result) {
switch (result) {
case VK_SUCCESS: return "VK_SUCCESS";
case VK_NOT_READY: return "VK_NOT_READY";
case VK_TIMEOUT: return "VK_TIMEOUT";
case VK_EVENT_SET: return "VK_EVENT_SET";
case VK_EVENT_RESET: return "VK_EVENT_RESET";
case VK_INCOMPLETE: return "VK_INCOMPLETE";
case VK_ERROR_OUT_OF_HOST_MEMORY: return "VK_ERROR_OUT_OF_HOST_MEMORY";
case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
case VK_ERROR_INITIALIZATION_FAILED: return "VK_ERROR_INITIALIZATION_FAILED";
case VK_ERROR_DEVICE_LOST: return "VK_ERROR_DEVICE_LOST";
case VK_ERROR_MEMORY_MAP_FAILED: return "VK_ERROR_MEMORY_MAP_FAILED";
case VK_ERROR_LAYER_NOT_PRESENT: return "VK_ERROR_LAYER_NOT_PRESENT";
case VK_ERROR_EXTENSION_NOT_PRESENT: return "VK_ERROR_EXTENSION_NOT_PRESENT";
case VK_ERROR_FEATURE_NOT_PRESENT: return "VK_ERROR_FEATURE_NOT_PRESENT";
case VK_ERROR_INCOMPATIBLE_DRIVER: return "VK_ERROR_INCOMPATIBLE_DRIVER";
case VK_ERROR_TOO_MANY_OBJECTS: return "VK_ERROR_TOO_MANY_OBJECTS";
case VK_ERROR_FORMAT_NOT_SUPPORTED: return "VK_ERROR_FORMAT_NOT_SUPPORTED";
case VK_ERROR_FRAGMENTED_POOL: return "VK_ERROR_FRAGMENTED_POOL";
case VK_ERROR_UNKNOWN: return "VK_ERROR_UNKNOWN";
case VK_ERROR_OUT_OF_POOL_MEMORY: return "VK_ERROR_OUT_OF_POOL_MEMORY";
case VK_ERROR_INVALID_EXTERNAL_HANDLE: return "VK_ERROR_INVALID_EXTERNAL_HANDLE";
case VK_ERROR_FRAGMENTATION: return "VK_ERROR_FRAGMENTATION";
case VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS: return "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS";
case VK_ERROR_SURFACE_LOST_KHR: return "VK_ERROR_SURFACE_LOST_KHR";
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
case VK_SUBOPTIMAL_KHR: return "VK_SUBOPTIMAL_KHR";
case VK_ERROR_OUT_OF_DATE_KHR: return "VK_ERROR_OUT_OF_DATE_KHR";
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
case VK_ERROR_VALIDATION_FAILED_EXT: return "VK_ERROR_VALIDATION_FAILED_EXT";
case VK_ERROR_INVALID_SHADER_NV: return "VK_ERROR_INVALID_SHADER_NV";
case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
case VK_ERROR_NOT_PERMITTED_EXT: return "VK_ERROR_NOT_PERMITTED_EXT";
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
case VK_THREAD_IDLE_KHR: return "VK_THREAD_IDLE_KHR";
case VK_THREAD_DONE_KHR: return "VK_THREAD_DONE_KHR";
case VK_OPERATION_DEFERRED_KHR: return "VK_OPERATION_DEFERRED_KHR";
case VK_OPERATION_NOT_DEFERRED_KHR: return "VK_OPERATION_NOT_DEFERRED_KHR";
case VK_PIPELINE_COMPILE_REQUIRED_EXT: return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
default: return "UNKNOWN";
}
}
const char *R_VkPresentModeName(VkPresentModeKHR present_mode) {
switch (present_mode) {
case VK_PRESENT_MODE_IMMEDIATE_KHR: return "VK_PRESENT_MODE_IMMEDIATE_KHR";
case VK_PRESENT_MODE_MAILBOX_KHR: return "VK_PRESENT_MODE_MAILBOX_KHR";
case VK_PRESENT_MODE_FIFO_KHR: return "VK_PRESENT_MODE_FIFO_KHR";
case VK_PRESENT_MODE_FIFO_RELAXED_KHR: return "VK_PRESENT_MODE_FIFO_RELAXED_KHR";
case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: return "VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR";
case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR: return "VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR";
default: return "UNKNOWN";
}
}
const char *R_VkFormatName( VkFormat format ) {
switch (format) {
case VK_FORMAT_UNDEFINED: return "VK_FORMAT_UNDEFINED";
case VK_FORMAT_R4G4_UNORM_PACK8: return "VK_FORMAT_R4G4_UNORM_PACK8";
case VK_FORMAT_R4G4B4A4_UNORM_PACK16: return "VK_FORMAT_R4G4B4A4_UNORM_PACK16";
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: return "VK_FORMAT_B4G4R4A4_UNORM_PACK16";
case VK_FORMAT_R5G6B5_UNORM_PACK16: return "VK_FORMAT_R5G6B5_UNORM_PACK16";
case VK_FORMAT_B5G6R5_UNORM_PACK16: return "VK_FORMAT_B5G6R5_UNORM_PACK16";
case VK_FORMAT_R5G5B5A1_UNORM_PACK16: return "VK_FORMAT_R5G5B5A1_UNORM_PACK16";
case VK_FORMAT_B5G5R5A1_UNORM_PACK16: return "VK_FORMAT_B5G5R5A1_UNORM_PACK16";
case VK_FORMAT_A1R5G5B5_UNORM_PACK16: return "VK_FORMAT_A1R5G5B5_UNORM_PACK16";
case VK_FORMAT_R8_UNORM: return "VK_FORMAT_R8_UNORM";
case VK_FORMAT_R8_SNORM: return "VK_FORMAT_R8_SNORM";
case VK_FORMAT_R8_USCALED: return "VK_FORMAT_R8_USCALED";
case VK_FORMAT_R8_SSCALED: return "VK_FORMAT_R8_SSCALED";
case VK_FORMAT_R8_UINT: return "VK_FORMAT_R8_UINT";
case VK_FORMAT_R8_SINT: return "VK_FORMAT_R8_SINT";
case VK_FORMAT_R8_SRGB: return "VK_FORMAT_R8_SRGB";
case VK_FORMAT_R8G8_UNORM: return "VK_FORMAT_R8G8_UNORM";
case VK_FORMAT_R8G8_SNORM: return "VK_FORMAT_R8G8_SNORM";
case VK_FORMAT_R8G8_USCALED: return "VK_FORMAT_R8G8_USCALED";
case VK_FORMAT_R8G8_SSCALED: return "VK_FORMAT_R8G8_SSCALED";
case VK_FORMAT_R8G8_UINT: return "VK_FORMAT_R8G8_UINT";
case VK_FORMAT_R8G8_SINT: return "VK_FORMAT_R8G8_SINT";
case VK_FORMAT_R8G8_SRGB: return "VK_FORMAT_R8G8_SRGB";
case VK_FORMAT_R8G8B8_UNORM: return "VK_FORMAT_R8G8B8_UNORM";
case VK_FORMAT_R8G8B8_SNORM: return "VK_FORMAT_R8G8B8_SNORM";
case VK_FORMAT_R8G8B8_USCALED: return "VK_FORMAT_R8G8B8_USCALED";
case VK_FORMAT_R8G8B8_SSCALED: return "VK_FORMAT_R8G8B8_SSCALED";
case VK_FORMAT_R8G8B8_UINT: return "VK_FORMAT_R8G8B8_UINT";
case VK_FORMAT_R8G8B8_SINT: return "VK_FORMAT_R8G8B8_SINT";
case VK_FORMAT_R8G8B8_SRGB: return "VK_FORMAT_R8G8B8_SRGB";
case VK_FORMAT_B8G8R8_UNORM: return "VK_FORMAT_B8G8R8_UNORM";
case VK_FORMAT_B8G8R8_SNORM: return "VK_FORMAT_B8G8R8_SNORM";
case VK_FORMAT_B8G8R8_USCALED: return "VK_FORMAT_B8G8R8_USCALED";
case VK_FORMAT_B8G8R8_SSCALED: return "VK_FORMAT_B8G8R8_SSCALED";
case VK_FORMAT_B8G8R8_UINT: return "VK_FORMAT_B8G8R8_UINT";
case VK_FORMAT_B8G8R8_SINT: return "VK_FORMAT_B8G8R8_SINT";
case VK_FORMAT_B8G8R8_SRGB: return "VK_FORMAT_B8G8R8_SRGB";
case VK_FORMAT_R8G8B8A8_UNORM: return "VK_FORMAT_R8G8B8A8_UNORM";
case VK_FORMAT_R8G8B8A8_SNORM: return "VK_FORMAT_R8G8B8A8_SNORM";
case VK_FORMAT_R8G8B8A8_USCALED: return "VK_FORMAT_R8G8B8A8_USCALED";
case VK_FORMAT_R8G8B8A8_SSCALED: return "VK_FORMAT_R8G8B8A8_SSCALED";
case VK_FORMAT_R8G8B8A8_UINT: return "VK_FORMAT_R8G8B8A8_UINT";
case VK_FORMAT_R8G8B8A8_SINT: return "VK_FORMAT_R8G8B8A8_SINT";
case VK_FORMAT_R8G8B8A8_SRGB: return "VK_FORMAT_R8G8B8A8_SRGB";
case VK_FORMAT_B8G8R8A8_UNORM: return "VK_FORMAT_B8G8R8A8_UNORM";
case VK_FORMAT_B8G8R8A8_SNORM: return "VK_FORMAT_B8G8R8A8_SNORM";
case VK_FORMAT_B8G8R8A8_USCALED: return "VK_FORMAT_B8G8R8A8_USCALED";
case VK_FORMAT_B8G8R8A8_SSCALED: return "VK_FORMAT_B8G8R8A8_SSCALED";
case VK_FORMAT_B8G8R8A8_UINT: return "VK_FORMAT_B8G8R8A8_UINT";
case VK_FORMAT_B8G8R8A8_SINT: return "VK_FORMAT_B8G8R8A8_SINT";
case VK_FORMAT_B8G8R8A8_SRGB: return "VK_FORMAT_B8G8R8A8_SRGB";
case VK_FORMAT_A8B8G8R8_UNORM_PACK32: return "VK_FORMAT_A8B8G8R8_UNORM_PACK32";
case VK_FORMAT_A8B8G8R8_SNORM_PACK32: return "VK_FORMAT_A8B8G8R8_SNORM_PACK32";
case VK_FORMAT_A8B8G8R8_USCALED_PACK32: return "VK_FORMAT_A8B8G8R8_USCALED_PACK32";
case VK_FORMAT_A8B8G8R8_SSCALED_PACK32: return "VK_FORMAT_A8B8G8R8_SSCALED_PACK32";
case VK_FORMAT_A8B8G8R8_UINT_PACK32: return "VK_FORMAT_A8B8G8R8_UINT_PACK32";
case VK_FORMAT_A8B8G8R8_SINT_PACK32: return "VK_FORMAT_A8B8G8R8_SINT_PACK32";
case VK_FORMAT_A8B8G8R8_SRGB_PACK32: return "VK_FORMAT_A8B8G8R8_SRGB_PACK32";
case VK_FORMAT_A2R10G10B10_UNORM_PACK32: return "VK_FORMAT_A2R10G10B10_UNORM_PACK32";
case VK_FORMAT_A2R10G10B10_SNORM_PACK32: return "VK_FORMAT_A2R10G10B10_SNORM_PACK32";
case VK_FORMAT_A2R10G10B10_USCALED_PACK32: return "VK_FORMAT_A2R10G10B10_USCALED_PACK32";
case VK_FORMAT_A2R10G10B10_SSCALED_PACK32: return "VK_FORMAT_A2R10G10B10_SSCALED_PACK32";
case VK_FORMAT_A2R10G10B10_UINT_PACK32: return "VK_FORMAT_A2R10G10B10_UINT_PACK32";
case VK_FORMAT_A2R10G10B10_SINT_PACK32: return "VK_FORMAT_A2R10G10B10_SINT_PACK32";
case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return "VK_FORMAT_A2B10G10R10_UNORM_PACK32";
case VK_FORMAT_A2B10G10R10_SNORM_PACK32: return "VK_FORMAT_A2B10G10R10_SNORM_PACK32";
case VK_FORMAT_A2B10G10R10_USCALED_PACK32: return "VK_FORMAT_A2B10G10R10_USCALED_PACK32";
case VK_FORMAT_A2B10G10R10_SSCALED_PACK32: return "VK_FORMAT_A2B10G10R10_SSCALED_PACK32";
case VK_FORMAT_A2B10G10R10_UINT_PACK32: return "VK_FORMAT_A2B10G10R10_UINT_PACK32";
case VK_FORMAT_A2B10G10R10_SINT_PACK32: return "VK_FORMAT_A2B10G10R10_SINT_PACK32";
case VK_FORMAT_R16_UNORM: return "VK_FORMAT_R16_UNORM";
case VK_FORMAT_R16_SNORM: return "VK_FORMAT_R16_SNORM";
case VK_FORMAT_R16_USCALED: return "VK_FORMAT_R16_USCALED";
case VK_FORMAT_R16_SSCALED: return "VK_FORMAT_R16_SSCALED";
case VK_FORMAT_R16_UINT: return "VK_FORMAT_R16_UINT";
case VK_FORMAT_R16_SINT: return "VK_FORMAT_R16_SINT";
case VK_FORMAT_R16_SFLOAT: return "VK_FORMAT_R16_SFLOAT";
case VK_FORMAT_R16G16_UNORM: return "VK_FORMAT_R16G16_UNORM";
case VK_FORMAT_R16G16_SNORM: return "VK_FORMAT_R16G16_SNORM";
case VK_FORMAT_R16G16_USCALED: return "VK_FORMAT_R16G16_USCALED";
case VK_FORMAT_R16G16_SSCALED: return "VK_FORMAT_R16G16_SSCALED";
case VK_FORMAT_R16G16_UINT: return "VK_FORMAT_R16G16_UINT";
case VK_FORMAT_R16G16_SINT: return "VK_FORMAT_R16G16_SINT";
case VK_FORMAT_R16G16_SFLOAT: return "VK_FORMAT_R16G16_SFLOAT";
case VK_FORMAT_R16G16B16_UNORM: return "VK_FORMAT_R16G16B16_UNORM";
case VK_FORMAT_R16G16B16_SNORM: return "VK_FORMAT_R16G16B16_SNORM";
case VK_FORMAT_R16G16B16_USCALED: return "VK_FORMAT_R16G16B16_USCALED";
case VK_FORMAT_R16G16B16_SSCALED: return "VK_FORMAT_R16G16B16_SSCALED";
case VK_FORMAT_R16G16B16_UINT: return "VK_FORMAT_R16G16B16_UINT";
case VK_FORMAT_R16G16B16_SINT: return "VK_FORMAT_R16G16B16_SINT";
case VK_FORMAT_R16G16B16_SFLOAT: return "VK_FORMAT_R16G16B16_SFLOAT";
case VK_FORMAT_R16G16B16A16_UNORM: return "VK_FORMAT_R16G16B16A16_UNORM";
case VK_FORMAT_R16G16B16A16_SNORM: return "VK_FORMAT_R16G16B16A16_SNORM";
case VK_FORMAT_R16G16B16A16_USCALED: return "VK_FORMAT_R16G16B16A16_USCALED";
case VK_FORMAT_R16G16B16A16_SSCALED: return "VK_FORMAT_R16G16B16A16_SSCALED";
case VK_FORMAT_R16G16B16A16_UINT: return "VK_FORMAT_R16G16B16A16_UINT";
case VK_FORMAT_R16G16B16A16_SINT: return "VK_FORMAT_R16G16B16A16_SINT";
case VK_FORMAT_R16G16B16A16_SFLOAT: return "VK_FORMAT_R16G16B16A16_SFLOAT";
case VK_FORMAT_R32_UINT: return "VK_FORMAT_R32_UINT";
case VK_FORMAT_R32_SINT: return "VK_FORMAT_R32_SINT";
case VK_FORMAT_R32_SFLOAT: return "VK_FORMAT_R32_SFLOAT";
case VK_FORMAT_R32G32_UINT: return "VK_FORMAT_R32G32_UINT";
case VK_FORMAT_R32G32_SINT: return "VK_FORMAT_R32G32_SINT";
case VK_FORMAT_R32G32_SFLOAT: return "VK_FORMAT_R32G32_SFLOAT";
case VK_FORMAT_R32G32B32_UINT: return "VK_FORMAT_R32G32B32_UINT";
case VK_FORMAT_R32G32B32_SINT: return "VK_FORMAT_R32G32B32_SINT";
case VK_FORMAT_R32G32B32_SFLOAT: return "VK_FORMAT_R32G32B32_SFLOAT";
case VK_FORMAT_R32G32B32A32_UINT: return "VK_FORMAT_R32G32B32A32_UINT";
case VK_FORMAT_R32G32B32A32_SINT: return "VK_FORMAT_R32G32B32A32_SINT";
case VK_FORMAT_R32G32B32A32_SFLOAT: return "VK_FORMAT_R32G32B32A32_SFLOAT";
case VK_FORMAT_R64_UINT: return "VK_FORMAT_R64_UINT";
case VK_FORMAT_R64_SINT: return "VK_FORMAT_R64_SINT";
case VK_FORMAT_R64_SFLOAT: return "VK_FORMAT_R64_SFLOAT";
case VK_FORMAT_R64G64_UINT: return "VK_FORMAT_R64G64_UINT";
case VK_FORMAT_R64G64_SINT: return "VK_FORMAT_R64G64_SINT";
case VK_FORMAT_R64G64_SFLOAT: return "VK_FORMAT_R64G64_SFLOAT";
case VK_FORMAT_R64G64B64_UINT: return "VK_FORMAT_R64G64B64_UINT";
case VK_FORMAT_R64G64B64_SINT: return "VK_FORMAT_R64G64B64_SINT";
case VK_FORMAT_R64G64B64_SFLOAT: return "VK_FORMAT_R64G64B64_SFLOAT";
case VK_FORMAT_R64G64B64A64_UINT: return "VK_FORMAT_R64G64B64A64_UINT";
case VK_FORMAT_R64G64B64A64_SINT: return "VK_FORMAT_R64G64B64A64_SINT";
case VK_FORMAT_R64G64B64A64_SFLOAT: return "VK_FORMAT_R64G64B64A64_SFLOAT";
case VK_FORMAT_B10G11R11_UFLOAT_PACK32: return "VK_FORMAT_B10G11R11_UFLOAT_PACK32";
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: return "VK_FORMAT_E5B9G9R9_UFLOAT_PACK32";
case VK_FORMAT_D16_UNORM: return "VK_FORMAT_D16_UNORM";
case VK_FORMAT_X8_D24_UNORM_PACK32: return "VK_FORMAT_X8_D24_UNORM_PACK32";
case VK_FORMAT_D32_SFLOAT: return "VK_FORMAT_D32_SFLOAT";
case VK_FORMAT_S8_UINT: return "VK_FORMAT_S8_UINT";
case VK_FORMAT_D16_UNORM_S8_UINT: return "VK_FORMAT_D16_UNORM_S8_UINT";
case VK_FORMAT_D24_UNORM_S8_UINT: return "VK_FORMAT_D24_UNORM_S8_UINT";
case VK_FORMAT_D32_SFLOAT_S8_UINT: return "VK_FORMAT_D32_SFLOAT_S8_UINT";
case VK_FORMAT_BC1_RGB_UNORM_BLOCK: return "VK_FORMAT_BC1_RGB_UNORM_BLOCK";
case VK_FORMAT_BC1_RGB_SRGB_BLOCK: return "VK_FORMAT_BC1_RGB_SRGB_BLOCK";
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: return "VK_FORMAT_BC1_RGBA_UNORM_BLOCK";
case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: return "VK_FORMAT_BC1_RGBA_SRGB_BLOCK";
case VK_FORMAT_BC2_UNORM_BLOCK: return "VK_FORMAT_BC2_UNORM_BLOCK";
case VK_FORMAT_BC2_SRGB_BLOCK: return "VK_FORMAT_BC2_SRGB_BLOCK";
case VK_FORMAT_BC3_UNORM_BLOCK: return "VK_FORMAT_BC3_UNORM_BLOCK";
case VK_FORMAT_BC3_SRGB_BLOCK: return "VK_FORMAT_BC3_SRGB_BLOCK";
case VK_FORMAT_BC4_UNORM_BLOCK: return "VK_FORMAT_BC4_UNORM_BLOCK";
case VK_FORMAT_BC4_SNORM_BLOCK: return "VK_FORMAT_BC4_SNORM_BLOCK";
case VK_FORMAT_BC5_UNORM_BLOCK: return "VK_FORMAT_BC5_UNORM_BLOCK";
case VK_FORMAT_BC5_SNORM_BLOCK: return "VK_FORMAT_BC5_SNORM_BLOCK";
case VK_FORMAT_BC6H_UFLOAT_BLOCK: return "VK_FORMAT_BC6H_UFLOAT_BLOCK";
case VK_FORMAT_BC6H_SFLOAT_BLOCK: return "VK_FORMAT_BC6H_SFLOAT_BLOCK";
case VK_FORMAT_BC7_UNORM_BLOCK: return "VK_FORMAT_BC7_UNORM_BLOCK";
case VK_FORMAT_BC7_SRGB_BLOCK: return "VK_FORMAT_BC7_SRGB_BLOCK";
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: return "VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK";
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: return "VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK";
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: return "VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK";
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: return "VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK";
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: return "VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK";
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: return "VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK";
case VK_FORMAT_EAC_R11_UNORM_BLOCK: return "VK_FORMAT_EAC_R11_UNORM_BLOCK";
case VK_FORMAT_EAC_R11_SNORM_BLOCK: return "VK_FORMAT_EAC_R11_SNORM_BLOCK";
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK: return "VK_FORMAT_EAC_R11G11_UNORM_BLOCK";
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK: return "VK_FORMAT_EAC_R11G11_SNORM_BLOCK";
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK: return "VK_FORMAT_ASTC_4x4_UNORM_BLOCK";
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: return "VK_FORMAT_ASTC_4x4_SRGB_BLOCK";
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK: return "VK_FORMAT_ASTC_5x4_UNORM_BLOCK";
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: return "VK_FORMAT_ASTC_5x4_SRGB_BLOCK";
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK: return "VK_FORMAT_ASTC_5x5_UNORM_BLOCK";
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: return "VK_FORMAT_ASTC_5x5_SRGB_BLOCK";
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK: return "VK_FORMAT_ASTC_6x5_UNORM_BLOCK";
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: return "VK_FORMAT_ASTC_6x5_SRGB_BLOCK";
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK: return "VK_FORMAT_ASTC_6x6_UNORM_BLOCK";
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: return "VK_FORMAT_ASTC_6x6_SRGB_BLOCK";
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK: return "VK_FORMAT_ASTC_8x5_UNORM_BLOCK";
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: return "VK_FORMAT_ASTC_8x5_SRGB_BLOCK";
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK: return "VK_FORMAT_ASTC_8x6_UNORM_BLOCK";
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: return "VK_FORMAT_ASTC_8x6_SRGB_BLOCK";
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK: return "VK_FORMAT_ASTC_8x8_UNORM_BLOCK";
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: return "VK_FORMAT_ASTC_8x8_SRGB_BLOCK";
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK: return "VK_FORMAT_ASTC_10x5_UNORM_BLOCK";
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: return "VK_FORMAT_ASTC_10x5_SRGB_BLOCK";
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK: return "VK_FORMAT_ASTC_10x6_UNORM_BLOCK";
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: return "VK_FORMAT_ASTC_10x6_SRGB_BLOCK";
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK: return "VK_FORMAT_ASTC_10x8_UNORM_BLOCK";
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: return "VK_FORMAT_ASTC_10x8_SRGB_BLOCK";
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK: return "VK_FORMAT_ASTC_10x10_UNORM_BLOCK";
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: return "VK_FORMAT_ASTC_10x10_SRGB_BLOCK";
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK: return "VK_FORMAT_ASTC_12x10_UNORM_BLOCK";
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: return "VK_FORMAT_ASTC_12x10_SRGB_BLOCK";
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK: return "VK_FORMAT_ASTC_12x12_UNORM_BLOCK";
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: return "VK_FORMAT_ASTC_12x12_SRGB_BLOCK";
case VK_FORMAT_G8B8G8R8_422_UNORM: return "VK_FORMAT_G8B8G8R8_422_UNORM";
case VK_FORMAT_B8G8R8G8_422_UNORM: return "VK_FORMAT_B8G8R8G8_422_UNORM";
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM: return "VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM";
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM: return "VK_FORMAT_G8_B8R8_2PLANE_420_UNORM";
case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM: return "VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM";
case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM: return "VK_FORMAT_G8_B8R8_2PLANE_422_UNORM";
case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM: return "VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM";
case VK_FORMAT_R10X6_UNORM_PACK16: return "VK_FORMAT_R10X6_UNORM_PACK16";
case VK_FORMAT_R10X6G10X6_UNORM_2PACK16: return "VK_FORMAT_R10X6G10X6_UNORM_2PACK16";
case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16: return "VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16";
case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16: return "VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16";
case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16: return "VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16";
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16: return "VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16";
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16: return "VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16";
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16: return "VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16";
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16: return "VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16";
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16: return "VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16";
case VK_FORMAT_R12X4_UNORM_PACK16: return "VK_FORMAT_R12X4_UNORM_PACK16";
case VK_FORMAT_R12X4G12X4_UNORM_2PACK16: return "VK_FORMAT_R12X4G12X4_UNORM_2PACK16";
case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16: return "VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16";
case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16: return "VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16";
case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16: return "VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16";
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16: return "VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16";
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16: return "VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16";
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16: return "VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16";
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16: return "VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16";
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16: return "VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16";
case VK_FORMAT_G16B16G16R16_422_UNORM: return "VK_FORMAT_G16B16G16R16_422_UNORM";
case VK_FORMAT_B16G16R16G16_422_UNORM: return "VK_FORMAT_B16G16R16G16_422_UNORM";
case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM: return "VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM";
case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM: return "VK_FORMAT_G16_B16R16_2PLANE_420_UNORM";
case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM: return "VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM";
case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM: return "VK_FORMAT_G16_B16R16_2PLANE_422_UNORM";
case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM: return "VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM";
case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: return "VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG";
case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: return "VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG";
case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: return "VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG";
case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: return "VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG";
case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: return "VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG";
case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: return "VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG";
case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: return "VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG";
case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: return "VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG";
case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT: return "VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT";
case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT: return "VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT";
case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT: return "VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT";
case VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT: return "VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT";
case VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT: return "VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT";
case VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT: return "VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT";
case VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT: return "VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT";
case VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT: return "VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT";
case VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT: return "VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT";
case VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT: return "VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT";
case VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT: return "VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT";
case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT: return "VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT";
case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT: return "VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT";
case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT: return "VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT";
case VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT: return "VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT";
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT: return "VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT";
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT: return "VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT";
case VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT: return "VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT";
case VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT: return "VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT";
case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT: return "VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT";
default: return "UNKNOWN";
}
}
const char *R_VkColorSpaceName(VkColorSpaceKHR colorspace) {
switch (colorspace) {
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: return "VK_COLOR_SPACE_SRGB_NONLINEAR_KHR";
case VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT: return "VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT";
case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT: return "VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT";
case VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT: return "VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT";
case VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT: return "VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT";
case VK_COLOR_SPACE_BT709_LINEAR_EXT: return "VK_COLOR_SPACE_BT709_LINEAR_EXT";
case VK_COLOR_SPACE_BT709_NONLINEAR_EXT: return "VK_COLOR_SPACE_BT709_NONLINEAR_EXT";
case VK_COLOR_SPACE_BT2020_LINEAR_EXT: return "VK_COLOR_SPACE_BT2020_LINEAR_EXT";
case VK_COLOR_SPACE_HDR10_ST2084_EXT: return "VK_COLOR_SPACE_HDR10_ST2084_EXT";
case VK_COLOR_SPACE_DOLBYVISION_EXT: return "VK_COLOR_SPACE_DOLBYVISION_EXT";
case VK_COLOR_SPACE_HDR10_HLG_EXT: return "VK_COLOR_SPACE_HDR10_HLG_EXT";
case VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT: return "VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT";
case VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT: return "VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT";
case VK_COLOR_SPACE_PASS_THROUGH_EXT: return "VK_COLOR_SPACE_PASS_THROUGH_EXT";
case VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT: return "VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT";
case VK_COLOR_SPACE_DISPLAY_NATIVE_AMD: return "VK_COLOR_SPACE_DISPLAY_NATIVE_AMD";
case VK_COLOR_SPACE_MAX_ENUM_KHR: return "VK_COLOR_SPACE_MAX_ENUM_KHR";
default: return "UNKNOWN";
}
}

View File

@ -151,7 +151,7 @@ void XVK_RayModel_Validate( void ) {
}
}
vk_ray_model_t* VK_RayModelCreate( vk_ray_model_init_t args ) {
vk_ray_model_t* VK_RayModelCreate( VkCommandBuffer cmdbuf, vk_ray_model_init_t args ) {
VkAccelerationStructureGeometryKHR *geoms;
uint32_t *geom_max_prim_counts;
VkAccelerationStructureBuildRangeInfoKHR *geom_build_ranges;
@ -247,9 +247,9 @@ vk_ray_model_t* VK_RayModelCreate( vk_ray_model_init_t args ) {
qboolean result;
asrgs.p_accel = &ray_model->as;
DEBUG_BEGINF(vk_core.cb, "build blas for %s", args.model->debug_name);
result = createOrUpdateAccelerationStructure(vk_core.cb, &asrgs, ray_model);
DEBUG_END(vk_core.cb);
DEBUG_BEGINF(cmdbuf, "build blas for %s", args.model->debug_name);
result = createOrUpdateAccelerationStructure(cmdbuf, &asrgs, ray_model);
DEBUG_END(cmdbuf);
if (!result)
{

View File

@ -9,6 +9,7 @@
#include "vk_math.h"
#include "vk_rtx.h"
#include "vk_descriptor.h"
#include "vk_framectl.h" // FIXME
#include "eiface.h"
#include "xash3d_mathlib.h"
@ -592,7 +593,7 @@ void VK_RenderEnd( VkCommandBuffer cmdbuf )
vkCmdBindIndexBuffer(cmdbuf, g_render.buffer.buffer, 0, VK_INDEX_TYPE_UINT16);
}
vkCmdBindDescriptorSets(vk_core.cb, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 3, 1, vk_desc.ubo_sets + 1, 1, &dlights_ubo_offset);
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 3, 1, vk_desc.ubo_sets + 1, 1, &dlights_ubo_offset);
for (int i = 0; i < g_render_state.num_draw_commands; ++i) {
const draw_command_t *const draw = g_render_state.draw_commands + i;
@ -615,29 +616,29 @@ void VK_RenderEnd( VkCommandBuffer cmdbuf )
if (ubo_offset != draw->draw.ubo_offset)
{
ubo_offset = draw->draw.ubo_offset;
vkCmdBindDescriptorSets(vk_core.cb, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 0, 1, vk_desc.ubo_sets, 1, &ubo_offset);
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 0, 1, vk_desc.ubo_sets, 1, &ubo_offset);
}
if (pipeline != draw->draw.draw.render_mode) {
pipeline = draw->draw.draw.render_mode;
vkCmdBindPipeline(vk_core.cb, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipelines[pipeline]);
vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipelines[pipeline]);
}
if (lightmap != draw->draw.draw.lightmap) {
lightmap = draw->draw.draw.lightmap;
vkCmdBindDescriptorSets(vk_core.cb, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 2, 1, &findTexture(lightmap)->vk.descriptor, 0, NULL);
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 2, 1, &findTexture(lightmap)->vk.descriptor, 0, NULL);
}
if (texture != draw->draw.draw.texture)
{
texture = draw->draw.draw.texture;
// TODO names/enums for binding points
vkCmdBindDescriptorSets(vk_core.cb, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 1, 1, &findTexture(texture)->vk.descriptor, 0, NULL);
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 1, 1, &findTexture(texture)->vk.descriptor, 0, NULL);
}
// Only indexed mode is supported
ASSERT(draw->draw.draw.index_offset >= 0);
vkCmdDrawIndexed(vk_core.cb, draw->draw.draw.element_count, 1, draw->draw.draw.index_offset, draw->draw.draw.vertex_offset, 0);
vkCmdDrawIndexed(cmdbuf, draw->draw.draw.element_count, 1, draw->draw.draw.index_offset, draw->draw.draw.vertex_offset, 0);
}
}
@ -680,14 +681,14 @@ void VK_RenderEndRTX( VkCommandBuffer cmdbuf, VkImageView img_dst_view, VkImage
}
}
qboolean VK_RenderModelInit( vk_render_model_t *model ) {
qboolean VK_RenderModelInit( VkCommandBuffer cmdbuf, vk_render_model_t *model ) {
if (vk_core.rtx && (g_render_state.current_frame_is_ray_traced || !model->dynamic)) {
// TODO runtime rtx switch: ???
const vk_ray_model_init_t args = {
.buffer = g_render.buffer.buffer,
.model = model,
};
model->ray_model = VK_RayModelCreate(args);
model->ray_model = VK_RayModelCreate(cmdbuf, args);
return !!model->ray_model;
}
@ -799,7 +800,7 @@ void VK_RenderModelDynamicCommit( void ) {
if (g_dynamic_model.model.num_geometries > 0) {
g_dynamic_model.model.dynamic = true;
VK_RenderModelInit( &g_dynamic_model.model );
VK_RenderModelInit( vk_frame.cmdbuf, &g_dynamic_model.model );
VK_RenderModelDraw( NULL, &g_dynamic_model.model );
}

View File

@ -125,7 +125,7 @@ typedef struct vk_render_model_s {
struct vk_ray_model_s *ray_model;
} vk_render_model_t;
qboolean VK_RenderModelInit( vk_render_model_t* model );
qboolean VK_RenderModelInit( VkCommandBuffer cmdbuf, vk_render_model_t* model );
void VK_RenderModelDestroy( vk_render_model_t* model );
void VK_RenderModelDraw( const cl_entity_t *ent, vk_render_model_t* model );

View File

@ -11,7 +11,7 @@ typedef struct {
VkBuffer buffer; // TODO must be uniform for all models. Shall we read it directly from vk_render?
} vk_ray_model_init_t;
struct vk_ray_model_s *VK_RayModelCreate( vk_ray_model_init_t model_init );
struct vk_ray_model_s *VK_RayModelCreate( VkCommandBuffer cmdbuf, vk_ray_model_init_t model_init );
void VK_RayModelDestroy( struct vk_ray_model_s *model );
void VK_RayFrameBegin( void );

View File

@ -150,16 +150,6 @@ void R_NewMap( void ) {
if (vk_core.rtx)
VK_RayNewMap();
// RTX map loading requires command buffer for building blases
if (vk_core.rtx)
{
const VkCommandBufferBeginInfo beginfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
XVK_CHECK(vkBeginCommandBuffer(vk_core.cb, &beginfo));
}
// Load light entities and patch data prior to loading map brush model
XVK_ParseMapEntities();
@ -174,6 +164,18 @@ void R_NewMap( void ) {
// Need parsed map entities, and also should happen before brush model loading
RT_LightsNewMapBegin(map);
// RTX map loading requires command buffer for building blases
if (vk_core.rtx)
{
//ASSERT(!"Not implemented");
const VkCommandBufferBeginInfo beginfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
XVK_CHECK(vkBeginCommandBuffer(vk_core.upload_pool.buffers[0], &beginfo));
}
// Load all models at once
gEngine.Con_Reportf( "Num models: %d:\n", num_models );
for( int i = 0; i < num_models; i++ )
@ -187,7 +189,7 @@ void R_NewMap( void ) {
if( m->type != mod_brush )
continue;
if (!VK_BrushModelLoad( m, i == 0 ))
if (!VK_BrushModelLoad( vk_core.upload_pool.buffers[0], m, i == 0 ))
{
gEngine.Con_Printf( S_ERROR "Couldn't load model %s\n", m->name );
}
@ -199,13 +201,14 @@ void R_NewMap( void ) {
if (vk_core.rtx)
{
//ASSERT(!"Not implemented");
const VkSubmitInfo subinfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.commandBufferCount = 1,
.pCommandBuffers = &vk_core.cb,
.pCommandBuffers = &vk_core.upload_pool.buffers[0],
};
XVK_CHECK(vkEndCommandBuffer(vk_core.cb));
XVK_CHECK(vkEndCommandBuffer(vk_core.upload_pool.buffers[0]));
XVK_CHECK(vkQueueSubmit(vk_core.queue, 1, &subinfo, VK_NULL_HANDLE));
XVK_CHECK(vkQueueWaitIdle(vk_core.queue));
}

247
ref_vk/vk_swapchain.c Normal file
View File

@ -0,0 +1,247 @@
#include "vk_swapchain.h"
#include "vk_image.h"
#include "eiface.h" // ARRAYSIZE
extern ref_globals_t *gpGlobals;
static struct {
// These don't belong here
VkRenderPass render_pass;
VkFormat depth_format;
VkSwapchainKHR swapchain;
VkFormat image_format;
uint32_t num_images;
VkImage *images;
VkImageView *image_views;
VkFramebuffer *framebuffers;
xvk_image_t depth;
uint32_t width, height;
} g_swapchain = {0};
// TODO move to common
static uint32_t clamp_u32(uint32_t v, uint32_t min, uint32_t max) {
if (v < min) v = min;
if (v > max) v = max;
return v;
}
static void createDepthImage(int w, int h, VkFormat depth_format) {
const xvk_image_create_t xic = {
.debug_name = "depth",
.format = depth_format,
.has_alpha = false,
.is_cubemap = false,
.mips = 1,
.layers = 1,
.width = w,
.height = h,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
};
g_swapchain.depth = XVK_ImageCreate( &xic );
}
static void destroySwapchainAndFramebuffers( VkSwapchainKHR swapchain ) {
for (uint32_t i = 0; i < g_swapchain.num_images; ++i) {
vkDestroyImageView(vk_core.device, g_swapchain.image_views[i], NULL);
vkDestroyFramebuffer(vk_core.device, g_swapchain.framebuffers[i], NULL);
}
XVK_ImageDestroy( &g_swapchain.depth );
vkDestroySwapchainKHR(vk_core.device, swapchain, NULL);
}
qboolean recreateSwapchain( qboolean force ) {
const uint32_t prev_num_images = g_swapchain.num_images;
uint32_t new_width, new_height;
VkSurfaceCapabilitiesKHR surface_caps;
XVK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_core.physical_device.device, vk_core.surface.surface, &surface_caps));
new_width = surface_caps.currentExtent.width;
new_height = surface_caps.currentExtent.height;
if (new_width == 0xfffffffful || new_width == 0)
new_width = gpGlobals->width;
if (new_height == 0xfffffffful || new_height == 0)
new_height = gpGlobals->height;
new_width = clamp_u32(new_width, surface_caps.minImageExtent.width, surface_caps.maxImageExtent.width);
new_height = clamp_u32(new_height, surface_caps.minImageExtent.height, surface_caps.maxImageExtent.height);
if (new_width == g_swapchain.width && new_height == g_swapchain.height && !force)
return true;
g_swapchain.width = new_width;
g_swapchain.height = new_height;
{
VkSwapchainCreateInfoKHR create_info = {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.pNext = NULL,
.surface = vk_core.surface.surface,
.imageFormat = g_swapchain.image_format,
.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR, // TODO get from surface_formats
.imageExtent.width = g_swapchain.width,
.imageExtent.height = g_swapchain.height,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | (vk_core.rtx ? VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT : 0),
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.preTransform = surface_caps.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = VK_PRESENT_MODE_FIFO_KHR, // TODO caps, MAILBOX is better
//.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR, // TODO caps, MAILBOX is better
.clipped = VK_TRUE,
.oldSwapchain = g_swapchain.swapchain,
.minImageCount = surface_caps.minImageCount + 1,
};
if (surface_caps.maxImageCount && surface_caps.maxImageCount < create_info.minImageCount)
create_info.minImageCount = surface_caps.maxImageCount;
gEngine.Con_Printf("Creating swapchain %dx%d min_count=%d (extent %dx%d)\n",
g_swapchain.width, g_swapchain.height, create_info.minImageCount,
surface_caps.currentExtent.width, surface_caps.currentExtent.height);
XVK_CHECK(vkCreateSwapchainKHR(vk_core.device, &create_info, NULL, &g_swapchain.swapchain));
if (create_info.oldSwapchain)
destroySwapchainAndFramebuffers( create_info.oldSwapchain );
}
createDepthImage(g_swapchain.width, g_swapchain.height, g_swapchain.depth_format);
g_swapchain.num_images = 0;
XVK_CHECK(vkGetSwapchainImagesKHR(vk_core.device, g_swapchain.swapchain, &g_swapchain.num_images, NULL));
if (prev_num_images != g_swapchain.num_images)
{
if (g_swapchain.images)
{
Mem_Free(g_swapchain.images);
Mem_Free(g_swapchain.image_views);
Mem_Free(g_swapchain.framebuffers);
}
g_swapchain.images = Mem_Malloc(vk_core.pool, sizeof(*g_swapchain.images) * g_swapchain.num_images);
g_swapchain.image_views = Mem_Malloc(vk_core.pool, sizeof(*g_swapchain.image_views) * g_swapchain.num_images);
g_swapchain.framebuffers = Mem_Malloc(vk_core.pool, sizeof(*g_swapchain.framebuffers) * g_swapchain.num_images);
}
XVK_CHECK(vkGetSwapchainImagesKHR(vk_core.device, g_swapchain.swapchain, &g_swapchain.num_images, g_swapchain.images));
// TODO move this out to where render pipelines are created
for (uint32_t i = 0; i < g_swapchain.num_images; ++i) {
const VkImageViewCreateInfo ivci = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = g_swapchain.image_format,
.image = g_swapchain.images[i],
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.subresourceRange.levelCount = 1,
.subresourceRange.layerCount = 1,
};
XVK_CHECK(vkCreateImageView(vk_core.device, &ivci, NULL, g_swapchain.image_views + i));
{
const VkImageView attachments[] = {
g_swapchain.image_views[i],
g_swapchain.depth.view,
};
const VkFramebufferCreateInfo fbci = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = g_swapchain.render_pass,
.attachmentCount = ARRAYSIZE(attachments),
.pAttachments = attachments,
.width = g_swapchain.width,
.height = g_swapchain.height,
.layers = 1,
};
XVK_CHECK(vkCreateFramebuffer(vk_core.device, &fbci, NULL, g_swapchain.framebuffers + i));
}
SET_DEBUG_NAMEF(g_swapchain.images[i], VK_OBJECT_TYPE_IMAGE, "swapchain image[%d]", i);
}
return true;
}
qboolean R_VkSwapchainInit( VkRenderPass render_pass, VkFormat depth_format ) {
const uint32_t prev_num_images = g_swapchain.num_images;
VkSurfaceCapabilitiesKHR surface_caps;
XVK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_core.physical_device.device, vk_core.surface.surface, &surface_caps));
g_swapchain.image_format = VK_FORMAT_B8G8R8A8_UNORM;//SRGB, // TODO get from surface_formats
g_swapchain.render_pass = render_pass;
g_swapchain.depth_format = depth_format;
return recreateSwapchain( true );
}
void R_VkSwapchainShutdown( void ) {
destroySwapchainAndFramebuffers( g_swapchain.swapchain );
}
r_vk_swapchain_framebuffer_t R_VkSwapchainAcquire( VkSemaphore sem_image_available ) {
r_vk_swapchain_framebuffer_t ret = {0};
qboolean force_recreate = false;
for (int i = 0;; ++i) {
// Check that swapchain has the same size
recreateSwapchain(force_recreate);
const VkResult acquire_result = vkAcquireNextImageKHR(vk_core.device, g_swapchain.swapchain, UINT64_MAX, sem_image_available, VK_NULL_HANDLE, &ret.index);
switch (acquire_result) {
case VK_ERROR_OUT_OF_DATE_KHR:
case VK_ERROR_SURFACE_LOST_KHR:
gEngine.Con_Printf(S_WARN "vkAcquireNextImageKHR returned %s, recreating swapchain\n", R_VkResultName(acquire_result));
if (i == 0) {
force_recreate = true;
continue;
}
gEngine.Con_Printf(S_WARN "second vkAcquireNextImageKHR failed, frame will be lost\n", R_VkResultName(acquire_result));
return ret;
default:
XVK_CHECK(acquire_result);
}
break;
}
ret.framebuffer = g_swapchain.framebuffers[ret.index];
ret.width = g_swapchain.width;
ret.height = g_swapchain.height;
ret.image = g_swapchain.images[ret.index];
ret.view = g_swapchain.image_views[ret.index];
return ret;
}
void R_VkSwapchainPresent( uint32_t index, VkSemaphore done ) {
const VkPresentInfoKHR presinfo = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.pSwapchains = &g_swapchain.swapchain,
.pImageIndices = &index,
.swapchainCount = 1,
.pWaitSemaphores = &done,
.waitSemaphoreCount = 1,
};
const VkResult present_result = vkQueuePresentKHR(vk_core.queue, &presinfo);
switch (present_result)
{
case VK_ERROR_OUT_OF_DATE_KHR:
case VK_ERROR_SURFACE_LOST_KHR:
gEngine.Con_Printf(S_WARN "vkQueuePresentKHR returned %s, frame will be lost\n", R_VkResultName(present_result));
break;
default:
XVK_CHECK(present_result);
}
}

22
ref_vk/vk_swapchain.h Normal file
View File

@ -0,0 +1,22 @@
#include "vk_core.h"
// TODO this needs to be negotiated by swapchain creation
// however, currently render pass also needs it so ugh
#define SWAPCHAIN_FORMAT VK_FORMAT_B8G8R8A8_UNORM //SRGB
//#define SWAPCHAIN_FORMAT VK_FORMAT_B8G8R8A8_SRGB
// TODO: move render pass and depth format away from this
qboolean R_VkSwapchainInit( VkRenderPass pass, VkFormat depth_format );
void R_VkSwapchainShutdown( void );
typedef struct {
uint32_t index;
uint32_t width, height;
VkFramebuffer framebuffer; // TODO move out
VkImage image;
VkImageView view;
} r_vk_swapchain_framebuffer_t;
r_vk_swapchain_framebuffer_t R_VkSwapchainAcquire( VkSemaphore sem_image_available );
void R_VkSwapchainPresent( uint32_t index, VkSemaphore done );

View File

@ -325,8 +325,7 @@ static void VK_CreateInternalTextures( void )
}
}
static VkFormat VK_GetFormat(pixformat_t format)
{
static VkFormat VK_GetFormat(pixformat_t format) {
switch(format)
{
case PF_RGBA_32: return VK_FORMAT_R8G8B8A8_UNORM;
@ -336,8 +335,7 @@ static VkFormat VK_GetFormat(pixformat_t format)
}
}
static size_t CalcImageSize( pixformat_t format, int width, int height, int depth )
{
static size_t CalcImageSize( pixformat_t format, int width, int height, int depth ) {
size_t size = 0;
// check the depth error
@ -364,6 +362,9 @@ static size_t CalcImageSize( pixformat_t format, int width, int height, int dept
case PF_ATI2:
size = (((width + 3) >> 2) * ((height + 3) >> 2) * 16) * depth;
break;
default:
gEngine.Con_Printf(S_ERROR "unsupported pixformat_t %d\n", format);
ASSERT(!"Unsupported format encountered");
}
return size;
@ -481,6 +482,7 @@ static void BuildMipMap( byte *in, int srcWidth, int srcHeight, int srcDepth, in
static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap) {
const VkFormat format = VK_GetFormat(layers[0]->type);
int mipCount = 0;
const VkCommandBuffer cmdbuf = vk_core.upload_pool.buffers[0];
// TODO non-rbga textures
@ -569,8 +571,8 @@ static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers,
.layerCount = num_layers,
}};
XVK_CHECK(vkBeginCommandBuffer(vk_core.cb_tex, &beginfo));
vkCmdPipelineBarrier(vk_core.cb_tex,
XVK_CHECK(vkBeginCommandBuffer(cmdbuf, &beginfo));
vkCmdPipelineBarrier(cmdbuf,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, NULL, 0, NULL, 1, &image_barrier);
@ -608,7 +610,7 @@ static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers,
}
// TODO we could do this only once w/ region array
vkCmdCopyBufferToImage(vk_core.cb_tex, g_vk_buffers.staging.buffer, tex->vk.image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
vkCmdCopyBufferToImage(cmdbuf, g_vk_buffers.staging.buffer, tex->vk.image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
staging_offset += mip_size;
}
@ -627,18 +629,18 @@ static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers,
.baseArrayLayer = 0,
.layerCount = num_layers,
};
vkCmdPipelineBarrier(vk_core.cb_tex,
vkCmdPipelineBarrier(cmdbuf,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0, 0, NULL, 0, NULL, 1, &image_barrier);
XVK_CHECK(vkEndCommandBuffer(vk_core.cb_tex));
XVK_CHECK(vkEndCommandBuffer(cmdbuf));
}
{
VkSubmitInfo subinfo = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO};
subinfo.commandBufferCount = 1;
subinfo.pCommandBuffers = &vk_core.cb_tex;
subinfo.pCommandBuffers = &cmdbuf;
XVK_CHECK(vkQueueSubmit(vk_core.queue, 1, &subinfo, VK_NULL_HANDLE));
XVK_CHECK(vkQueueWaitIdle(vk_core.queue));
}
@ -834,6 +836,9 @@ void VK_FreeTexture( unsigned int texnum ) {
gEngine.FS_FreeImage( tex->original );
*/
// TODO how to do this properly?
XVK_CHECK(vkDeviceWaitIdle(vk_core.device));
XVK_ImageDestroy(&tex->vk.image);
memset(tex, 0, sizeof(*tex));