diff --git a/ref_vk/vk_2d.c b/ref_vk/vk_2d.c index 1ba39e47..dcd1a6b6 100644 --- a/ref_vk/vk_2d.c +++ b/ref_vk/vk_2d.c @@ -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; diff --git a/ref_vk/vk_brush.c b/ref_vk/vk_brush.c index b65ad61e..e15d32ad 100644 --- a/ref_vk/vk_brush.c +++ b/ref_vk/vk_brush.c @@ -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; diff --git a/ref_vk/vk_brush.h b/ref_vk/vk_brush.h index f75ce129..9c950fd2 100644 --- a/ref_vk/vk_brush.h +++ b/ref_vk/vk_brush.h @@ -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 ); diff --git a/ref_vk/vk_core.c b/ref_vk/vk_core.c index 4cb48b82..b30361d4 100644 --- a/ref_vk/vk_core.c +++ b/ref_vk/vk_core.c @@ -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); } diff --git a/ref_vk/vk_core.h b/ref_vk/vk_core.h index c09bbba0..10da888b 100644 --- a/ref_vk/vk_core.h +++ b/ref_vk/vk_core.h @@ -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) \ diff --git a/ref_vk/vk_framectl.c b/ref_vk/vk_framectl.c index d8b8843f..dc116983 100644 --- a/ref_vk/vk_framectl.c +++ b/ref_vk/vk_framectl.c @@ -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 +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, ©); + dest_image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©); 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; } diff --git a/ref_vk/vk_framectl.h b/ref_vk/vk_framectl.h index 17aea20f..a8885ff8 100644 --- a/ref_vk/vk_framectl.h +++ b/ref_vk/vk_framectl.h @@ -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 diff --git a/ref_vk/vk_image.c b/ref_vk/vk_image.c index 66f61723..a20da26b 100644 --- a/ref_vk/vk_image.c +++ b/ref_vk/vk_image.c @@ -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; diff --git a/ref_vk/vk_image.h b/ref_vk/vk_image.h index c685441d..e46d781c 100644 --- a/ref_vk/vk_image.h +++ b/ref_vk/vk_image.h @@ -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); diff --git a/ref_vk/vk_misc.c b/ref_vk/vk_misc.c new file mode 100644 index 00000000..afa1230b --- /dev/null +++ b/ref_vk/vk_misc.c @@ -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"; + } +} diff --git a/ref_vk/vk_ray_model.c b/ref_vk/vk_ray_model.c index b5fabd21..59fdc4de 100644 --- a/ref_vk/vk_ray_model.c +++ b/ref_vk/vk_ray_model.c @@ -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) { diff --git a/ref_vk/vk_render.c b/ref_vk/vk_render.c index a4768791..27a037d4 100644 --- a/ref_vk/vk_render.c +++ b/ref_vk/vk_render.c @@ -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 ); } diff --git a/ref_vk/vk_render.h b/ref_vk/vk_render.h index 0d67cdab..2ba88caf 100644 --- a/ref_vk/vk_render.h +++ b/ref_vk/vk_render.h @@ -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 ); diff --git a/ref_vk/vk_rtx.h b/ref_vk/vk_rtx.h index 04960198..2e2ca3aa 100644 --- a/ref_vk/vk_rtx.h +++ b/ref_vk/vk_rtx.h @@ -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 ); diff --git a/ref_vk/vk_scene.c b/ref_vk/vk_scene.c index 0de1c64e..442ae730 100644 --- a/ref_vk/vk_scene.c +++ b/ref_vk/vk_scene.c @@ -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)); } diff --git a/ref_vk/vk_swapchain.c b/ref_vk/vk_swapchain.c new file mode 100644 index 00000000..04ebd4b3 --- /dev/null +++ b/ref_vk/vk_swapchain.c @@ -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); + } +} diff --git a/ref_vk/vk_swapchain.h b/ref_vk/vk_swapchain.h new file mode 100644 index 00000000..84bcadff --- /dev/null +++ b/ref_vk/vk_swapchain.h @@ -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 ); diff --git a/ref_vk/vk_textures.c b/ref_vk/vk_textures.c index b92748bb..76bc073c 100644 --- a/ref_vk/vk_textures.c +++ b/ref_vk/vk_textures.c @@ -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, ®ion); + vkCmdCopyBufferToImage(cmdbuf, g_vk_buffers.staging.buffer, tex->vk.image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); 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));