#define GLFW_INCLUDE_NONE #define GLFW_INCLUDE_VULKAN #include "backends/imgui_impl_glfw.h" #include "backends/imgui_impl_vulkan.h" #include "window.hpp" #include "asset-manager.hpp" #include "camera.hpp" #include "ecs.hpp" #include "embedded-shaders.h" #include "font.hpp" #include "game-settings.hpp" #include "math-helpers.hpp" #include "pk.h" #include "plugins.hpp" #include "static-cube.hpp" #include "static-missing-texture.hpp" #include "static-ui.hpp" #include "vendor-stb-image-include.h" #include "window-types.hpp" #include "glm/ext/matrix_transform.hpp" #include struct pk_membucket *MemBkt_Vulkan = nullptr; struct pke_vkAllocData { void *data; std::size_t size; }; pk_arr_t vulkanAllocs{}; #define NELEMS(x) (sizeof(x) / sizeof((x)[0])) const bool ENABLE_VALIDATION_LAYERS = true; const bool VULKAN_DEBUG_REPORT = true; bool pke_window_did_init_imgui = false; /* * Initialization */ GLFWwindow *window = nullptr; GLFWmonitor *monitor = nullptr; GLFWvidmode vidMode; VkInstance vkInstance = nullptr; VkPhysicalDevice vkPhysicalDevice = nullptr; VkPhysicalDeviceProperties vkPhysicalDeviceProperties; VkPhysicalDeviceMemoryProperties vkPhysicalDeviceMemoryProperties; VkDevice vkDevice = nullptr; VkQueue presentQueue = nullptr; VkSurfaceKHR vkSurfaceKHR = nullptr; VkDebugReportCallbackEXT vkDebugReport = nullptr; VkAllocationCallbacks vkAllocatorStruct = {}; VkAllocationCallbacks *vkAllocator = nullptr; unsigned int graphicsFamilyIndex; unsigned int presentFamilyIndex; unsigned int transferFamilyIndex; /* * Instantiation */ bool shouldRecreateSwapchain = false; unsigned int selectedSurfaceIndex = -1u; unsigned int swapchainLength = 0u; unsigned int prevSwapchainLength = 0u; VkSwapchainKHR vkSwapchainKHR = VK_NULL_HANDLE; VkSurfaceFormatKHR vkSurfaceFormatKHR; VkFormat depthFormat; VkPresentModeKHR vkPresentModeKHR = VK_PRESENT_MODE_FIFO_KHR; VkExtent2D Extent; VkSampler global_sampler; VkSampleCountFlagBits global_sample_count; struct pkvk_queued_actions { struct pkvk_queued_actions_delete { pk_arr_t image_view{}; pk_arr_t image{}; pk_arr_t buffers{}; pk_arr_t memory{}; pk_arr_t descriptor_pool{}; } destroy; } pkvk_actn_queue; // pkvk_shared.descr_set_layout.single_sampler // the pke default, works for: // 2025-01-28 - present // 2025-01-28 - standard 3d struct PKVK_Shared { struct PKVK_Shared_Command_Pool { VkCommandPool transfer; VkCommandPool graphics; } command_pool; struct PKVK_Shared_Command_Buffer { VkCommandBuffer transfer; VkCommandBuffer graphics; } command_buffer; struct PKVK_Shared_Queue { VkQueue transfer; VkQueue graphics; } queue; } pkvk_shared; struct PKVK_Present { VkDescriptorSetLayout descriptor_set_layout; VkPipelineLayout pipeline_layout; VkPipeline pipeline; VkDescriptorPool descriptor_pool; VkDescriptorSet *descriptor_sets; VkCommandBuffer *command_buffers; VkSemaphore *semaphores_present_complete; VkSemaphore *semaphores_render_finished; VkFence *fences_in_flight; VkImage *images; VkImageView *image_views; uint32_t semaphore_index; uint32_t frame_index; } pkvk_present; // TODO consider creating a depth pre-pass // 2025-01-29 JCB // I know this is a popular thing to do, but I want to avoid it until we // actually need it. // Unsure how we would want to handle 2d - we could either intentionally // go for painter's method, or maybe there's a good way to declare depth // in 2d that I am not thinking of at the moment. VkImage *depthImages = nullptr; VkImageView *depthImageViews = nullptr; VkDeviceMemory depthImagesMemory; struct PKVK_3D { VkDeviceMemory images_memory; VkImageView *image_views = nullptr; VkImage *render_images = nullptr; VkImage *images_color_resolve = nullptr; VkImageView *image_views_color_resolve = nullptr; VkDeviceMemory images_memory_color_resolve; } pkvk_3d; struct PKVK_2D { VkImage *images = nullptr; VkImageView *image_views = nullptr; VkDeviceMemory images_memory; VkImage *images_color_resolve = nullptr; VkImageView *image_views_color_resolve = nullptr; VkDeviceMemory images_memory_color_resolve; } pkvk_2d; struct PKVK_Glyph { VkImage *images = nullptr; VkImageView *image_views = nullptr; VkDeviceMemory images_memory; VkImage *images_color_resolve = nullptr; VkImageView *image_views_color_resolve = nullptr; VkDeviceMemory images_memory_color_resolve; } pkvk_glyph; UniformBufferObject UBO{ .model = glm::mat4(1), .view = glm::lookAt(glm::vec3(0), glm::vec3(2, 2, 0), glm::vec3(0, 1, 0)), .proj = glm::mat4(1), }; VkDeviceMemory uniformBufferMemory; VkDeviceSize paddedUboBufferSize; // console const int64_t consoleBufferCount = 30; const int64_t consoleLineLength = 128; char consoleBuffer[consoleBufferCount][consoleLineLength]; int64_t consoleBufferIndex = 0; // public VkBuffer *UniformBuffers; DebugHitbox pkeDebugHitbox{}; ImplementedPKVK pkePipelines{}; pk_ev_mgr_id_T pke_ev_mgr_id_window; pk_ev_id_T pke_ev_id_framebuffer_length_changed; /* * ImGui */ VkDescriptorPool imGuiDescriptorPool; const std::vector REQUIRED_EXTENSIONS = std::vector { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; static void ImGuiCheckVkResult(VkResult err) { if (err == 0) { return; } fprintf(stderr, "[imgui][vulkan] Error: VkResult = %d\n", err); if (err < 0) { abort(); } } VkBool32 UserDebugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData) { (void)messageSeverity; (void)messageType; (void)pUserData; printf("Validation Layer: %s\n", pCallbackData->pMessage); return VK_FALSE; } static VKAPI_ATTR VkBool32 VKAPI_CALL DebugReport(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, unsigned long object, size_t location, int messageCode, const char *pLayerPrefix, const char *pMessage, void *pUserData) { (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); return VK_FALSE; } // 2025-06-10 TODO this should be called in more places or not called at all. // It's possible we won't ever actually need to use this call if we never // target a device that doesn't support depth+stencil (e.g. older android). bool pkvk_is_depth_only_format(VkFormat format) { return format == VK_FORMAT_D16_UNORM || format == VK_FORMAT_D32_SFLOAT; } unsigned int FindMemoryTypeIndex(uint32_t typeFilter, VkMemoryPropertyFlags memPropertyFlags) { for (uint32_t i = 0; i < vkPhysicalDeviceMemoryProperties.memoryTypeCount; i++) { if ((typeFilter & (1 << i)) && (vkPhysicalDeviceMemoryProperties.memoryTypes[i].propertyFlags & memPropertyFlags) == memPropertyFlags) { return i; } } fprintf(stdout, "[vulkan] Failed to find appropriate memory type index: typeFilter: %u - memPropertyFlags: %u\n", typeFilter, memPropertyFlags); return 0; } VkFormat FindSupportedFormat(long candidateCount, const VkFormat *candidates, VkImageTiling tiling, VkFormatFeatureFlags flags) { for (long i = 0; i < candidateCount; ++i) { VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(vkPhysicalDevice, candidates[i], &props); if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & flags) == flags) { return candidates[i]; } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & flags) == flags) { return candidates[i]; } } fprintf(stdout, "[vulkan] Failed to find appropriate format: tiling: %u - flags: %u\n", tiling, flags); return VkFormat(0); } VkCommandBuffer GetCommandBuffer_Graphics() { return pkvk_shared.command_buffer.graphics; } VkCommandBuffer GetCommandBuffer_Transfer() { return pkvk_shared.command_buffer.transfer; } // TODO fence void PKVK_BeginBuffer(unsigned int family_index, VkDeviceSize requestedMemorySize, PKVK_TmpBufferDetails &tmpBufferDetails, PKVK_TmpBufferFlags flags) { if (family_index == graphicsFamilyIndex) { tmpBufferDetails.queue = pkvk_shared.queue.graphics; tmpBufferDetails.cmdBuffer = pkvk_shared.command_buffer.graphics; } else if (family_index == transferFamilyIndex) { tmpBufferDetails.queue = pkvk_shared.queue.transfer; tmpBufferDetails.cmdBuffer = pkvk_shared.command_buffer.transfer; } else { throw "Unhandled queue family"; } if (requestedMemorySize == 0) return; VkBufferCreateInfo transferBufferCI{}; transferBufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; transferBufferCI.pNext = nullptr; transferBufferCI.flags = 0; transferBufferCI.size = requestedMemorySize; transferBufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; transferBufferCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; transferBufferCI.queueFamilyIndexCount = 1; transferBufferCI.pQueueFamilyIndices = &family_index; VkResult vkResult = vkCreateBuffer(vkDevice, &transferBufferCI, vkAllocator, &tmpBufferDetails.buffer); assert(vkResult == VK_SUCCESS); VkMemoryRequirements memoryRequirements; vkGetBufferMemoryRequirements(vkDevice, tmpBufferDetails.buffer, &memoryRequirements); if (memoryRequirements.size < requestedMemorySize) { fprintf(stderr, "[%s][PKVK_BeginBuffer] memory requirement is smaller than requested: %lu < %lu", __FILE__, memoryRequirements.size, requestedMemorySize); fflush(stderr); exit(1); } VkMemoryAllocateInfo transferMemAllocInfo; transferMemAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; transferMemAllocInfo.pNext = nullptr; transferMemAllocInfo.allocationSize = memoryRequirements.size; transferMemAllocInfo.memoryTypeIndex = FindMemoryTypeIndex(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); vkResult = vkAllocateMemory(vkDevice, &transferMemAllocInfo, vkAllocator, &tmpBufferDetails.deviceMemory); assert(vkResult == VK_SUCCESS); vkResult = vkBindBufferMemory(vkDevice, tmpBufferDetails.buffer, tmpBufferDetails.deviceMemory, 0); assert(vkResult == VK_SUCCESS); if (!PK_HAS_FLAG(flags, PKVK_TmpBufferFlags_MAP_MEMORY)) return; vkResult = vkMapMemory(vkDevice, tmpBufferDetails.deviceMemory, 0, transferMemAllocInfo.allocationSize, 0, &tmpBufferDetails.deviceData); assert(vkResult == VK_SUCCESS); } void PKVK_EndBuffer(PKVK_TmpBufferDetails &tmpBufferDetails, PKVK_TmpBufferFlags flags) { if (PK_HAS_FLAG(flags, PKVK_TmpBufferFlags_MAP_MEMORY)) { if (tmpBufferDetails.deviceMemory != VK_NULL_HANDLE) vkUnmapMemory(vkDevice, tmpBufferDetails.deviceMemory); } if (tmpBufferDetails.buffer != VK_NULL_HANDLE) vkDestroyBuffer(vkDevice, tmpBufferDetails.buffer, vkAllocator); if (tmpBufferDetails.deviceMemory != VK_NULL_HANDLE) vkFreeMemory(vkDevice, tmpBufferDetails.deviceMemory, vkAllocator); tmpBufferDetails = {}; } void pkvk_buffer_create(pkvk_buffer_create_data *data, pkvk_buffer_create_data_out *out) { pk_arr arr_vert_mem_reqs; memset(&arr_vert_mem_reqs, 0, sizeof(pk_arr)); arr_vert_mem_reqs.alignment = alignof(VkMemoryRequirements); arr_vert_mem_reqs.stride = sizeof(VkMemoryRequirements); arr_vert_mem_reqs.bkt = pkeSettings.mem_bkt.game_transient; /* * buffer setup */ VkBuffer tmpBuffer; VkBufferCreateInfo bufferCI{}; bufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferCI.pNext = nullptr; bufferCI.flags = {}; bufferCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; bufferCI.queueFamilyIndexCount = 1; bufferCI.pQueueFamilyIndices = &graphicsFamilyIndex; for (uint8_t i = 0; i < data->n_buffers; ++i) { bufferCI.size = data->buffer_byte_length[i]; if (i == data->index_index) { bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; bufferCI.usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; } else if (i == data->index_instance) { bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCI.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; bufferCI.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; } else { bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; bufferCI.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; } vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &tmpBuffer); VkMemoryRequirements tmp; vkGetBufferMemoryRequirements(vkDevice, tmpBuffer, &tmp); if (i == data->index_instance) { out->memory_requirements_instance = tmp; } else { pk_arr_append(&arr_vert_mem_reqs, &tmp); } vkDestroyBuffer(vkDevice, tmpBuffer, vkAllocator); } if (arr_vert_mem_reqs.next > 0) { CalculateCombinedMemReqs(arr_vert_mem_reqs.next, (VkMemoryRequirements*)arr_vert_mem_reqs.data, out->memory_requirements_vertex); } /* * memory allocation */ VkMemoryAllocateInfo vkMemoryAllocateInfo; vkMemoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; vkMemoryAllocateInfo.pNext = nullptr; // verts vkMemoryAllocateInfo.allocationSize = out->memory_requirements_vertex.size; vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(out->memory_requirements_vertex.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); if (vkMemoryAllocateInfo.allocationSize > 0) { vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &out->device_memory_vertex); } // inst vkMemoryAllocateInfo.allocationSize = out->memory_requirements_instance.size; vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(out->memory_requirements_instance.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); if (vkMemoryAllocateInfo.allocationSize > 0) { vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &out->device_memory_instance); } // data transfer PKVK_TmpBufferDetails tmpBufferDetails{}; VkDeviceSize size = 0; VkDeviceSize running_offset = 0; VkDeviceSize alignment_padding = 0; VkBufferCopy buffer_copy{}; buffer_copy.dstOffset = 0; PKVK_BeginBuffer(transferFamilyIndex, out->memory_requirements_vertex.size, tmpBufferDetails); VkCommandBufferBeginInfo vkCommandBufferBeginInfo; vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkCommandBufferBeginInfo.pNext = nullptr; // TODO consider single-use? vkCommandBufferBeginInfo.flags = 0; vkCommandBufferBeginInfo.pInheritanceInfo = nullptr; vkBeginCommandBuffer(tmpBufferDetails.cmdBuffer, &vkCommandBufferBeginInfo); for (uint8_t i = 0; i < data->n_buffers; ++i) { size = data->buffer_byte_length[i]; if (i == data->index_instance) { alignment_padding = size % out->memory_requirements_instance.alignment; alignment_padding = alignment_padding == 0 ? 0 : out->memory_requirements_instance.alignment - alignment_padding; } else { alignment_padding = size % out->memory_requirements_vertex.alignment; alignment_padding = alignment_padding == 0 ? 0 : out->memory_requirements_vertex.alignment - alignment_padding; } size += alignment_padding; bufferCI.size = size; if (i == data->index_index) { bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; bufferCI.usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; } else if (i == data->index_instance) { bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCI.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; bufferCI.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; } else { bufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; bufferCI.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; } vkCreateBuffer(vkDevice, &bufferCI, vkAllocator, &out->buffers[i]); if (i == data->index_instance) { vkBindBufferMemory(vkDevice, out->buffers[i], out->device_memory_instance, 0); } else { vkBindBufferMemory(vkDevice, out->buffers[i], out->device_memory_vertex, running_offset); } if (data->src_data[i] != nullptr) { assert(tmpBufferDetails.deviceData != nullptr); assert(data->buffer_byte_length[i] != 0); memcpy((char *)tmpBufferDetails.deviceData + running_offset, data->src_data[i], data->buffer_byte_length[i]); buffer_copy.srcOffset = running_offset; buffer_copy.size = data->buffer_byte_length[i]; running_offset += size; vkCmdCopyBuffer(tmpBufferDetails.cmdBuffer, tmpBufferDetails.buffer, out->buffers[i], 1, &buffer_copy); } } vkEndCommandBuffer(tmpBufferDetails.cmdBuffer); VkSubmitInfo submit_info; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; submit_info.waitSemaphoreCount = 0; submit_info.pWaitSemaphores = nullptr; submit_info.pWaitDstStageMask = nullptr; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &tmpBufferDetails.cmdBuffer; submit_info.signalSemaphoreCount = 0; submit_info.pSignalSemaphores = nullptr; vkQueueSubmit(tmpBufferDetails.queue, 1, &submit_info, nullptr); vkQueueWaitIdle(tmpBufferDetails.queue); PKVK_EndBuffer(tmpBufferDetails); } void pkvk_texture_upload(pkvk_texture_upload_data *data, pkvk_texture_upload_data_out *out) { uint8_t i; int txtr_x, txtr_y, txtr_chan; VkResult vkResult{}; VkImage tmpImage{}; PKVK_TmpBufferDetails tmpBufferDetails{}; VkImageViewCreateInfo vkImageViewCreateInfo{}; VkImageCreateInfo imageCreateInfo{}; VkMemoryRequirements combined_mem_reqs{}; VkBufferImageCopy2 vkBufferImageCopies[PKVK_TEXTURE_UPLOAD_ARR_MAX_LENGTH]; VkCopyBufferToImageInfo2 copy_buffer_to_image_2s[PKVK_TEXTURE_UPLOAD_ARR_MAX_LENGTH]; VkImageMemoryBarrier vkImageMemoryBarriers[PKVK_TEXTURE_UPLOAD_ARR_MAX_LENGTH]; VkMemoryRequirements image_memory_requirements[PKVK_TEXTURE_UPLOAD_ARR_MAX_LENGTH]; VkDeviceSize offsets[PKVK_TEXTURE_UPLOAD_ARR_MAX_LENGTH]; VkDeviceSize size = 0; VkDeviceSize offset = 0; VkDeviceSize padding = 0; memset(&offsets, 0, sizeof(offsets)); vkImageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; vkImageViewCreateInfo.pNext = VK_NULL_HANDLE; vkImageViewCreateInfo.flags = 0; vkImageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; vkImageViewCreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB; vkImageViewCreateInfo.components = VkComponentMapping { .r = VK_COMPONENT_SWIZZLE_IDENTITY, .g = VK_COMPONENT_SWIZZLE_IDENTITY, .b = VK_COMPONENT_SWIZZLE_IDENTITY, .a = VK_COMPONENT_SWIZZLE_IDENTITY, }; vkImageViewCreateInfo.subresourceRange = VkImageSubresourceRange { .aspectMask = VkImageAspectFlagBits::VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0u, // TODO MipMap .levelCount = 1u, .baseArrayLayer = 0u, .layerCount = 1u, }; imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCreateInfo.pNext = VK_NULL_HANDLE; imageCreateInfo.flags = 0; imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB; imageCreateInfo.extent.depth = 1; imageCreateInfo.mipLevels = 1; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.queueFamilyIndexCount = 0; imageCreateInfo.pQueueFamilyIndices = VK_NULL_HANDLE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; for (i = 0; i < data->n_textures; ++i) { vkImageMemoryBarriers[i].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; vkImageMemoryBarriers[i].pNext = VK_NULL_HANDLE; vkImageMemoryBarriers[i].srcAccessMask = {}; vkImageMemoryBarriers[i].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; vkImageMemoryBarriers[i].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; vkImageMemoryBarriers[i].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; vkImageMemoryBarriers[i].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkImageMemoryBarriers[i].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkImageMemoryBarriers[i].subresourceRange = vkImageViewCreateInfo.subresourceRange; vkBufferImageCopies[i].sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2; vkBufferImageCopies[i].pNext = VK_NULL_HANDLE; vkBufferImageCopies[i].bufferOffset = 0; vkBufferImageCopies[i].bufferRowLength = 0; vkBufferImageCopies[i].bufferImageHeight = 0; vkBufferImageCopies[i].imageSubresource = VkImageSubresourceLayers { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .baseArrayLayer = 0, .layerCount = 1, }; vkBufferImageCopies[i].imageOffset = VkOffset3D { .x = 0, .y = 0, .z = 0, }; vkBufferImageCopies[i].imageExtent = VkExtent3D { .width = 0, .height = 0, .depth = 1, }; copy_buffer_to_image_2s[i].sType = VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2; copy_buffer_to_image_2s[i].pNext = VK_NULL_HANDLE; copy_buffer_to_image_2s[i].srcBuffer = VK_NULL_HANDLE; copy_buffer_to_image_2s[i].dstImage = VK_NULL_HANDLE; copy_buffer_to_image_2s[i].dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; copy_buffer_to_image_2s[i].regionCount = 1; copy_buffer_to_image_2s[i].pRegions = &vkBufferImageCopies[i]; } /* * get memory requirements */ for (i = 0; i < data->n_textures; ++i) { if(data->texture_assets[i]->type != PKE_ASSET_TYPE_TEXTURE) { fprintf(stderr, "[pkvk_texture_upload] Expected all passed assets to be textures, got: %hhu for idx#%i.\n", static_cast(data->texture_assets[i]->type), i); return; } imageCreateInfo.extent.width = data->texture_assets[i]->details.texture.width; imageCreateInfo.extent.height = data->texture_assets[i]->details.texture.height; vkResult = vkCreateImage(vkDevice, &imageCreateInfo, vkAllocator, &tmpImage); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] Failed to create (prelim) VkImage for texture index: %i, got vkResult: %i\n", i, vkResult); } vkGetImageMemoryRequirements(vkDevice, tmpImage, &image_memory_requirements[i]); vkDestroyImage(vkDevice, tmpImage, vkAllocator); } CalculateCombinedMemReqs((uint64_t)data->n_textures, image_memory_requirements, combined_mem_reqs); /* * memory allocation */ VkMemoryAllocateInfo vkMemoryAllocateInfo; vkMemoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; vkMemoryAllocateInfo.pNext = nullptr; vkMemoryAllocateInfo.allocationSize = combined_mem_reqs.size; vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_mem_reqs.memoryTypeBits, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); if (vkMemoryAllocateInfo.memoryTypeIndex == 0) { vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_mem_reqs.memoryTypeBits, 0); } vkResult = vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &out->device_memory); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] failed to allocate device_memory, expected VK_SUCCESS, got vkResult: %i.\n", vkResult); return; } /* * buffer setup */ char *temp_buffer = (char *)malloc(combined_mem_reqs.size); if (temp_buffer == nullptr) { fprintf(stderr, "[pkvk_texture_upload] failed to malloc temp buffer.\n"); goto pkvk_texture_err_out; } // copy data to temp buffer // 2025-08-06 - JCB - PERF TODO actually test performance // It's possible that copying these to a temporary buffer is a performance loss. // The idea here is to separate this number-crunching from the buffer recording. // If this texture upload function ends up being a bottleneck here's a few suggestions: // - split stbi work to separate threads (likely biggest impact) // - reduce # of data-copies (copy straight to the vulkan buffer instead of intermediary) for (i = 0; i < data->n_textures; ++i) { /* run texture data through stbi, putting it in the VkFormat we desire, & copy */ stbi_uc *txtr_bytes = stbi_load_from_memory((unsigned char*)data->texture_assets[i]->ptr, data->texture_assets[i]->size, &txtr_x, &txtr_y, &txtr_chan, 4); assert(txtr_bytes != nullptr); assert(txtr_chan == 4); size = txtr_x * txtr_y * txtr_chan; memcpy(temp_buffer + offset, txtr_bytes, size); free(txtr_bytes); // create image imageCreateInfo.extent.width = data->texture_assets[i]->details.texture.width; imageCreateInfo.extent.height = data->texture_assets[i]->details.texture.height; vkResult = vkCreateImage(vkDevice, &imageCreateInfo, vkAllocator, &out->images[i]); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] Failed to create VkImage for texture index: %i, got vkResult: %i\n", i, vkResult); goto pkvk_texture_err_out; } vkResult = vkBindImageMemory(vkDevice, out->images[i], out->device_memory, offset); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] Failed to bind image memory for texture index: %i, got vkResult: %i\n", i, vkResult); goto pkvk_texture_err_out; } // create image view vkImageViewCreateInfo.image = out->images[i]; vkResult = vkCreateImageView(vkDevice, &vkImageViewCreateInfo, vkAllocator, &out->image_views[i]); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] Failed to create vkImageView for texture index: %i, got vkResult: %i\n", i, vkResult); goto pkvk_texture_err_out; } // set up image copy parameters vkBufferImageCopies[i].bufferOffset = offset; vkBufferImageCopies[i].bufferRowLength = data->texture_assets[i]->details.texture.width; vkBufferImageCopies[i].bufferImageHeight = data->texture_assets[i]->details.texture.height; vkBufferImageCopies[i].imageExtent.width = data->texture_assets[i]->details.texture.width; vkBufferImageCopies[i].imageExtent.height = data->texture_assets[i]->details.texture.height; vkImageMemoryBarriers[i].image = out->images[i]; offset += size; padding = (combined_mem_reqs.alignment - (size % combined_mem_reqs.alignment)) % combined_mem_reqs.alignment; offset += padding; } /* * transition layout & copy to buffer */ PKVK_BeginBuffer(transferFamilyIndex, combined_mem_reqs.size, tmpBufferDetails); // copy data to vulkan buffer memcpy(tmpBufferDetails.deviceData, temp_buffer, combined_mem_reqs.size); free(temp_buffer); temp_buffer = nullptr; VkCommandBufferBeginInfo vkCommandBufferBeginInfo; vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkCommandBufferBeginInfo.pNext = nullptr; vkCommandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkCommandBufferBeginInfo.pInheritanceInfo = nullptr; vkResult = vkBeginCommandBuffer(tmpBufferDetails.cmdBuffer, &vkCommandBufferBeginInfo); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] Failed to begin command buffer (temp buffer), got vkResult: %i\n", vkResult); goto pkvk_texture_err_out; } vkCmdPipelineBarrier(tmpBufferDetails.cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, (uint32_t)data->n_textures, vkImageMemoryBarriers); for (i = 0; i < data->n_textures; ++i) { copy_buffer_to_image_2s[i].dstImage = out->images[i]; copy_buffer_to_image_2s[i].srcBuffer = tmpBufferDetails.buffer; vkCmdCopyBufferToImage2(tmpBufferDetails.cmdBuffer, ©_buffer_to_image_2s[i]); } vkResult = vkEndCommandBuffer(tmpBufferDetails.cmdBuffer); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] Failed to end command buffer (temp buffer), got vkResult: %i\n", vkResult); goto pkvk_texture_err_out; } VkSubmitInfo submitInfo; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = nullptr; submitInfo.pWaitDstStageMask = nullptr; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &tmpBufferDetails.cmdBuffer; submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = nullptr; vkResult = vkQueueSubmit(tmpBufferDetails.queue, 1, &submitInfo, nullptr); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] Failed to submit command buffer to queue (temp buffer), got vkResult: %i\n", vkResult); goto pkvk_texture_err_out; } vkResult = vkQueueWaitIdle(tmpBufferDetails.queue); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] Failed to wait queue idle (temp buffer), got vkResult: %i\n", vkResult); goto pkvk_texture_err_out; } vkResult = vkResetCommandBuffer(tmpBufferDetails.cmdBuffer, 0); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] Failed to reset command buffer (temp buffer), got vkResult: %i\n", vkResult); goto pkvk_texture_err_out; } PKVK_EndBuffer(tmpBufferDetails); // update command buffer data for (i = 0; i < data->n_textures; ++i) { vkImageMemoryBarriers[i].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; vkImageMemoryBarriers[i].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; vkImageMemoryBarriers[i].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; vkImageMemoryBarriers[i].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } tmpBufferDetails = {}; PKVK_BeginBuffer(graphicsFamilyIndex, 0, tmpBufferDetails); { VkCommandBufferBeginInfo vkCommandBufferBeginInfo; vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkCommandBufferBeginInfo.pNext = nullptr; vkCommandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkCommandBufferBeginInfo.pInheritanceInfo = nullptr; vkResult = vkBeginCommandBuffer(tmpBufferDetails.cmdBuffer, &vkCommandBufferBeginInfo); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] Failed to begin command buffer, got vkResult: %i\n", vkResult); goto pkvk_texture_err_out; } vkCmdPipelineBarrier(tmpBufferDetails.cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, (uint32_t)data->n_textures, vkImageMemoryBarriers); vkResult = vkEndCommandBuffer(tmpBufferDetails.cmdBuffer); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] Failed to end command buffer, got vkResult: %i\n", vkResult); goto pkvk_texture_err_out; } VkSubmitInfo submitInfo; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = nullptr; submitInfo.pWaitDstStageMask = nullptr; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &tmpBufferDetails.cmdBuffer; submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = nullptr; vkResult = vkQueueSubmit(tmpBufferDetails.queue, 1, &submitInfo, nullptr); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] Failed to submit command buffer to queue, got vkResult: %i\n", vkResult); goto pkvk_texture_err_out; } vkResult = vkQueueWaitIdle(tmpBufferDetails.queue); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] Failed to wait queue idle, got vkResult: %i\n", vkResult); goto pkvk_texture_err_out; } vkResult = vkResetCommandBuffer(tmpBufferDetails.cmdBuffer, 0); if (vkResult != VK_SUCCESS) { fprintf(stderr, "[pkvk_texture_upload] Failed to reset command buffer , got vkResult: %i\n", vkResult); goto pkvk_texture_err_out; } } PKVK_EndBuffer(tmpBufferDetails); return; pkvk_texture_err_out: for (i = 0; i < data->n_textures; ++i) { if (out->image_views[i] != VK_NULL_HANDLE) { vkDestroyImageView(vkDevice, out->image_views[i], vkAllocator); } if (out->images[i] != VK_NULL_HANDLE) { vkDestroyImage(vkDevice, out->images[i], vkAllocator); } } if (out->device_memory != VK_NULL_HANDLE) { vkFreeMemory(vkDevice, out->device_memory, vkAllocator); } if (temp_buffer != nullptr) { free(temp_buffer); } // swallow vkResult vkResetCommandBuffer(tmpBufferDetails.cmdBuffer, 0); return; } void pkvk_texture_upload_array(pkvk_texture_upload_data *data, pkvk_texture_upload_data_out *out) { uint8_t i; int txtr_x, txtr_y, txtr_chan; VkResult vkResult{}; VkImage tmpImage{}; PKVK_TmpBufferDetails tmpBufferDetails{}; VkImageViewCreateInfo vkImageViewCreateInfo{}; VkImageCreateInfo imageCreateInfo{}; VkMemoryRequirements combined_mem_reqs{}; VkBufferImageCopy2 vkBufferImageCopies[PKVK_TEXTURE_UPLOAD_ARR_MAX_LENGTH]; VkCopyBufferToImageInfo2 copy_buffer_to_image_2{}; VkImageMemoryBarrier vkImageMemoryBarrier{}; VkDeviceSize size = 0; VkDeviceSize offset = 0; vkImageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; vkImageViewCreateInfo.pNext = VK_NULL_HANDLE; vkImageViewCreateInfo.flags = 0; vkImageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; vkImageViewCreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB; vkImageViewCreateInfo.components = VkComponentMapping { .r = VK_COMPONENT_SWIZZLE_IDENTITY, .g = VK_COMPONENT_SWIZZLE_IDENTITY, .b = VK_COMPONENT_SWIZZLE_IDENTITY, .a = VK_COMPONENT_SWIZZLE_IDENTITY, }; vkImageViewCreateInfo.subresourceRange = VkImageSubresourceRange { .aspectMask = VkImageAspectFlagBits::VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0u, // TODO MipMap .levelCount = 1u, .baseArrayLayer = 0u, .layerCount = (uint32_t)data->n_textures, }; imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCreateInfo.pNext = VK_NULL_HANDLE; imageCreateInfo.flags = 0; imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB; imageCreateInfo.extent.width = data->texture_assets[0]->details.texture.width; imageCreateInfo.extent.height = data->texture_assets[0]->details.texture.height; imageCreateInfo.extent.depth = 1; imageCreateInfo.mipLevels = 1; imageCreateInfo.arrayLayers = data->n_textures; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.queueFamilyIndexCount = 0; imageCreateInfo.pQueueFamilyIndices = VK_NULL_HANDLE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageCreateInfo.extent = { .width = data->texture_assets[0]->details.texture.width, .height = data->texture_assets[0]->details.texture.height, .depth = 1, }; vkImageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; vkImageMemoryBarrier.pNext = VK_NULL_HANDLE; vkImageMemoryBarrier.srcAccessMask = {}; vkImageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; vkImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; vkImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; vkImageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkImageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkImageMemoryBarrier.subresourceRange = vkImageViewCreateInfo.subresourceRange; for (i = 0; i < data->n_textures; ++i) { vkBufferImageCopies[i].sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2; vkBufferImageCopies[i].pNext = VK_NULL_HANDLE; vkBufferImageCopies[i].bufferOffset = 0; vkBufferImageCopies[i].bufferRowLength = data->texture_assets[0]->details.texture.width; vkBufferImageCopies[i].bufferImageHeight = data->texture_assets[0]->details.texture.width; vkBufferImageCopies[i].bufferRowLength = 0; vkBufferImageCopies[i].bufferImageHeight = 0; vkBufferImageCopies[i].imageSubresource = VkImageSubresourceLayers { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .baseArrayLayer = i, .layerCount = 1, }; vkBufferImageCopies[i].imageOffset = VkOffset3D { .x = 0, .y = 0, .z = 0, }; vkBufferImageCopies[i].imageExtent = VkExtent3D { .width = data->texture_assets[0]->details.texture.width, .height = data->texture_assets[0]->details.texture.height, .depth = 1, }; } copy_buffer_to_image_2.sType = VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2; copy_buffer_to_image_2.pNext = VK_NULL_HANDLE; copy_buffer_to_image_2.srcBuffer = VK_NULL_HANDLE; copy_buffer_to_image_2.dstImage = VK_NULL_HANDLE; copy_buffer_to_image_2.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; copy_buffer_to_image_2.regionCount = (uint32_t)data->n_textures; copy_buffer_to_image_2.pRegions = vkBufferImageCopies; /* * get memory requirements */ for (i = 0; i < data->n_textures; ++i) { if(data->texture_assets[i]->type != PKE_ASSET_TYPE_TEXTURE) { fprintf(stderr, "[pkvk_texture_upload] Expected all passed assets to be textures, got: %hhu for idx#%i.", static_cast(data->texture_assets[i]->type), i); return; } if (data->texture_assets[i]->details.texture.width != data->texture_assets[0]->details.texture.width || data->texture_assets[i]->details.texture.height != data->texture_assets[0]->details.texture.height) { fprintf(stderr, "[pkvk_texture_upload] Requested array but the dimensions of texture (idx %i) did not match.", i); return; } } vkResult = vkCreateImage(vkDevice, &imageCreateInfo, vkAllocator, &tmpImage); assert(vkResult == VK_SUCCESS); vkGetImageMemoryRequirements(vkDevice, tmpImage, &combined_mem_reqs); vkDestroyImage(vkDevice, tmpImage, vkAllocator); /* * memory allocation */ VkMemoryAllocateInfo vkMemoryAllocateInfo; vkMemoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; vkMemoryAllocateInfo.pNext = nullptr; vkMemoryAllocateInfo.allocationSize = combined_mem_reqs.size; vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_mem_reqs.memoryTypeBits, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); if (vkMemoryAllocateInfo.memoryTypeIndex == 0) { vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combined_mem_reqs.memoryTypeBits, 0); } vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &out->device_memory); /* * buffer setup */ char *temp_buffer = (char *)malloc(combined_mem_reqs.size); // copy data to temp buffer // 2025-08-06 - JCB - PERF TODO actually test performance // It's possible that copying these to a temporary buffer is a performance loss. // The idea here is to separate this number-crunching from the buffer recording. // If this texture upload function ends up being a bottleneck here's a few suggestions: // - split stbi work to separate threads (likely biggest impact) // - reduce # of data-copies (copy straight to the vulkan buffer instead of intermediary) for (i = 0; i < data->n_textures; ++i) { vkBufferImageCopies[i].bufferOffset = offset; /* run texture data through stbi, putting it in the VkFormat we desire, & copy */ stbi_uc *txtr_bytes = stbi_load_from_memory((unsigned char*)data->texture_assets[i]->ptr, data->texture_assets[i]->size, &txtr_x, &txtr_y, &txtr_chan, 4); assert(txtr_bytes != nullptr); assert(txtr_chan == 4); size = sizeof(uint8_t) * txtr_x * txtr_y * txtr_chan; memcpy(temp_buffer + offset, txtr_bytes, size); free(txtr_bytes); offset += size; } // create image vkResult = vkCreateImage(vkDevice, &imageCreateInfo, vkAllocator, &out->images[0]); assert(vkResult == VK_SUCCESS); vkResult = vkBindImageMemory(vkDevice, out->images[0], out->device_memory, 0); assert(vkResult == VK_SUCCESS); // create image view vkImageViewCreateInfo.image = out->images[0]; vkResult = vkCreateImageView(vkDevice, &vkImageViewCreateInfo, vkAllocator, &out->image_views[0]); assert(vkResult == VK_SUCCESS); // set up image copy parameters vkImageMemoryBarrier.image = out->images[0]; /* * transition layout & copy to buffer */ PKVK_BeginBuffer(transferFamilyIndex, combined_mem_reqs.size, tmpBufferDetails); // copy data to vulkan buffer memcpy(tmpBufferDetails.deviceData, temp_buffer, combined_mem_reqs.size); free(temp_buffer); VkCommandBufferBeginInfo vkCommandBufferBeginInfo; vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkCommandBufferBeginInfo.pNext = nullptr; vkCommandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkCommandBufferBeginInfo.pInheritanceInfo = nullptr; vkBeginCommandBuffer(tmpBufferDetails.cmdBuffer, &vkCommandBufferBeginInfo); vkCmdPipelineBarrier(tmpBufferDetails.cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &vkImageMemoryBarrier); copy_buffer_to_image_2.dstImage = out->images[0]; copy_buffer_to_image_2.srcBuffer = tmpBufferDetails.buffer; vkCmdCopyBufferToImage2(tmpBufferDetails.cmdBuffer, ©_buffer_to_image_2); vkEndCommandBuffer(tmpBufferDetails.cmdBuffer); VkSubmitInfo submitInfo; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = nullptr; submitInfo.pWaitDstStageMask = nullptr; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &tmpBufferDetails.cmdBuffer; submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = nullptr; vkQueueSubmit(tmpBufferDetails.queue, 1, &submitInfo, nullptr); vkQueueWaitIdle(tmpBufferDetails.queue); vkResetCommandBuffer(tmpBufferDetails.cmdBuffer, 0); PKVK_EndBuffer(tmpBufferDetails); // update command buffer data vkImageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; vkImageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; vkImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; vkImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; tmpBufferDetails = {}; PKVK_BeginBuffer(graphicsFamilyIndex, 0, tmpBufferDetails); { VkCommandBufferBeginInfo vkCommandBufferBeginInfo; vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkCommandBufferBeginInfo.pNext = nullptr; vkCommandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkCommandBufferBeginInfo.pInheritanceInfo = nullptr; vkBeginCommandBuffer(tmpBufferDetails.cmdBuffer, &vkCommandBufferBeginInfo); vkCmdPipelineBarrier(tmpBufferDetails.cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &vkImageMemoryBarrier); vkEndCommandBuffer(tmpBufferDetails.cmdBuffer); VkSubmitInfo submitInfo; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = nullptr; submitInfo.pWaitDstStageMask = nullptr; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &tmpBufferDetails.cmdBuffer; submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = nullptr; vkQueueSubmit(tmpBufferDetails.queue, 1, &submitInfo, nullptr); vkQueueWaitIdle(tmpBufferDetails.queue); } PKVK_EndBuffer(tmpBufferDetails); } void pkvk_queue_vk_image_view_destroy(VkImageView image_view) { pk_arr_append_t(&pkvk_actn_queue.destroy.image_view, image_view); } void pkvk_queue_vk_image_destroy(VkImage image) { pk_arr_append_t(&pkvk_actn_queue.destroy.image, image); } void pkvk_queue_vk_buffer_destroy(VkBuffer buffer) { pk_arr_append_t(&pkvk_actn_queue.destroy.buffers, buffer); } void pkvk_queue_vk_memory_free(VkDeviceMemory memory) { pk_arr_append_t(&pkvk_actn_queue.destroy.memory, memory); } void pkvk_queue_vk_descriptor_pool_destroy(VkDescriptorPool descriptor_pool) { pk_arr_append_t(&pkvk_actn_queue.destroy.descriptor_pool, descriptor_pool); } unsigned int FindQueueFamilyIndex(VkPhysicalDevice device, char hasPresentSupport = -1, VkQueueFlagBits includeBits = (VkQueueFlagBits)0U, VkQueueFlagBits excludeBits = (VkQueueFlagBits)0U) { if (hasPresentSupport == -1 && includeBits == 0 && excludeBits == 0) { return 0U; } unsigned int queueFamilyPropertyCount = 0U; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyPropertyCount, nullptr); auto *queueFamilyProperties = pk_new_arr(queueFamilyPropertyCount, pkeSettings.mem_bkt.game_transient); vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyPropertyCount, queueFamilyProperties); for (unsigned int i = 0; i < queueFamilyPropertyCount; i++) { if (includeBits != 0 && (queueFamilyProperties[i].queueFlags & includeBits) == 0) { continue; } if (excludeBits != 0 && (queueFamilyProperties[i].queueFlags & excludeBits) != 0) { continue; } if (hasPresentSupport != -1) { VkBool32 presentSupport; VkResult vkResult = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, vkSurfaceKHR, &presentSupport); assert(vkResult == VK_SUCCESS); if (presentSupport != (VkBool32)hasPresentSupport) { continue; } } return i; } return 0xFFFFFFFF; } void PKE_vkFreeFunction(void *pUserData, void *pMemory) { (void)pUserData; if (pMemory == nullptr) return; pke_vkAllocData *chunk = nullptr; uint32_t index, i, count; count = vulkanAllocs.next; for (i = 0; i < count; ++i) { if (vulkanAllocs[i].data == pMemory) { chunk = &vulkanAllocs[i]; index = i; break; } } if (chunk != nullptr) { assert(chunk != nullptr); pk_delete_bkt(chunk->data, chunk->size, MemBkt_Vulkan); pk_arr_remove_at(&vulkanAllocs, index); } else { fprintf(stderr, "%s\n", "PKE_vkFreeFunction - untracked pointer"); } } void *PKE_vkAllocateFunction(void *pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocScope) { (void)pUserData; (void)allocScope; if (size == 0) { return nullptr; } void *ptr = pk_new_bkt(size, alignment, MemBkt_Vulkan); pk_arr_append_t(&vulkanAllocs, { .data = ptr, .size = size, }); return ptr; } void *PKE_vkReallocationFunction(void *pUserData, void *pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocScope) { (void)pUserData; if (pOriginal == nullptr) { return PKE_vkAllocateFunction(pUserData, size, alignment, allocScope); } if (size == 0) { PKE_vkFreeFunction(pUserData, pOriginal); return nullptr; } pke_vkAllocData *chunk = nullptr; uint32_t index, i, count; count = vulkanAllocs.next; for (i = 0; i < count; ++i) { if (vulkanAllocs[i].data == pOriginal) { chunk = &(vulkanAllocs[i]); index = i; break; } } if (chunk != nullptr) { if (chunk->size >= size) { return pOriginal; } } void *newPtr = PKE_vkAllocateFunction(pUserData, size, alignment, allocScope); if (chunk != nullptr) { memcpy(newPtr, chunk->data, (chunk->size < size ? chunk->size : size) - 1); pk_delete_bkt(chunk->data, chunk->size, MemBkt_Vulkan); pk_arr_remove_at(&vulkanAllocs, index); } else { } return newPtr; } void PKE_vkInternalAllocationNotification(void *pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope) { (void)pUserData; (void)size; (void)allocationType; (void)allocationScope; return; } void PKE_vkInternalFreeNotification(void *pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope) { (void)pUserData; (void)size; (void)allocationType; (void)allocationScope; return; } void InitVulkan() { vkAllocator = pk_new(MemBkt_Vulkan); vkAllocator->pUserData = nullptr; vkAllocator->pfnAllocation = PKE_vkAllocateFunction; vkAllocator->pfnReallocation = PKE_vkReallocationFunction; vkAllocator->pfnFree = PKE_vkFreeFunction; vkAllocator->pfnInternalAllocation = PKE_vkInternalAllocationNotification; vkAllocator->pfnInternalFree = PKE_vkInternalFreeNotification; VkResult vkResult; if (ENABLE_VALIDATION_LAYERS) { unsigned int layerCount; vkResult = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); assert(vkResult == VK_SUCCESS); VkLayerProperties *availableLayerProperties = pk_new_arr(layerCount, pkeSettings.mem_bkt.game_transient); vkResult = vkEnumerateInstanceLayerProperties(&layerCount, availableLayerProperties); assert(vkResult == VK_SUCCESS); printf("Available Layers:\n"); for (long i = 0; i < layerCount; ++i) { printf("\t%s\n", availableLayerProperties[i].layerName); } } VkApplicationInfo appInfo; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pNext = nullptr; appInfo.pApplicationName = "pikul"; appInfo.applicationVersion = 1; appInfo.pEngineName = "pikul"; appInfo.engineVersion = 1; appInfo.apiVersion = VK_API_VERSION_1_3; VkInstanceCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pNext = nullptr; createInfo.flags = {}; createInfo.pApplicationInfo = &appInfo; createInfo.enabledLayerCount = 0; std::vector enabledLayerNames(1); enabledLayerNames[0] = "VK_LAYER_KHRONOS_validation"; printf("Requested Layers:\n"); for (const char *s : enabledLayerNames) { printf("\t%s\n", s); } VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo; debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; debugCreateInfo.pNext = nullptr; debugCreateInfo.flags = {}; if (ENABLE_VALIDATION_LAYERS) { createInfo.enabledLayerCount = enabledLayerNames.size(); createInfo.ppEnabledLayerNames = enabledLayerNames.data(); debugCreateInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; debugCreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; debugCreateInfo.pfnUserCallback = UserDebugCallback; debugCreateInfo.pUserData = nullptr; createInfo.pNext = &debugCreateInfo; } std::vector allGlfwExtensions; { unsigned int extensionCount = 0; const char **glfwExtensions = glfwGetRequiredInstanceExtensions(&extensionCount); allGlfwExtensions.reserve(extensionCount + 2); copy(&glfwExtensions[0], &glfwExtensions[extensionCount], back_inserter(allGlfwExtensions)); if (ENABLE_VALIDATION_LAYERS) { allGlfwExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); allGlfwExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); } createInfo.enabledExtensionCount = allGlfwExtensions.size(); createInfo.ppEnabledExtensionNames = allGlfwExtensions.data(); } printf("Required Extensions:\n"); for (const auto &ext : allGlfwExtensions) { printf("\t%s\n", ext); } if (ENABLE_VALIDATION_LAYERS) { unsigned int extensionCount = 0; vkResult = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); assert(vkResult == VK_SUCCESS); auto *extensions = pk_new_arr(extensionCount, pkeSettings.mem_bkt.game_transient); vkResult = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions); assert(vkResult == VK_SUCCESS); printf("Available Extensions:\n"); for (long i = 0; i < extensionCount; ++i) { printf("\t%s\n", extensions[i].extensionName); } } vkResult = vkCreateInstance(&createInfo, vkAllocator, &vkInstance); if (vkResult != VK_SUCCESS) { printf("Failed to create VkInstance! : %d\n", vkResult); throw vkResult; } if (VULKAN_DEBUG_REPORT) { PFN_vkCreateDebugReportCallbackEXT func = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(vkInstance, "vkCreateDebugReportCallbackEXT"); assert(func != nullptr && "vkCreateDebugReportCallbackEXT ProcAddr was nullptr"); VkDebugReportCallbackCreateInfoEXT debugReportCallbackCreateInfo; debugReportCallbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; debugReportCallbackCreateInfo.pNext = nullptr; debugReportCallbackCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; debugReportCallbackCreateInfo.pfnCallback = DebugReport; debugReportCallbackCreateInfo.pUserData = nullptr; vkResult = func(vkInstance, &debugReportCallbackCreateInfo, vkAllocator, &vkDebugReport); if (vkResult != VK_SUCCESS) { fprintf(stderr, "%s\n", "Failed to create debug report!"); } } // create surface if (glfwCreateWindowSurface(vkInstance, window, vkAllocator, &vkSurfaceKHR) != VK_SUCCESS) { throw "Failed to create window surface"; } // pick physical device unsigned int physicalDeviceCount = 0; vkResult = vkEnumeratePhysicalDevices(vkInstance, &physicalDeviceCount, nullptr); assert(vkResult == VK_SUCCESS); assert(physicalDeviceCount > 0); auto *physicalDevices = pk_new_arr(physicalDeviceCount, pkeSettings.mem_bkt.game_transient); vkResult = vkEnumeratePhysicalDevices(vkInstance, &physicalDeviceCount, physicalDevices); assert(vkResult == VK_SUCCESS); graphicsFamilyIndex = 0; presentFamilyIndex = 0; for (long i = 0; i < physicalDeviceCount; ++i) { const auto &device = physicalDevices[i]; // check queue families graphicsFamilyIndex = FindQueueFamilyIndex(device, -1, VK_QUEUE_GRAPHICS_BIT); presentFamilyIndex = FindQueueFamilyIndex(device, 1); if (graphicsFamilyIndex == 0xFFFFFFFF || presentFamilyIndex == 0xFFFFFFFF) { continue; } // check device extension support std::vector requiredExtensions(REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()); unsigned int extensionCount = 0; vkResult = vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); assert(vkResult == VK_SUCCESS); auto *extensionProperties = pk_new_arr(extensionCount, pkeSettings.mem_bkt.game_transient); vkResult = vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, extensionProperties); assert(vkResult == VK_SUCCESS); for (long k = 0; k < extensionCount; ++k) { const auto &property = extensionProperties[k]; auto it = requiredExtensions.begin(); while (it != requiredExtensions.end()) { if (strcmp(*it.base(), property.extensionName)) { it = requiredExtensions.erase(it); } else { it++; } } } if (requiredExtensions.empty() == false) { continue; } // surface formats unsigned int surfaceCount = 0; vkResult = vkGetPhysicalDeviceSurfaceFormatsKHR(device, vkSurfaceKHR, &surfaceCount, nullptr); assert(vkResult == VK_SUCCESS); if (surfaceCount == 0) { continue; } // check present modes unsigned int presentModeCount = 0; vkResult = vkGetPhysicalDeviceSurfacePresentModesKHR(device, vkSurfaceKHR, &presentModeCount, nullptr); assert(vkResult == VK_SUCCESS); if (presentModeCount == 0) { continue; } vkPhysicalDevice = device; vkGetPhysicalDeviceProperties(vkPhysicalDevice, &vkPhysicalDeviceProperties); printf("Selected VkPhysicalDevice: %s\n", vkPhysicalDeviceProperties.deviceName); vkGetPhysicalDeviceMemoryProperties(vkPhysicalDevice, &vkPhysicalDeviceMemoryProperties); break; } assert(vkPhysicalDevice != nullptr && "Failed to find suitable physical device"); // Create logical device { transferFamilyIndex = FindQueueFamilyIndex(vkPhysicalDevice, 0, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT); if (transferFamilyIndex == 0xFFFFFFFF) { transferFamilyIndex = FindQueueFamilyIndex(vkPhysicalDevice, -1, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT); } if (transferFamilyIndex == 0xFFFFFFFF) { transferFamilyIndex = FindQueueFamilyIndex(vkPhysicalDevice, -1, VK_QUEUE_TRANSFER_BIT); } if (transferFamilyIndex == 0xFFFFFFFF) { printf("Failed to find transfer queue index\n"); throw("Failed to find transfer queue index"); } VkDeviceQueueCreateInfo deviceQueueCreateInfos[2]; float graphicsPriorities[1] = { 1.0f }; deviceQueueCreateInfos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; deviceQueueCreateInfos[0].pNext = nullptr; deviceQueueCreateInfos[0].flags = 0; deviceQueueCreateInfos[0].queueFamilyIndex = graphicsFamilyIndex; deviceQueueCreateInfos[0].queueCount = 1l; deviceQueueCreateInfos[0].pQueuePriorities = graphicsPriorities; float transferPriorities[1] = { 0.5f }; deviceQueueCreateInfos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; deviceQueueCreateInfos[1].pNext = nullptr; deviceQueueCreateInfos[1].flags = 0; deviceQueueCreateInfos[1].queueFamilyIndex = transferFamilyIndex; deviceQueueCreateInfos[1].queueCount = 1l; deviceQueueCreateInfos[1].pQueuePriorities = transferPriorities; VkPhysicalDeviceFeatures vkPhysicalDeviceFeatures; vkGetPhysicalDeviceFeatures(vkPhysicalDevice, &vkPhysicalDeviceFeatures); VkPhysicalDeviceFeatures requestedFeatures{}; // {} to initialize everything to 0 requestedFeatures.samplerAnisotropy = VK_TRUE; requestedFeatures.sampleRateShading = VK_TRUE; requestedFeatures.wideLines = VK_TRUE; requestedFeatures.fillModeNonSolid = vkPhysicalDeviceFeatures.fillModeNonSolid; VkDeviceCreateInfo vkDeviceCreateInfo; vkDeviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; vkDeviceCreateInfo.flags = 0; vkDeviceCreateInfo.queueCreateInfoCount = 2; vkDeviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfos; if (ENABLE_VALIDATION_LAYERS) { vkDeviceCreateInfo.enabledLayerCount = enabledLayerNames.size(); vkDeviceCreateInfo.ppEnabledLayerNames = enabledLayerNames.data(); } vkDeviceCreateInfo.enabledExtensionCount = REQUIRED_EXTENSIONS.size(); vkDeviceCreateInfo.ppEnabledExtensionNames = REQUIRED_EXTENSIONS.data(); vkDeviceCreateInfo.pEnabledFeatures = &requestedFeatures; VkPhysicalDeviceDynamicRenderingFeatures dynamicRenderingFeatures{}; vkDeviceCreateInfo.pNext = &dynamicRenderingFeatures; dynamicRenderingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES; dynamicRenderingFeatures.pNext = NULL; dynamicRenderingFeatures.dynamicRendering = VK_TRUE; vkResult = vkCreateDevice(vkPhysicalDevice, &vkDeviceCreateInfo, vkAllocator, &vkDevice); if (vkResult != VK_SUCCESS) { printf("Failed to create VkInstance! : %d\n", vkResult); throw vkResult; } } // queues { vkGetDeviceQueue(vkDevice, graphicsFamilyIndex, 0, &pkvk_shared.queue.graphics); vkGetDeviceQueue(vkDevice, presentFamilyIndex, 0, &presentQueue); vkGetDeviceQueue(vkDevice, transferFamilyIndex, 0, &pkvk_shared.queue.transfer); fprintf(stdout, "[window.cpp] present queue: %p\n", (void*)presentQueue); fprintf(stdout, "[window.cpp] graphics queue: %p\n", (void*)pkvk_shared.queue.graphics); fprintf(stdout, "[window.cpp] transfer queue: %p\n", (void*)pkvk_shared.queue.transfer); } // surface formats { const VkFormat acceptableDepthFormats[3] = { VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT, }; depthFormat = FindSupportedFormat(3, acceptableDepthFormats, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); } // sampler { VkSampleCountFlags counts = vkPhysicalDeviceProperties.limits.framebufferColorSampleCounts & vkPhysicalDeviceProperties.limits.framebufferDepthSampleCounts; if (counts & VK_SAMPLE_COUNT_64_BIT) { global_sample_count = VK_SAMPLE_COUNT_64_BIT; } else if (counts & VK_SAMPLE_COUNT_32_BIT) { global_sample_count = VK_SAMPLE_COUNT_32_BIT; } else if (counts & VK_SAMPLE_COUNT_16_BIT) { global_sample_count = VK_SAMPLE_COUNT_16_BIT; } else if (counts & VK_SAMPLE_COUNT_8_BIT) { global_sample_count = VK_SAMPLE_COUNT_8_BIT; } else if (counts & VK_SAMPLE_COUNT_4_BIT) { global_sample_count = VK_SAMPLE_COUNT_4_BIT; } else if (counts & VK_SAMPLE_COUNT_2_BIT) { global_sample_count = VK_SAMPLE_COUNT_2_BIT; } } // generic sampler VkSamplerCreateInfo samplerCreateInfo; samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerCreateInfo.pNext = nullptr; samplerCreateInfo.flags = 0U; samplerCreateInfo.magFilter = VK_FILTER_NEAREST; samplerCreateInfo.minFilter = VK_FILTER_NEAREST; samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerCreateInfo.mipLodBias = 0.0f; samplerCreateInfo.anisotropyEnable = VK_TRUE; samplerCreateInfo.maxAnisotropy = 1.0f; samplerCreateInfo.compareEnable = VK_TRUE; samplerCreateInfo.compareOp = VK_COMPARE_OP_LESS; samplerCreateInfo.minLod = 0.0f; samplerCreateInfo.maxLod = 1.0f; samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; samplerCreateInfo.unnormalizedCoordinates = {}; vkResult = vkCreateSampler(vkDevice, &samplerCreateInfo, vkAllocator, &global_sampler); assert(vkResult == VK_SUCCESS); } void CreateImageResources_Inner(VkImageCreateInfo *imageCreateInfo, VkImageViewCreateInfo *imageViewCreateInfo, VkBufferUsageFlagBits bufferUsageFlagBits, VkBuffer *imagesBuffer, VkImage *images, VkImageView *imageViews, VkDeviceMemory *deviceMemory) { (void)bufferUsageFlagBits; (void)imagesBuffer; VkImage tmpImage; VkResult vkResult; vkResult = vkCreateImage(vkDevice, imageCreateInfo, vkAllocator, &tmpImage); assert(vkResult == VK_SUCCESS); VkMemoryRequirements imageMemoryRequirements; vkGetImageMemoryRequirements(vkDevice, tmpImage, &imageMemoryRequirements); VkDeviceSize paddedImageSize = imageMemoryRequirements.size + (imageMemoryRequirements.alignment - (imageMemoryRequirements.size % imageMemoryRequirements.alignment)); assert(paddedImageSize % imageMemoryRequirements.alignment == 0); vkDestroyImage(vkDevice, tmpImage, vkAllocator); VkMemoryAllocateInfo vkMemoryAllocateInfo{}; vkMemoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; vkMemoryAllocateInfo.pNext = nullptr; vkMemoryAllocateInfo.allocationSize = paddedImageSize * swapchainLength; vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(imageMemoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); vkResult = vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, deviceMemory); assert(vkResult == VK_SUCCESS); for (long i = 0; i < swapchainLength; ++i) { vkResult = vkCreateImage(vkDevice, imageCreateInfo, vkAllocator, &images[i]); assert(vkResult == VK_SUCCESS); vkResult = vkBindImageMemory(vkDevice, images[i], *deviceMemory, paddedImageSize * i); assert(vkResult == VK_SUCCESS); imageViewCreateInfo->image = images[i]; vkResult = vkCreateImageView(vkDevice, imageViewCreateInfo, vkAllocator, &imageViews[i]); assert(vkResult == VK_SUCCESS); } } /* 2025-03-03 JCB * I separated this out just so I could think about it more clearly. * This function currently assumes that all of these present modes are available. * My 7900XTX supports all 4 of these. * This needs to be re-visited for platforms that have limited support. * Issue: user requests unlocked framefrate without vsync, but doesn't support immediate mode. * Is it better to give them mailbox to prefer unlimited framerate? * Or let it fall back to fifo, ignoring the vsync setting? * Note that `isFramerateUnlocked` is designed to limit framerate artificially. */ VkPresentModeKHR PickPresentMode(VkPresentModeKHR *present_modes, uint32_t n_present_modes) { VkPresentModeKHR ret = VK_PRESENT_MODE_FIFO_KHR; int32_t immediateIndex = -1; int32_t fifoRelaxedIndex = -1; int32_t mailboxIndex = -1; for (uint32_t i = 0; i < n_present_modes; ++i) { if (present_modes[i] == VkPresentModeKHR::VK_PRESENT_MODE_IMMEDIATE_KHR) { immediateIndex = i; } else if (present_modes[i] == VkPresentModeKHR::VK_PRESENT_MODE_MAILBOX_KHR) { mailboxIndex = i; } else if (present_modes[i] == VkPresentModeKHR::VK_PRESENT_MODE_FIFO_RELAXED_KHR) { fifoRelaxedIndex = i; } } if (pkeSettings.graphicsSettings.isFramerateUnlocked == true && pkeSettings.graphicsSettings.isWaitingForVsync == true) { if (mailboxIndex != -1) { ret = VK_PRESENT_MODE_MAILBOX_KHR; } } else if (pkeSettings.graphicsSettings.isFramerateUnlocked == false && pkeSettings.graphicsSettings.isWaitingForVsync == true) { // fifo } else if (pkeSettings.graphicsSettings.isFramerateUnlocked == true && pkeSettings.graphicsSettings.isWaitingForVsync == false) { if (immediateIndex != -1) { ret = VK_PRESENT_MODE_IMMEDIATE_KHR; } } else { if (fifoRelaxedIndex != -1) { ret = VK_PRESENT_MODE_FIFO_RELAXED_KHR; } } return ret; } void CreateSwapchain() { VkResult vkResult; VkSurfaceCapabilitiesKHR surfaceCapabilities; vkResult = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vkPhysicalDevice, vkSurfaceKHR, &surfaceCapabilities); assert(vkResult == VK_SUCCESS); if (selectedSurfaceIndex == -1u) { unsigned int surfaceFormatCounts; vkResult = vkGetPhysicalDeviceSurfaceFormatsKHR(vkPhysicalDevice, vkSurfaceKHR, &surfaceFormatCounts, nullptr); assert(vkResult == VK_SUCCESS); VkSurfaceFormatKHR *surfaceFormats = pk_new_arr(surfaceFormatCounts, pkeSettings.mem_bkt.game_transient); vkResult = vkGetPhysicalDeviceSurfaceFormatsKHR(vkPhysicalDevice, vkSurfaceKHR, &surfaceFormatCounts, surfaceFormats); assert(vkResult == VK_SUCCESS); selectedSurfaceIndex = 0; for (long i = 0; i < surfaceFormatCounts; ++i) { if (surfaceFormats[i].format != VkFormat::VK_FORMAT_B8G8R8A8_SRGB) continue; if (surfaceFormats[i].colorSpace != VkColorSpaceKHR::VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) continue; selectedSurfaceIndex = i; vkSurfaceFormatKHR.colorSpace = surfaceFormats[i].colorSpace; vkSurfaceFormatKHR.format = surfaceFormats[i].format; break; } } int width, height; glfwGetFramebufferSize(window, &width, &height); Extent.width = width; Extent.height = height; // clamp Extent.width = PK_CLAMP(Extent.width, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width); Extent.height = PK_CLAMP(Extent.height, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height); { unsigned int presentModeCount = 0; vkResult = vkGetPhysicalDeviceSurfacePresentModesKHR(vkPhysicalDevice, vkSurfaceKHR, &presentModeCount, nullptr); assert(vkResult == VK_SUCCESS); VkPresentModeKHR *presentModes = pk_new_arr(presentModeCount, pkeSettings.mem_bkt.game_transient); vkResult = vkGetPhysicalDeviceSurfacePresentModesKHR(vkPhysicalDevice, vkSurfaceKHR, &presentModeCount, presentModes); assert(vkResult == VK_SUCCESS); vkPresentModeKHR = PickPresentMode(presentModes, presentModeCount); } VkSwapchainCreateInfoKHR vkSwapchainCreateInfo; vkSwapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; vkSwapchainCreateInfo.pNext = nullptr; vkSwapchainCreateInfo.flags = 0; vkSwapchainCreateInfo.surface = vkSurfaceKHR; vkSwapchainCreateInfo.minImageCount = surfaceCapabilities.minImageCount; vkSwapchainCreateInfo.imageFormat = vkSurfaceFormatKHR.format; vkSwapchainCreateInfo.imageColorSpace = vkSurfaceFormatKHR.colorSpace; vkSwapchainCreateInfo.imageExtent = Extent; vkSwapchainCreateInfo.imageArrayLayers = 1; vkSwapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // vkSwapchainCreateInfo.imageSharingMode = {}; vkSwapchainCreateInfo.queueFamilyIndexCount = 0; vkSwapchainCreateInfo.pQueueFamilyIndices = nullptr; vkSwapchainCreateInfo.preTransform = surfaceCapabilities.currentTransform; vkSwapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; vkSwapchainCreateInfo.presentMode = vkPresentModeKHR; vkSwapchainCreateInfo.clipped = VK_TRUE; // vkSwapchainCreateInfo.oldSwapchain = vkSwapchainKHR; vkSwapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE; unsigned int qfi[2] = { graphicsFamilyIndex, presentFamilyIndex }; if (graphicsFamilyIndex != presentFamilyIndex) { vkSwapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; vkSwapchainCreateInfo.queueFamilyIndexCount = 2; vkSwapchainCreateInfo.pQueueFamilyIndices = qfi; } else { vkSwapchainCreateInfo.imageSharingMode = VkSharingMode::VK_SHARING_MODE_EXCLUSIVE; vkSwapchainCreateInfo.queueFamilyIndexCount = 0; } vkResult = vkCreateSwapchainKHR(vkDevice, &vkSwapchainCreateInfo, vkAllocator, &vkSwapchainKHR); assert(vkResult == VK_SUCCESS); fprintf(stdout, "[window.cpp] swapchainKHR: %p\n", (void*)vkSwapchainKHR); assert(pkvk_present.images == nullptr || pkvk_present.images == CAFE_BABE(VkImage)); assert(pkvk_present.image_views == nullptr || pkvk_present.image_views == CAFE_BABE(VkImageView)); assert(pkvk_3d.render_images == nullptr || pkvk_3d.render_images == CAFE_BABE(VkImage)); assert(pkvk_3d.image_views == nullptr || pkvk_3d.image_views == CAFE_BABE(VkImageView)); assert(pkvk_3d.images_color_resolve == nullptr || pkvk_3d.images_color_resolve == CAFE_BABE(VkImage)); assert(pkvk_3d.image_views_color_resolve == nullptr || pkvk_3d.image_views_color_resolve == CAFE_BABE(VkImageView)); assert(depthImages == nullptr || depthImages == CAFE_BABE(VkImage)); assert(depthImageViews == nullptr || depthImageViews == CAFE_BABE(VkImageView)); assert(pkvk_2d.images == nullptr || pkvk_2d.images == CAFE_BABE(VkImage)); assert(pkvk_2d.image_views == nullptr || pkvk_2d.image_views == CAFE_BABE(VkImageView)); assert(pkvk_2d.images_color_resolve == nullptr || pkvk_2d.images_color_resolve == CAFE_BABE(VkImage)); assert(pkvk_2d.image_views_color_resolve == nullptr || pkvk_2d.image_views_color_resolve == CAFE_BABE(VkImageView)); assert(pkvk_glyph.images == nullptr || pkvk_glyph.images == CAFE_BABE(VkImage)); assert(pkvk_glyph.image_views == nullptr || pkvk_glyph.image_views == CAFE_BABE(VkImageView)); assert(pkvk_glyph.images_color_resolve == nullptr || pkvk_glyph.images_color_resolve == CAFE_BABE(VkImage)); assert(pkvk_glyph.image_views_color_resolve == nullptr || pkvk_glyph.image_views_color_resolve == CAFE_BABE(VkImageView)); prevSwapchainLength = swapchainLength; vkResult = vkGetSwapchainImagesKHR(vkDevice, vkSwapchainKHR, &swapchainLength, nullptr); assert(vkResult == VK_SUCCESS); assert(swapchainLength >= surfaceCapabilities.minImageCount); assert(surfaceCapabilities.maxImageCount == 0 || swapchainLength <= surfaceCapabilities.maxImageCount); if (prevSwapchainLength == 0) prevSwapchainLength = swapchainLength; pkvk_present.images = pk_new_arr(swapchainLength, MemBkt_Vulkan); vkResult = vkGetSwapchainImagesKHR(vkDevice, vkSwapchainKHR, &swapchainLength, pkvk_present.images); assert(vkResult == VK_SUCCESS); pkvk_present.image_views = pk_new_arr(swapchainLength, MemBkt_Vulkan); VkImageSubresourceRange vkImageSubresourceRange; vkImageSubresourceRange.aspectMask = VkImageAspectFlagBits::VK_IMAGE_ASPECT_COLOR_BIT; vkImageSubresourceRange.baseMipLevel = 0u; vkImageSubresourceRange.levelCount = 1u; vkImageSubresourceRange.baseArrayLayer = 0u; vkImageSubresourceRange.layerCount = 1u; VkImageViewCreateInfo vkImageViewCreateInfo; vkImageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; vkImageViewCreateInfo.pNext = nullptr; vkImageViewCreateInfo.flags = 0; vkImageViewCreateInfo.viewType = VkImageViewType::VK_IMAGE_VIEW_TYPE_2D; vkImageViewCreateInfo.format = vkSurfaceFormatKHR.format; vkImageViewCreateInfo.components = { VkComponentSwizzle::VK_COMPONENT_SWIZZLE_IDENTITY, VkComponentSwizzle::VK_COMPONENT_SWIZZLE_IDENTITY, VkComponentSwizzle::VK_COMPONENT_SWIZZLE_IDENTITY, VkComponentSwizzle::VK_COMPONENT_SWIZZLE_IDENTITY, }; vkImageViewCreateInfo.subresourceRange = vkImageSubresourceRange; for (long i = 0; i < swapchainLength; ++i) { vkImageViewCreateInfo.image = pkvk_present.images[i]; vkResult = vkCreateImageView(vkDevice, &vkImageViewCreateInfo, vkAllocator, &pkvk_present.image_views[i]); assert(vkResult == VK_SUCCESS); } // render target items VkImageCreateInfo renderTargetImageCI{}; renderTargetImageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; renderTargetImageCI.pNext = nullptr; renderTargetImageCI.flags = 0; renderTargetImageCI.imageType = VK_IMAGE_TYPE_2D; renderTargetImageCI.format = vkSurfaceFormatKHR.format; renderTargetImageCI.extent.width = Extent.width; renderTargetImageCI.extent.height = Extent.height; renderTargetImageCI.extent.depth = 1; renderTargetImageCI.mipLevels = 1; renderTargetImageCI.arrayLayers = 1; renderTargetImageCI.samples = global_sample_count; renderTargetImageCI.tiling = VK_IMAGE_TILING_OPTIMAL; renderTargetImageCI.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; renderTargetImageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; renderTargetImageCI.queueFamilyIndexCount = 0; renderTargetImageCI.pQueueFamilyIndices = nullptr; renderTargetImageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // 3d color (fragment) images pkvk_3d.render_images = pk_new_arr(swapchainLength, MemBkt_Vulkan); pkvk_3d.image_views = pk_new_arr(swapchainLength, MemBkt_Vulkan); CreateImageResources_Inner(&renderTargetImageCI, &vkImageViewCreateInfo, VkBufferUsageFlagBits(0), nullptr, pkvk_3d.render_images, pkvk_3d.image_views, &pkvk_3d.images_memory); // 2d color (fragment) images pkvk_2d.images = pk_new_arr(swapchainLength, MemBkt_Vulkan); pkvk_2d.image_views = pk_new_arr(swapchainLength, MemBkt_Vulkan); CreateImageResources_Inner(&renderTargetImageCI, &vkImageViewCreateInfo, VkBufferUsageFlagBits(0), nullptr, pkvk_2d.images, pkvk_2d.image_views, &pkvk_2d.images_memory); // 2d glyph color (fragment) images pkvk_glyph.images = pk_new_arr(swapchainLength, MemBkt_Vulkan); pkvk_glyph.image_views = pk_new_arr(swapchainLength, MemBkt_Vulkan); CreateImageResources_Inner(&renderTargetImageCI, &vkImageViewCreateInfo, VkBufferUsageFlagBits(0), nullptr, pkvk_glyph.images, pkvk_glyph.image_views, &pkvk_glyph.images_memory); // resolve settings renderTargetImageCI.samples = VK_SAMPLE_COUNT_1_BIT; renderTargetImageCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // 3d color (resolve) pkvk_3d.images_color_resolve = pk_new_arr(swapchainLength, MemBkt_Vulkan); pkvk_3d.image_views_color_resolve = pk_new_arr(swapchainLength, MemBkt_Vulkan); CreateImageResources_Inner(&renderTargetImageCI, &vkImageViewCreateInfo, VkBufferUsageFlagBits(0), nullptr, pkvk_3d.images_color_resolve, pkvk_3d.image_views_color_resolve, &pkvk_3d.images_memory_color_resolve); // 2d color (resolve) pkvk_2d.images_color_resolve = pk_new_arr(swapchainLength, MemBkt_Vulkan); pkvk_2d.image_views_color_resolve = pk_new_arr(swapchainLength, MemBkt_Vulkan); CreateImageResources_Inner(&renderTargetImageCI, &vkImageViewCreateInfo, VkBufferUsageFlagBits(0), nullptr, pkvk_2d.images_color_resolve, pkvk_2d.image_views_color_resolve, &pkvk_2d.images_memory_color_resolve); // 2d glyph color (resolve) pkvk_glyph.images_color_resolve = pk_new_arr(swapchainLength, MemBkt_Vulkan); pkvk_glyph.image_views_color_resolve = pk_new_arr(swapchainLength, MemBkt_Vulkan); CreateImageResources_Inner(&renderTargetImageCI, &vkImageViewCreateInfo, VkBufferUsageFlagBits(0), nullptr, pkvk_glyph.images_color_resolve, pkvk_glyph.image_views_color_resolve, &pkvk_glyph.images_memory_color_resolve); // depth settings renderTargetImageCI.samples = global_sample_count; renderTargetImageCI.format = depthFormat; renderTargetImageCI.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; vkImageViewCreateInfo.format = depthFormat; vkImageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; // depth images depthImages = pk_new_arr(swapchainLength, MemBkt_Vulkan); depthImageViews = pk_new_arr(swapchainLength, MemBkt_Vulkan); CreateImageResources_Inner(&renderTargetImageCI, &vkImageViewCreateInfo, VkBufferUsageFlagBits(0), nullptr, depthImages, depthImageViews, &depthImagesMemory); } void UpdatePresentDescriptorSets() { const unsigned int sub_pass_count = 3; VkWriteDescriptorSet *writeDescriptorSets = pk_new_arr(swapchainLength * sub_pass_count, pkeSettings.mem_bkt.game_transient); VkDescriptorImageInfo *descriptorImageInfo = pk_new_arr(swapchainLength * sub_pass_count, pkeSettings.mem_bkt.game_transient); for (long i = 0; i < swapchainLength * sub_pass_count; ++i) { writeDescriptorSets[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[i].pNext = nullptr; writeDescriptorSets[i].dstBinding = 0U; writeDescriptorSets[i].dstArrayElement = 0U; writeDescriptorSets[i].descriptorCount = 1; writeDescriptorSets[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[i].pBufferInfo = nullptr; writeDescriptorSets[i].pTexelBufferView = nullptr; descriptorImageInfo[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; descriptorImageInfo[i].sampler = global_sampler; writeDescriptorSets[i].dstSet = pkvk_present.descriptor_sets[i]; writeDescriptorSets[i].pImageInfo = &descriptorImageInfo[i]; } for (long i = 0; i < swapchainLength; ++i) { descriptorImageInfo[(i * sub_pass_count) + 0].imageView = pkvk_3d.image_views_color_resolve[i]; descriptorImageInfo[(i * sub_pass_count) + 1].imageView = pkvk_2d.image_views_color_resolve[i]; descriptorImageInfo[(i * sub_pass_count) + 2].imageView = pkvk_glyph.image_views_color_resolve[i]; } vkUpdateDescriptorSets(vkDevice, swapchainLength * sub_pass_count, writeDescriptorSets, 0, nullptr); } void UpdateCamera() { if (ActiveCamera->stale == PkeCameraStaleFlags{0}) return; CompInstance *inst = nullptr; CompInstance *targetInst = nullptr; glm::vec3 gPos; glm::quat gRot; btTransform trfm; if (ActiveCamera == &NullCamera) { inst = &NullCameraInstance; } else { inst = ECS_GetInstance(ActiveCamera->phys.instHandle); } inst->bt.motionState->getWorldTransform(trfm); BulletToGlm(trfm.getOrigin(), gPos); BulletToGlm(trfm.getRotation(), gRot); if (bool(ActiveCamera->stale & PKE_CAMERA_STALE_POS)) { UBO.model = glm::translate(glm::mat4(1.f), gPos); } if (bool(ActiveCamera->stale & PKE_CAMERA_STALE_ROT)) { if (bool(ActiveCamera->view == PKE_CAMERA_VIEW_FREE)) { UBO.view = glm::mat4_cast(gRot); } else if (bool(ActiveCamera->view == PKE_CAMERA_VIEW_TARGET)) { glm::vec3 gTargetPos{0.f, 0.f, 0.f}; targetInst = ECS_GetInstance(ActiveCamera->phys.target_inst_handle); if (targetInst != nullptr && targetInst != CAFE_BABE(CompInstance)) { btTransform targetTrfm; targetInst->bt.motionState->getWorldTransform(targetTrfm); BulletToGlm(targetTrfm.getOrigin(), gTargetPos); } UBO.view = glm::lookAt(glm::vec3(0), gPos + gTargetPos, glm::vec3(0.f, 1.f, 0.f)); } } if (bool(ActiveCamera->stale & PKE_CAMERA_STALE_PERSPECTIVE)) { if (bool(ActiveCamera->type == PKE_CAMERA_TYPE_PERSPECTIVE)) { UBO.proj = glm::perspective(glm::radians(45.f), Extent.width / (float)Extent.height, 0.1f, 1000.f); UBO.proj[1][1] *= -1; } else if (bool(ActiveCamera->type == PKE_CAMERA_TYPE_ORTHOGONAL)) { UBO.proj = glm::ortho(0.f, (float)Extent.width, 0.f, (float)Extent.height, 0.f, 1000.f); UBO.proj[1][1] *= -1; } } ActiveCamera->stale = PkeCameraStaleFlags{0}; } void CreatePresentDescriptorSetLayout() { VkDescriptorSetLayoutBinding imageSamplerLayoutBinding; imageSamplerLayoutBinding.binding = 0; imageSamplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; imageSamplerLayoutBinding.descriptorCount = 1; imageSamplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; imageSamplerLayoutBinding.pImmutableSamplers = nullptr; VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo; descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; descriptorSetLayoutCreateInfo.pNext = nullptr; descriptorSetLayoutCreateInfo.flags = 0; descriptorSetLayoutCreateInfo.bindingCount = 1; descriptorSetLayoutCreateInfo.pBindings = &imageSamplerLayoutBinding; auto vkResult = vkCreateDescriptorSetLayout(vkDevice, &descriptorSetLayoutCreateInfo, vkAllocator, &pkvk_present.descriptor_set_layout); if (vkResult != VK_SUCCESS) { throw "failed to create descriptor set layout"; } } void CreatePresentPipeline() { VkResult vkResult; assert(pkvk_present.descriptor_sets == nullptr || pkvk_present.descriptor_sets == CAFE_BABE(VkDescriptorSet)); // enqueue asset loading AssetHandle vertShaderAsset{AM_GetHandle(embedded_shaders[embedded_shader_index_present_vert].name)}; AssetHandle fragShaderAsset{AM_GetHandle(embedded_shaders[embedded_shader_index_present_frag].name)}; VkPipelineShaderStageCreateInfo shaderStages[2]; for (long i = 0; i < 2; ++i) { shaderStages[i].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shaderStages[i].pNext = nullptr; shaderStages[i].flags = 0; shaderStages[i].pName = "main"; shaderStages[i].pSpecializationInfo = nullptr; } shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; // shaderStages[0].module = // see below // shaderStages[1].module = // see below VkDynamicState dynamicStates[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamicState; dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamicState.pNext = nullptr; dynamicState.flags = {}; dynamicState.dynamicStateCount = 2; dynamicState.pDynamicStates = dynamicStates; VkPipelineVertexInputStateCreateInfo vertexInputInfo; vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertexInputInfo.pNext = nullptr; vertexInputInfo.flags = {}; vertexInputInfo.vertexBindingDescriptionCount = 0; vertexInputInfo.pVertexBindingDescriptions = nullptr; vertexInputInfo.vertexAttributeDescriptionCount = 0; vertexInputInfo.pVertexAttributeDescriptions = nullptr; VkPipelineInputAssemblyStateCreateInfo inputAssembly; inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; inputAssembly.pNext = nullptr; inputAssembly.flags = {}; inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; inputAssembly.primitiveRestartEnable = VK_FALSE; // dynamic VkViewport viewport; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = 0.0f; viewport.height = 0.0f; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; // dynamic VkRect2D scissor; scissor.offset = {0, 0}; scissor.extent = Extent; VkPipelineViewportStateCreateInfo viewportState; viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewportState.pNext = nullptr; viewportState.flags = {}; viewportState.viewportCount = 1; viewportState.pViewports = &viewport; viewportState.scissorCount = 1; viewportState.pScissors = &scissor; VkPipelineRasterizationStateCreateInfo rasterizer; rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer.pNext = nullptr; rasterizer.flags = {}; rasterizer.depthClampEnable = VK_FALSE; rasterizer.rasterizerDiscardEnable = VK_FALSE; rasterizer.polygonMode = VK_POLYGON_MODE_FILL; rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; rasterizer.depthBiasEnable = VK_FALSE; rasterizer.depthBiasConstantFactor = 0.0f; rasterizer.depthBiasClamp = 0.0f; rasterizer.depthBiasSlopeFactor = 0.0f; rasterizer.lineWidth = 1.0f; VkPipelineMultisampleStateCreateInfo multisampling; multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling.pNext = nullptr; multisampling.flags = {}; multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; multisampling.sampleShadingEnable = VK_FALSE; multisampling.minSampleShading = 1.0f; multisampling.pSampleMask = nullptr; multisampling.alphaToCoverageEnable = VK_FALSE; multisampling.alphaToOneEnable = VK_FALSE; // TODO VkPipelineDepthStencilStateCreateInfo depthStencilInfo{}; depthStencilInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; depthStencilInfo.pNext = nullptr; depthStencilInfo.flags = 0; depthStencilInfo.depthTestEnable = VK_FALSE; depthStencilInfo.depthWriteEnable = VK_FALSE; depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS; depthStencilInfo.depthBoundsTestEnable = VK_FALSE; depthStencilInfo.stencilTestEnable = VK_FALSE; depthStencilInfo.front = {}; depthStencilInfo.back = {}; depthStencilInfo.minDepthBounds = {}; depthStencilInfo.maxDepthBounds = {}; VkPipelineColorBlendAttachmentState colorBlendAttachment; colorBlendAttachment.blendEnable = VK_TRUE; colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; VkPipelineColorBlendStateCreateInfo colorBlending; colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; colorBlending.pNext = nullptr; colorBlending.flags = {}; colorBlending.logicOpEnable = VK_FALSE; colorBlending.logicOp = VK_LOGIC_OP_COPY; colorBlending.attachmentCount = 1; colorBlending.pAttachments = &colorBlendAttachment; colorBlending.blendConstants[0] = 0.0f; colorBlending.blendConstants[1] = 0.0f; colorBlending.blendConstants[2] = 0.0f; colorBlending.blendConstants[3] = 0.0f; /* * PipelineLayout + DescriptorSets */ VkDescriptorPoolSize vkDescriptorPoolSize; vkDescriptorPoolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; vkDescriptorPoolSize.descriptorCount = swapchainLength * 3; VkDescriptorPoolCreateInfo descriptorPoolCreateInfo; descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptorPoolCreateInfo.pNext = nullptr; descriptorPoolCreateInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; descriptorPoolCreateInfo.maxSets = swapchainLength * 3; descriptorPoolCreateInfo.poolSizeCount = 1; descriptorPoolCreateInfo.pPoolSizes = &vkDescriptorPoolSize; vkResult = vkCreateDescriptorPool(vkDevice, &descriptorPoolCreateInfo, vkAllocator, &pkvk_present.descriptor_pool); assert(vkResult == VK_SUCCESS); VkDescriptorSetLayout *setLayouts = pk_new_arr(swapchainLength * 3, pkeSettings.mem_bkt.game_transient); for (long i = 0; i < swapchainLength * 3; ++i) { setLayouts[i] = pkvk_present.descriptor_set_layout; } VkDescriptorSetAllocateInfo allocInfo; allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; allocInfo.pNext = nullptr; allocInfo.descriptorPool = pkvk_present.descriptor_pool; allocInfo.descriptorSetCount = swapchainLength * 3; allocInfo.pSetLayouts = setLayouts; pkvk_present.descriptor_sets = pk_new_arr(swapchainLength * 3, MemBkt_Vulkan); vkResult = vkAllocateDescriptorSets(vkDevice, &allocInfo, pkvk_present.descriptor_sets); // setLayouts = CAFE_BABE(VkDescriptorSetLayout); if (vkResult != VK_SUCCESS) { throw "failed to allocate present descriptor sets"; } VkPipelineLayoutCreateInfo pipelineLayoutInfo; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.pNext = nullptr; pipelineLayoutInfo.flags = {}; pipelineLayoutInfo.setLayoutCount = 1; pipelineLayoutInfo.pSetLayouts = &pkvk_present.descriptor_set_layout; pipelineLayoutInfo.pushConstantRangeCount = 0; pipelineLayoutInfo.pPushConstantRanges = nullptr; vkResult = vkCreatePipelineLayout(vkDevice, &pipelineLayoutInfo, vkAllocator, &pkvk_present.pipeline_layout); if (vkResult != VK_SUCCESS) { throw "failed to create pipeline layout"; } VkGraphicsPipelineCreateInfo pipelineInfo; pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipelineInfo.flags = {}; pipelineInfo.stageCount = 2; pipelineInfo.pStages = shaderStages; pipelineInfo.pVertexInputState = &vertexInputInfo; pipelineInfo.pInputAssemblyState = &inputAssembly; pipelineInfo.pTessellationState = nullptr; pipelineInfo.pViewportState = &viewportState; pipelineInfo.pRasterizationState = &rasterizer; pipelineInfo.pMultisampleState = &multisampling; pipelineInfo.pDepthStencilState = &depthStencilInfo; pipelineInfo.pColorBlendState = &colorBlending; pipelineInfo.pDynamicState = &dynamicState; pipelineInfo.layout = pkvk_present.pipeline_layout; pipelineInfo.renderPass = VK_NULL_HANDLE; // dynamic rendering pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; pipelineInfo.basePipelineIndex = {}; VkPipelineRenderingCreateInfo renderingInfo{}; pipelineInfo.pNext = &renderingInfo; renderingInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; renderingInfo.colorAttachmentCount = 1; VkFormat color_format = vkSurfaceFormatKHR.format; renderingInfo.pColorAttachmentFormats = &color_format; /* 2023-12-22 JCB * This happens just before needed because we want to let this * thread run while background threads load the assets. */ auto vertShader = UploadShader(vertShaderAsset); auto fragShader = UploadShader(fragShaderAsset); shaderStages[0].module = vertShader; shaderStages[1].module = fragShader; vkResult = vkCreateGraphicsPipelines(vkDevice, VK_NULL_HANDLE, 1, &pipelineInfo, vkAllocator, &pkvk_present.pipeline); if (vkResult != VK_SUCCESS) { throw "failed to create graphics pipeline."; } vkDestroyShaderModule(vkDevice, fragShader, vkAllocator); vkDestroyShaderModule(vkDevice, vertShader, vkAllocator); } void CreateCommandPool() { VkCommandPoolCreateInfo poolInfo; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.pNext = nullptr; poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; poolInfo.queueFamilyIndex = graphicsFamilyIndex; auto result = vkCreateCommandPool(vkDevice, &poolInfo, vkAllocator, &pkvk_shared.command_pool.graphics); if (result != VK_SUCCESS) { throw "failed to create command pool"; } poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; poolInfo.queueFamilyIndex = transferFamilyIndex; result = vkCreateCommandPool(vkDevice, &poolInfo, vkAllocator, &pkvk_shared.command_pool.transfer); if (result != VK_SUCCESS) { throw "failed to create transfer command pool"; } } void DestroyPresentCommandBuffers() { if (pkvk_present.command_buffers != nullptr) { vkFreeCommandBuffers(vkDevice, pkvk_shared.command_pool.graphics, prevSwapchainLength, pkvk_present.command_buffers); pk_delete_arr(pkvk_present.command_buffers, prevSwapchainLength, MemBkt_Vulkan); pkvk_present.command_buffers = nullptr; } } void CreatePresentCommandBuffers() { assert(pkvk_present.command_buffers == nullptr); VkCommandBufferAllocateInfo allocInfo; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.pNext = nullptr; allocInfo.commandPool = pkvk_shared.command_pool.graphics; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocInfo.commandBufferCount = swapchainLength; pkvk_present.command_buffers = pk_new_arr(swapchainLength, MemBkt_Vulkan); auto result = vkAllocateCommandBuffers(vkDevice, &allocInfo, pkvk_present.command_buffers); if (result != VK_SUCCESS) { throw "failed to allocate command buffer"; } } void CreateCommandBuffers() { VkResult result; VkCommandBufferAllocateInfo allocInfo; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.pNext = nullptr; allocInfo.commandPool = pkvk_shared.command_pool.graphics; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocInfo.commandBufferCount = 1; result = vkAllocateCommandBuffers(vkDevice, &allocInfo, &pkvk_shared.command_buffer.graphics); if (result != VK_SUCCESS) { throw "failed to allocate command buffer"; } allocInfo.commandPool = pkvk_shared.command_pool.transfer; result = vkAllocateCommandBuffers(vkDevice, &allocInfo, &pkvk_shared.command_buffer.transfer); if (result != VK_SUCCESS) { throw "failed to allocate command buffer"; } } void CreateUniformBuffers() { VkResult result; uint32_t queueFamilyIndexes[2] = {graphicsFamilyIndex, transferFamilyIndex}; VkBufferCreateInfo vkBufferCreateInfo; vkBufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; vkBufferCreateInfo.pNext = nullptr; vkBufferCreateInfo.flags = {}; vkBufferCreateInfo.size = sizeof(UniformBufferObject); vkBufferCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; vkBufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; vkBufferCreateInfo.queueFamilyIndexCount = graphicsFamilyIndex == transferFamilyIndex ? 1 : 2; vkBufferCreateInfo.pQueueFamilyIndices = queueFamilyIndexes; UniformBuffers = pk_new_arr(swapchainLength, MemBkt_Vulkan); result = vkCreateBuffer(vkDevice, &vkBufferCreateInfo, vkAllocator, &UniformBuffers[0]); assert(result == VK_SUCCESS); VkMemoryRequirements memReqs; vkGetBufferMemoryRequirements(vkDevice, UniformBuffers[0], &memReqs); paddedUboBufferSize = memReqs.size + (memReqs.alignment - (memReqs.size & memReqs.alignment)); VkMemoryAllocateInfo vkMemoryAllocateInfo; vkMemoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; vkMemoryAllocateInfo.pNext = nullptr; vkMemoryAllocateInfo.allocationSize = paddedUboBufferSize * swapchainLength; vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); result = vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &uniformBufferMemory); assert(result == VK_SUCCESS); vkDestroyBuffer(vkDevice, UniformBuffers[0], vkAllocator); vkBufferCreateInfo.size = paddedUboBufferSize; for (long i = 0; i < swapchainLength; ++i) { result = vkCreateBuffer(vkDevice, &vkBufferCreateInfo, vkAllocator, &UniformBuffers[i]); assert(result == VK_SUCCESS); result = vkBindBufferMemory(vkDevice, UniformBuffers[i], uniformBufferMemory, paddedUboBufferSize * i); assert(result == VK_SUCCESS); } } void CreateSyncObjects() { assert(pkvk_present.semaphores_present_complete == nullptr || pkvk_present.semaphores_present_complete == CAFE_BABE(VkSemaphore)); assert(pkvk_present.semaphores_render_finished == nullptr || pkvk_present.semaphores_render_finished == CAFE_BABE(VkSemaphore)); assert(pkvk_present.fences_in_flight == nullptr || pkvk_present.fences_in_flight == CAFE_BABE(VkFence)); VkSemaphoreCreateInfo semaphoreInfo; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphoreInfo.pNext = nullptr; semaphoreInfo.flags = {}; VkFenceCreateInfo fenceInfo; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.pNext = nullptr; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; pkvk_present.semaphores_present_complete = pk_new_arr(swapchainLength, MemBkt_Vulkan); pkvk_present.semaphores_render_finished = pk_new_arr(swapchainLength, MemBkt_Vulkan); pkvk_present.fences_in_flight = pk_new_arr(swapchainLength, MemBkt_Vulkan); for (long i = 0; i < swapchainLength; ++i) { auto result1 = vkCreateSemaphore(vkDevice, &semaphoreInfo, vkAllocator, &pkvk_present.semaphores_present_complete[i]); auto result2 = vkCreateSemaphore(vkDevice, &semaphoreInfo, vkAllocator, &pkvk_present.semaphores_render_finished[i]); auto result3 = vkCreateFence(vkDevice, &fenceInfo, vkAllocator, &pkvk_present.fences_in_flight[i]); auto result = result1 == result2 && result2 == result3 && result3 == VK_SUCCESS; if (!result) { throw "failed to create sync objects"; } fprintf(stdout, "[window.cpp] image available semaphore %li: %p\n", i, (void*)pkvk_present.semaphores_present_complete[i]); fprintf(stdout, "[window.cpp] render finished semaphore %li: %p\n", i, (void*)pkvk_present.semaphores_render_finished[i]); fprintf(stdout, "[window.cpp] in_flight fence %li: %p\n", i, (void*)pkvk_present.fences_in_flight[i]); } } void CreateGraphicsPipelines() { VkResult vkResult; PKVK_TmpBufferDetails tmpBufferDetails{}; // layouts & sampler { VkDescriptorSetLayoutBinding vkDescriptorSetLayoutBindings[2]; for (long i = 0; i < 2; ++i) { vkDescriptorSetLayoutBindings[i].pImmutableSamplers = nullptr; } vkDescriptorSetLayoutBindings[0].binding = 0; vkDescriptorSetLayoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; vkDescriptorSetLayoutBindings[0].descriptorCount = 1; vkDescriptorSetLayoutBindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; vkDescriptorSetLayoutBindings[1].binding = 1; vkDescriptorSetLayoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; vkDescriptorSetLayoutBindings[1].descriptorCount = 1; vkDescriptorSetLayoutBindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; VkDescriptorSetLayoutCreateInfo vkDescriptorSetLayoutCreateInfo { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .pNext = nullptr, .flags = 0, .bindingCount = 2, .pBindings = vkDescriptorSetLayoutBindings, }; vkResult = vkCreateDescriptorSetLayout(vkDevice, &vkDescriptorSetLayoutCreateInfo, vkAllocator, &pkePipelines.descr_layouts.named.ubo_txtr); assert(vkResult == VK_SUCCESS); assert(pkePipelines.descr_layouts.named.ubo_txtr != VK_NULL_HANDLE); // no UBO on glyph vkDescriptorSetLayoutBindings[0] = vkDescriptorSetLayoutBindings[1]; vkDescriptorSetLayoutBindings[0].binding = 0; vkDescriptorSetLayoutCreateInfo.bindingCount = 1; vkResult = vkCreateDescriptorSetLayout(vkDevice, &vkDescriptorSetLayoutCreateInfo, vkAllocator, &pkePipelines.descr_layouts.named.txtr); assert(vkResult == VK_SUCCESS); assert(pkePipelines.descr_layouts.named.txtr != VK_NULL_HANDLE); // no UBO or txtr // 2025-02-28 JCB // I'm calling this "base" for now, meaning no bindings. // There's a good chance this will be toggled on and off as needed. // At the current moment, I'm using this to start the UI render work. // There's a good chance the UI shaders will get *a* UBO (for color?). vkDescriptorSetLayoutCreateInfo.bindingCount = 0; vkDescriptorSetLayoutCreateInfo.pBindings = VK_NULL_HANDLE; vkResult = vkCreateDescriptorSetLayout(vkDevice, &vkDescriptorSetLayoutCreateInfo, vkAllocator, &pkePipelines.descr_layouts.named.base); assert(vkResult == VK_SUCCESS); assert(pkePipelines.descr_layouts.named.base != VK_NULL_HANDLE); VkPipelineLayoutCreateInfo vkPipelineLayoutCreateInfo { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .pNext = nullptr, .flags = 0, .setLayoutCount = 1, .pSetLayouts = &pkePipelines.descr_layouts.named.ubo_txtr, .pushConstantRangeCount = 0, .pPushConstantRanges = nullptr, }; vkCreatePipelineLayout(vkDevice, &vkPipelineLayoutCreateInfo, vkAllocator, &pkePipelines.pipe_layouts.named.ubo_txtr); assert(pkePipelines.pipe_layouts.named.ubo_txtr != VK_NULL_HANDLE); vkPipelineLayoutCreateInfo.pSetLayouts = &pkePipelines.descr_layouts.named.txtr; vkCreatePipelineLayout(vkDevice, &vkPipelineLayoutCreateInfo, vkAllocator, &pkePipelines.pipe_layouts.named.txtr); assert(pkePipelines.pipe_layouts.named.txtr != VK_NULL_HANDLE); vkPipelineLayoutCreateInfo.pSetLayouts = &pkePipelines.descr_layouts.named.base; vkCreatePipelineLayout(vkDevice, &vkPipelineLayoutCreateInfo, vkAllocator, &pkePipelines.pipe_layouts.named.base); assert(pkePipelines.pipe_layouts.named.base != VK_NULL_HANDLE); } // pipelines { // enqueue asset loading AssetHandle vertShaderAssetHandle { AM_GetHandle(embedded_shaders[embedded_shader_index_3d_vert].name)}; AssetHandle textureFragShaderAssetHandle { AM_GetHandle(embedded_shaders[embedded_shader_index_3d_frag].name)}; AssetHandle vertGlyphAssetHandle { AM_GetHandle(embedded_shaders[embedded_shader_index_glyph_vert].name)}; AssetHandle fragGlyphAssetHandle { AM_GetHandle(embedded_shaders[embedded_shader_index_glyph_frag].name)}; AssetHandle asset_handle_vert_ui_base { AM_GetHandle(embedded_shaders[embedded_shader_index_ui_base_vert].name)}; AssetHandle asset_handle_frag_ui_base { AM_GetHandle(embedded_shaders[embedded_shader_index_ui_base_frag].name)}; AssetHandle asset_handle_frag_ui_txtr { AM_GetHandle(embedded_shaders[embedded_shader_index_ui_texture_frag].name)}; const long vertexBindingCount = 4; long index = 0; VkVertexInputBindingDescription vertInputBD_txtr[vertexBindingCount]; const long vertexAttrDescCount = 7; VkVertexInputAttributeDescription vertAttrDesc_txtr[vertexAttrDescCount]; VkPipelineVertexInputStateCreateInfo vkPipelineVertexInputStateCreateInfo_txtr; { // model vertex vertInputBD_txtr[index].binding = index; vertInputBD_txtr[index].stride = sizeof(glm::vec3); vertInputBD_txtr[index].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; index += 1; // model normals vertInputBD_txtr[index].binding = index; vertInputBD_txtr[index].stride = sizeof(glm::vec3); vertInputBD_txtr[index].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; index += 1; // model UV vertInputBD_txtr[index].binding = index; vertInputBD_txtr[index].stride = sizeof(glm::vec2); vertInputBD_txtr[index].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; index += 1; // instance vertInputBD_txtr[index].binding = index; vertInputBD_txtr[index].stride = sizeof(glm::mat4); vertInputBD_txtr[index].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE; // index += 1; index = 0; for (long i = 0; i < vertexAttrDescCount; ++i) { vertAttrDesc_txtr[i].location = i; } // model vertex vertAttrDesc_txtr[index].binding = 0; vertAttrDesc_txtr[index].format = VK_FORMAT_R32G32B32_SFLOAT; vertAttrDesc_txtr[index].offset = 0; index += 1; // model normals vertAttrDesc_txtr[index].binding = 1; vertAttrDesc_txtr[index].format = VK_FORMAT_R32G32B32_SFLOAT; vertAttrDesc_txtr[index].offset = 0; index += 1; // model UV vertAttrDesc_txtr[index].binding = 2; vertAttrDesc_txtr[index].format = VK_FORMAT_R32G32_SFLOAT; vertAttrDesc_txtr[index].offset = 0; index += 1; // instPosRotScale for (long i = 0; i < 4; ++i) { vertAttrDesc_txtr[index].binding = 3; vertAttrDesc_txtr[index].format = VK_FORMAT_R32G32B32A32_SFLOAT; vertAttrDesc_txtr[index].offset = sizeof(glm::vec4) * i; index += 1; } vkPipelineVertexInputStateCreateInfo_txtr.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vkPipelineVertexInputStateCreateInfo_txtr.pNext = nullptr; vkPipelineVertexInputStateCreateInfo_txtr.flags = {}; vkPipelineVertexInputStateCreateInfo_txtr.vertexBindingDescriptionCount = vertexBindingCount; vkPipelineVertexInputStateCreateInfo_txtr.pVertexBindingDescriptions = vertInputBD_txtr; vkPipelineVertexInputStateCreateInfo_txtr.vertexAttributeDescriptionCount = vertexAttrDescCount; vkPipelineVertexInputStateCreateInfo_txtr.pVertexAttributeDescriptions = vertAttrDesc_txtr; } index = 0; long offset = 0; const long vertexBindingCount_glyph = 4; VkVertexInputBindingDescription vertInputBD_glyph[vertexBindingCount_glyph]; const long vertexAttrDescCount_glyph = 14; VkVertexInputAttributeDescription vertAttrDesc_glyph[vertexAttrDescCount_glyph]; VkPipelineVertexInputStateCreateInfo vkPipelineVertexInputStateCreateInfo_glyph{vkPipelineVertexInputStateCreateInfo_txtr}; { // vertex - vertex vertInputBD_glyph[index].binding = index; vertInputBD_glyph[index].stride = sizeof(glm::vec2); vertInputBD_glyph[index].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; index += 1; // vertex - UV vertInputBD_glyph[index].binding = index; vertInputBD_glyph[index].stride = sizeof(glm::vec2); vertInputBD_glyph[index].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; index += 1; // vertex - atlas_size vertInputBD_glyph[index].binding = index; vertInputBD_glyph[index].stride = sizeof(glm::vec2); vertInputBD_glyph[index].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; index += 1; // instance - total vertInputBD_glyph[index].binding = index; vertInputBD_glyph[index].stride = 0 + sizeof(glm::vec4) * 4 // mat4 pos_scale + sizeof(glm::vec4) // in_fg_color + sizeof(glm::vec4) // in_bg_color + sizeof(glm::vec2) // in_sprite_region_min + sizeof(glm::vec2) // in_sprite_region_max + sizeof(glm::vec2) // in_bounding_region_min + sizeof(glm::vec2) // in_bounding_region_max + sizeof(float) // in_width + (sizeof(float) * 3) // padding + 0; vertInputBD_glyph[index].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE; // index += 1; index = 0; for (long i = 0; i < vertexAttrDescCount_glyph; ++i) { vertAttrDesc_glyph[i].location = i; } // vertex - vertex vertAttrDesc_glyph[index].binding = 0; vertAttrDesc_glyph[index].format = VK_FORMAT_R32G32_SFLOAT; vertAttrDesc_glyph[index].offset = 0; index += 1; // vertex - UV vertAttrDesc_glyph[index].binding = 1; vertAttrDesc_glyph[index].format = VK_FORMAT_R32G32_SFLOAT; vertAttrDesc_glyph[index].offset = 0; index += 1; // vertex - in_atlas_size vertAttrDesc_glyph[index].binding = 2; vertAttrDesc_glyph[index].format = VK_FORMAT_R32G32_SFLOAT; vertAttrDesc_glyph[index].offset = 0; index += 1; // instance - pos_scale for (long i = 0; i < 4; ++i) { vertAttrDesc_glyph[index].binding = 3; vertAttrDesc_glyph[index].format = VK_FORMAT_R32G32B32A32_SFLOAT; vertAttrDesc_glyph[index].offset = offset; offset += sizeof(glm::vec4); index += 1; } // instance - in_fg_color vertAttrDesc_glyph[index].binding = 3; vertAttrDesc_glyph[index].format = VK_FORMAT_R32G32B32A32_SFLOAT; vertAttrDesc_glyph[index].offset = offset; offset += sizeof(glm::vec4); index += 1; // instance - in_bg_color vertAttrDesc_glyph[index].binding = 3; vertAttrDesc_glyph[index].format = VK_FORMAT_R32G32B32A32_SFLOAT; vertAttrDesc_glyph[index].offset = offset; offset += sizeof(glm::vec4); index += 1; // instance - in_sprite_region_min vertAttrDesc_glyph[index].binding = 3; vertAttrDesc_glyph[index].format = VK_FORMAT_R32G32_SFLOAT; vertAttrDesc_glyph[index].offset = offset; offset += sizeof(glm::vec2); index += 1; // instance - in_sprite_region_max vertAttrDesc_glyph[index].binding = 3; vertAttrDesc_glyph[index].format = VK_FORMAT_R32G32_SFLOAT; vertAttrDesc_glyph[index].offset = offset; offset += sizeof(glm::vec2); index += 1; // instance - in_bounding_region_min vertAttrDesc_glyph[index].binding = 3; vertAttrDesc_glyph[index].format = VK_FORMAT_R32G32_SFLOAT; vertAttrDesc_glyph[index].offset = offset; offset += sizeof(glm::vec2); index += 1; // instance - in_bounding_region_max vertAttrDesc_glyph[index].binding = 3; vertAttrDesc_glyph[index].format = VK_FORMAT_R32G32_SFLOAT; vertAttrDesc_glyph[index].offset = offset; offset += sizeof(glm::vec2); index += 1; // instance - in_width vertAttrDesc_glyph[index].binding = 3; vertAttrDesc_glyph[index].format = VK_FORMAT_R32_SFLOAT; vertAttrDesc_glyph[index].offset = offset; // offset += sizeof(float); // index += 1; vkPipelineVertexInputStateCreateInfo_glyph.vertexBindingDescriptionCount = vertexBindingCount_glyph; vkPipelineVertexInputStateCreateInfo_glyph.pVertexBindingDescriptions = vertInputBD_glyph; vkPipelineVertexInputStateCreateInfo_glyph.vertexAttributeDescriptionCount = vertexAttrDescCount_glyph; vkPipelineVertexInputStateCreateInfo_glyph.pVertexAttributeDescriptions = vertAttrDesc_glyph; } index = 0; offset = 0; const long vertexBindingCount_ui_base = 3; VkVertexInputBindingDescription vertInputBD_ui_base[vertexBindingCount_ui_base]; const long vertexAttrDescCount_ui_base = 11; VkVertexInputAttributeDescription vertAttrDesc_ui_base[vertexAttrDescCount_ui_base]; VkPipelineVertexInputStateCreateInfo vkPipelineVertexInputStateCreateInfo_ui_base{vkPipelineVertexInputStateCreateInfo_txtr}; { // vertex - vertex vertInputBD_ui_base[index].binding = index; vertInputBD_ui_base[index].stride = sizeof(glm::vec2); vertInputBD_ui_base[index].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; index += 1; // vertex - UV vertInputBD_ui_base[index].binding = index; vertInputBD_ui_base[index].stride = sizeof(glm::vec2); vertInputBD_ui_base[index].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; index += 1; // instance - total vertInputBD_ui_base[index].binding = index; vertInputBD_ui_base[index].stride = 0 + sizeof(glm::mat4) // pos_scale + sizeof(glm::vec4) // in_border_color + sizeof(glm::vec4) // in_background_color + sizeof(glm::vec2) // px_scale + sizeof(float) // depth + sizeof(float) // texture_layer + 0; vertInputBD_ui_base[index].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE; // index += 1; index = 0; for (long i = 0; i < vertexAttrDescCount_ui_base; ++i) { vertAttrDesc_ui_base[i].location = i; } // vertex - vertex vertAttrDesc_ui_base[index].binding = 0; vertAttrDesc_ui_base[index].format = VK_FORMAT_R32G32_SFLOAT; vertAttrDesc_ui_base[index].offset = 0; index += 1; // vertex - UV vertAttrDesc_ui_base[index].binding = 1; vertAttrDesc_ui_base[index].format = VK_FORMAT_R32G32_SFLOAT; vertAttrDesc_ui_base[index].offset = 0; index += 1; // instance - pos_scale for (long i = 0; i < 4; ++i) { vertAttrDesc_ui_base[index].binding = 2; vertAttrDesc_ui_base[index].format = VK_FORMAT_R32G32B32A32_SFLOAT; vertAttrDesc_ui_base[index].offset = offset; offset += sizeof(glm::vec4); index += 1; } // instance - in_border_color vertAttrDesc_ui_base[index].binding = 2; vertAttrDesc_ui_base[index].format = VK_FORMAT_R32G32B32A32_SFLOAT; vertAttrDesc_ui_base[index].offset = offset; offset += sizeof(glm::vec4); index += 1; // instance - in_background_color vertAttrDesc_ui_base[index].binding = 2; vertAttrDesc_ui_base[index].format = VK_FORMAT_R32G32B32A32_SFLOAT; vertAttrDesc_ui_base[index].offset = offset; offset += sizeof(glm::vec4); index += 1; // instance - px_scale vertAttrDesc_ui_base[index].binding = 2; vertAttrDesc_ui_base[index].format = VK_FORMAT_R32G32_SFLOAT; vertAttrDesc_ui_base[index].offset = offset; offset += sizeof(glm::vec2); index += 1; // instance - depth vertAttrDesc_ui_base[index].binding = 2; vertAttrDesc_ui_base[index].format = VK_FORMAT_R32_SFLOAT; vertAttrDesc_ui_base[index].offset = offset; offset += sizeof(float); index += 1; // instance - texture_layer vertAttrDesc_ui_base[index].binding = 2; vertAttrDesc_ui_base[index].format = VK_FORMAT_R32_SFLOAT; vertAttrDesc_ui_base[index].offset = offset; // offset += sizeof(float); // index += 1; vkPipelineVertexInputStateCreateInfo_ui_base.vertexBindingDescriptionCount = vertexBindingCount_ui_base; vkPipelineVertexInputStateCreateInfo_ui_base.pVertexBindingDescriptions = vertInputBD_ui_base; vkPipelineVertexInputStateCreateInfo_ui_base.vertexAttributeDescriptionCount = vertexAttrDescCount_ui_base; vkPipelineVertexInputStateCreateInfo_ui_base.pVertexAttributeDescriptions = vertAttrDesc_ui_base; } VkPipelineInputAssemblyStateCreateInfo vkPipelineInputAssemblyStateCreateInfo; vkPipelineInputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; vkPipelineInputAssemblyStateCreateInfo.pNext = nullptr; vkPipelineInputAssemblyStateCreateInfo.flags = {}; vkPipelineInputAssemblyStateCreateInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; vkPipelineInputAssemblyStateCreateInfo.primitiveRestartEnable = VK_FALSE; // TODO - is this right? set to dynamic later VkPipelineViewportStateCreateInfo vkPipelineViewportStateCreateInfo; vkPipelineViewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; vkPipelineViewportStateCreateInfo.pNext = nullptr; vkPipelineViewportStateCreateInfo.flags = {}; vkPipelineViewportStateCreateInfo.viewportCount = 1; vkPipelineViewportStateCreateInfo.pViewports = nullptr; vkPipelineViewportStateCreateInfo.scissorCount = 1; vkPipelineViewportStateCreateInfo.pScissors = nullptr; VkPipelineRasterizationStateCreateInfo vkPipelineRasterizationStateCreateInfoFill; vkPipelineRasterizationStateCreateInfoFill.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; vkPipelineRasterizationStateCreateInfoFill.pNext = nullptr; vkPipelineRasterizationStateCreateInfoFill.flags = {}; vkPipelineRasterizationStateCreateInfoFill.depthClampEnable = VK_FALSE; vkPipelineRasterizationStateCreateInfoFill.rasterizerDiscardEnable = VK_FALSE; vkPipelineRasterizationStateCreateInfoFill.polygonMode = VK_POLYGON_MODE_FILL; vkPipelineRasterizationStateCreateInfoFill.cullMode = VK_CULL_MODE_BACK_BIT; vkPipelineRasterizationStateCreateInfoFill.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; vkPipelineRasterizationStateCreateInfoFill.depthBiasEnable = VK_FALSE; vkPipelineRasterizationStateCreateInfoFill.depthBiasConstantFactor = 0.0f; vkPipelineRasterizationStateCreateInfoFill.depthBiasClamp = 0.0f; vkPipelineRasterizationStateCreateInfoFill.depthBiasSlopeFactor = 0.0f; vkPipelineRasterizationStateCreateInfoFill.lineWidth = 1.0f; VkPipelineRasterizationStateCreateInfo vkPipelineRasterizationStateCreateInfoLine{vkPipelineRasterizationStateCreateInfoFill}; vkPipelineRasterizationStateCreateInfoLine.cullMode = VK_CULL_MODE_NONE; vkPipelineRasterizationStateCreateInfoLine.polygonMode = VK_POLYGON_MODE_LINE; vkPipelineRasterizationStateCreateInfoLine.lineWidth = 5.f; VkPipelineMultisampleStateCreateInfo vkPipelineMultisampleStateCreateInfo; vkPipelineMultisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; vkPipelineMultisampleStateCreateInfo.pNext = nullptr; vkPipelineMultisampleStateCreateInfo.flags = 0; vkPipelineMultisampleStateCreateInfo.rasterizationSamples = global_sample_count; vkPipelineMultisampleStateCreateInfo.sampleShadingEnable = VK_FALSE; vkPipelineMultisampleStateCreateInfo.minSampleShading = 0.0f; vkPipelineMultisampleStateCreateInfo.pSampleMask = nullptr; vkPipelineMultisampleStateCreateInfo.alphaToCoverageEnable = VK_FALSE; vkPipelineMultisampleStateCreateInfo.alphaToOneEnable = VK_FALSE; // TODO Transparency VkPipelineColorBlendAttachmentState vkPipelineColorBlendAttachmentState[1]; vkPipelineColorBlendAttachmentState[0].blendEnable = VK_FALSE; vkPipelineColorBlendAttachmentState[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; vkPipelineColorBlendAttachmentState[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; vkPipelineColorBlendAttachmentState[0].colorBlendOp = VK_BLEND_OP_ADD; vkPipelineColorBlendAttachmentState[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; vkPipelineColorBlendAttachmentState[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; vkPipelineColorBlendAttachmentState[0].alphaBlendOp = VK_BLEND_OP_SUBTRACT; vkPipelineColorBlendAttachmentState[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; VkPipelineColorBlendStateCreateInfo vkPipelineColorBlendStateCreateInfo; vkPipelineColorBlendStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; vkPipelineColorBlendStateCreateInfo.pNext = nullptr; vkPipelineColorBlendStateCreateInfo.flags = {}; vkPipelineColorBlendStateCreateInfo.logicOpEnable = VK_FALSE; vkPipelineColorBlendStateCreateInfo.logicOp = VK_LOGIC_OP_COPY; vkPipelineColorBlendStateCreateInfo.attachmentCount = 1; vkPipelineColorBlendStateCreateInfo.pAttachments = vkPipelineColorBlendAttachmentState; vkPipelineColorBlendStateCreateInfo.blendConstants[0] = 0.0f; vkPipelineColorBlendStateCreateInfo.blendConstants[1] = 0.0f; vkPipelineColorBlendStateCreateInfo.blendConstants[2] = 0.0f; vkPipelineColorBlendStateCreateInfo.blendConstants[3] = 0.0f; VkDynamicState dynamicStates[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo vkPipelineDynamicStateCreateInfo; vkPipelineDynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; vkPipelineDynamicStateCreateInfo.pNext = nullptr; vkPipelineDynamicStateCreateInfo.flags = {}; vkPipelineDynamicStateCreateInfo.dynamicStateCount = 2; vkPipelineDynamicStateCreateInfo.pDynamicStates = dynamicStates; VkPipelineDepthStencilStateCreateInfo vkPipelineDepthStencilStateCreateInfo; vkPipelineDepthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; vkPipelineDepthStencilStateCreateInfo.pNext = nullptr; vkPipelineDepthStencilStateCreateInfo.flags = {}; vkPipelineDepthStencilStateCreateInfo.depthTestEnable = VK_TRUE; vkPipelineDepthStencilStateCreateInfo.depthWriteEnable = VK_TRUE; vkPipelineDepthStencilStateCreateInfo.depthCompareOp = VK_COMPARE_OP_LESS; vkPipelineDepthStencilStateCreateInfo.depthBoundsTestEnable = VK_FALSE; vkPipelineDepthStencilStateCreateInfo.stencilTestEnable = VK_FALSE; vkPipelineDepthStencilStateCreateInfo.front = {}; vkPipelineDepthStencilStateCreateInfo.back = {}; vkPipelineDepthStencilStateCreateInfo.minDepthBounds = {}; vkPipelineDepthStencilStateCreateInfo.maxDepthBounds = {}; VkPipelineShaderStageCreateInfo vkPipelineShaderStageCreateInfo_txtr[2]; for (long i = 0; i < 2; ++i) { vkPipelineShaderStageCreateInfo_txtr[i].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; vkPipelineShaderStageCreateInfo_txtr[i].pNext = nullptr; vkPipelineShaderStageCreateInfo_txtr[i].flags = {}; vkPipelineShaderStageCreateInfo_txtr[i].pName = "main"; vkPipelineShaderStageCreateInfo_txtr[i].pSpecializationInfo = nullptr; } vkPipelineShaderStageCreateInfo_txtr[0].stage = VK_SHADER_STAGE_VERTEX_BIT; vkPipelineShaderStageCreateInfo_txtr[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; VkPipelineShaderStageCreateInfo vkPipelineShaderStageCreateInfo_glyph[2]; for (long i = 0; i < 2; ++i) { vkPipelineShaderStageCreateInfo_glyph[i].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; vkPipelineShaderStageCreateInfo_glyph[i].pNext = nullptr; vkPipelineShaderStageCreateInfo_glyph[i].flags = {}; vkPipelineShaderStageCreateInfo_glyph[i].pName = "main"; vkPipelineShaderStageCreateInfo_glyph[i].pSpecializationInfo = nullptr; } vkPipelineShaderStageCreateInfo_glyph[0].stage = VK_SHADER_STAGE_VERTEX_BIT; vkPipelineShaderStageCreateInfo_glyph[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; VkPipelineShaderStageCreateInfo vkPipelineShaderStageCreateInfo_ui_base[2]; for (long i = 0; i < 2; ++i) { vkPipelineShaderStageCreateInfo_ui_base[i].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; vkPipelineShaderStageCreateInfo_ui_base[i].pNext = nullptr; vkPipelineShaderStageCreateInfo_ui_base[i].flags = {}; vkPipelineShaderStageCreateInfo_ui_base[i].pName = "main"; vkPipelineShaderStageCreateInfo_ui_base[i].pSpecializationInfo = nullptr; } vkPipelineShaderStageCreateInfo_ui_base[0].stage = VK_SHADER_STAGE_VERTEX_BIT; vkPipelineShaderStageCreateInfo_ui_base[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; /* pipelines * 0: entity_standard * 1: entity_wireframe * 2: glyph * 3: ui_base * 4: ui_txtr */ std::array vkGraphicsPipelineCreateInfo; for (size_t i = 0; i < vkGraphicsPipelineCreateInfo.size(); ++i) { vkGraphicsPipelineCreateInfo[i].sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; vkGraphicsPipelineCreateInfo[i].pNext = nullptr; vkGraphicsPipelineCreateInfo[i].flags = {}; vkGraphicsPipelineCreateInfo[i].stageCount = 2; vkGraphicsPipelineCreateInfo[i].pInputAssemblyState = &vkPipelineInputAssemblyStateCreateInfo; vkGraphicsPipelineCreateInfo[i].pTessellationState = nullptr; vkGraphicsPipelineCreateInfo[i].pViewportState = &vkPipelineViewportStateCreateInfo; vkGraphicsPipelineCreateInfo[i].pRasterizationState = &vkPipelineRasterizationStateCreateInfoFill; vkGraphicsPipelineCreateInfo[i].pMultisampleState = &vkPipelineMultisampleStateCreateInfo; vkGraphicsPipelineCreateInfo[i].pDepthStencilState = &vkPipelineDepthStencilStateCreateInfo; vkGraphicsPipelineCreateInfo[i].pColorBlendState = &vkPipelineColorBlendStateCreateInfo; vkGraphicsPipelineCreateInfo[i].pDynamicState = &vkPipelineDynamicStateCreateInfo; vkGraphicsPipelineCreateInfo[i].subpass = 0; vkGraphicsPipelineCreateInfo[i].basePipelineHandle = VK_NULL_HANDLE; vkGraphicsPipelineCreateInfo[i].basePipelineIndex = {}; vkGraphicsPipelineCreateInfo[i].renderPass = VK_NULL_HANDLE; // dynamic rendering } vkGraphicsPipelineCreateInfo[0].layout = pkePipelines.pipe_layouts.named.ubo_txtr; vkGraphicsPipelineCreateInfo[1].layout = pkePipelines.pipe_layouts.named.ubo_txtr; vkGraphicsPipelineCreateInfo[2].layout = pkePipelines.pipe_layouts.named.txtr; vkGraphicsPipelineCreateInfo[3].layout = pkePipelines.pipe_layouts.named.base; vkGraphicsPipelineCreateInfo[4].layout = pkePipelines.pipe_layouts.named.txtr; vkGraphicsPipelineCreateInfo[0].pVertexInputState = &vkPipelineVertexInputStateCreateInfo_txtr; vkGraphicsPipelineCreateInfo[1].pVertexInputState = &vkPipelineVertexInputStateCreateInfo_txtr; vkGraphicsPipelineCreateInfo[2].pVertexInputState = &vkPipelineVertexInputStateCreateInfo_glyph; vkGraphicsPipelineCreateInfo[3].pVertexInputState = &vkPipelineVertexInputStateCreateInfo_ui_base; vkGraphicsPipelineCreateInfo[4].pVertexInputState = &vkPipelineVertexInputStateCreateInfo_ui_base; vkGraphicsPipelineCreateInfo[1].pRasterizationState = &vkPipelineRasterizationStateCreateInfoLine; vkGraphicsPipelineCreateInfo[0].pStages = vkPipelineShaderStageCreateInfo_txtr; vkGraphicsPipelineCreateInfo[1].pStages = vkPipelineShaderStageCreateInfo_txtr; vkGraphicsPipelineCreateInfo[2].pStages = vkPipelineShaderStageCreateInfo_glyph; vkGraphicsPipelineCreateInfo[3].pStages = vkPipelineShaderStageCreateInfo_ui_base; vkGraphicsPipelineCreateInfo[4].pStages = vkPipelineShaderStageCreateInfo_ui_base; // deffered shader creation auto shader_module_3d_vert = UploadShader(vertShaderAssetHandle); auto shader_module_3d_frag = UploadShader(textureFragShaderAssetHandle); auto shader_module_glyph_vert = UploadShader(vertGlyphAssetHandle); auto shader_module_glyph_frag = UploadShader(fragGlyphAssetHandle); auto shader_module_ui_base_vert = UploadShader(asset_handle_vert_ui_base); auto shader_module_ui_clr_frag = UploadShader(asset_handle_frag_ui_base); auto shader_module_ui_txtr_frag = UploadShader(asset_handle_frag_ui_txtr); vkPipelineShaderStageCreateInfo_txtr[0].module = shader_module_3d_vert; vkPipelineShaderStageCreateInfo_txtr[1].module = shader_module_3d_frag; vkPipelineShaderStageCreateInfo_glyph[0].module = shader_module_glyph_vert; vkPipelineShaderStageCreateInfo_glyph[1].module = shader_module_glyph_frag; vkPipelineShaderStageCreateInfo_ui_base[0].module = shader_module_ui_base_vert; vkPipelineShaderStageCreateInfo_ui_base[1].module = shader_module_ui_clr_frag; VkPipelineRenderingCreateInfo renderingInfo{}; vkGraphicsPipelineCreateInfo[0].pNext = &renderingInfo; vkGraphicsPipelineCreateInfo[1].pNext = &renderingInfo; vkGraphicsPipelineCreateInfo[2].pNext = &renderingInfo; vkGraphicsPipelineCreateInfo[3].pNext = &renderingInfo; vkGraphicsPipelineCreateInfo[4].pNext = &renderingInfo; renderingInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; std::array color_formats; color_formats.at(0) = vkSurfaceFormatKHR.format; color_formats.at(1) = depthFormat; color_formats.at(2) = depthFormat; renderingInfo.colorAttachmentCount = 1; renderingInfo.pColorAttachmentFormats = color_formats.data(); renderingInfo.depthAttachmentFormat = color_formats.at(1); if (!pkvk_is_depth_only_format(depthFormat)) { renderingInfo.stencilAttachmentFormat = color_formats.at(2); } vkResult = vkCreateGraphicsPipelines(vkDevice, VK_NULL_HANDLE, 1, &vkGraphicsPipelineCreateInfo[0], vkAllocator, &pkePipelines.pipelines.named.entity_standard); assert(vkResult == VK_SUCCESS); vkResult = vkCreateGraphicsPipelines(vkDevice, VK_NULL_HANDLE, 1, &vkGraphicsPipelineCreateInfo[1], vkAllocator, &pkePipelines.pipelines.named.entity_wireframe); assert(vkResult == VK_SUCCESS); renderingInfo.depthAttachmentFormat = VK_FORMAT_UNDEFINED; vkResult = vkCreateGraphicsPipelines(vkDevice, VK_NULL_HANDLE, 1, &vkGraphicsPipelineCreateInfo[2], vkAllocator, &pkePipelines.pipelines.named.font_glyph); assert(vkResult == VK_SUCCESS); vkResult = vkCreateGraphicsPipelines(vkDevice, VK_NULL_HANDLE, 1, &vkGraphicsPipelineCreateInfo[3], vkAllocator, &pkePipelines.pipelines.named.ui_base); assert(vkResult == VK_SUCCESS); vkPipelineShaderStageCreateInfo_ui_base[1].module = shader_module_ui_txtr_frag; vkResult = vkCreateGraphicsPipelines(vkDevice, VK_NULL_HANDLE, 1, &vkGraphicsPipelineCreateInfo[4], vkAllocator, &pkePipelines.pipelines.named.ui_txtr); assert(vkResult == VK_SUCCESS); assert(pkePipelines.pipelines.named.entity_standard != VK_NULL_HANDLE); assert(pkePipelines.pipelines.named.entity_wireframe != VK_NULL_HANDLE); assert(pkePipelines.pipelines.named.font_glyph != VK_NULL_HANDLE); assert(pkePipelines.pipelines.named.ui_base != VK_NULL_HANDLE); assert(pkePipelines.pipelines.named.ui_txtr != VK_NULL_HANDLE); vkDestroyShaderModule(vkDevice, shader_module_ui_txtr_frag, vkAllocator); vkDestroyShaderModule(vkDevice, shader_module_ui_clr_frag, vkAllocator); vkDestroyShaderModule(vkDevice, shader_module_ui_base_vert, vkAllocator); vkDestroyShaderModule(vkDevice, shader_module_glyph_frag, vkAllocator); vkDestroyShaderModule(vkDevice, shader_module_glyph_vert, vkAllocator); vkDestroyShaderModule(vkDevice, shader_module_3d_frag, vkAllocator); vkDestroyShaderModule(vkDevice, shader_module_3d_vert, vkAllocator); } // debug texture { VkImageCreateInfo dbgTextureCI; dbgTextureCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; dbgTextureCI.pNext = nullptr; dbgTextureCI.flags = 0; dbgTextureCI.imageType = VK_IMAGE_TYPE_2D; dbgTextureCI.format = VK_FORMAT_R8G8B8A8_SRGB; dbgTextureCI.extent = VkExtent3D { .width = 2, .height = 2, .depth = 1, }; dbgTextureCI.mipLevels = 1; dbgTextureCI.arrayLayers = 1; dbgTextureCI.samples = VK_SAMPLE_COUNT_1_BIT; dbgTextureCI.tiling = VK_IMAGE_TILING_OPTIMAL; dbgTextureCI.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; dbgTextureCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; dbgTextureCI.queueFamilyIndexCount = 0; dbgTextureCI.pQueueFamilyIndices = nullptr; dbgTextureCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VkResult result; result = vkCreateImage(vkDevice, &dbgTextureCI, vkAllocator, &pkeDebugHitbox.vkImage); assert(result == VK_SUCCESS); VkMemoryRequirements imageMemReqs; vkGetImageMemoryRequirements(vkDevice, pkeDebugHitbox.vkImage, &imageMemReqs); VkMemoryAllocateInfo imageAllocInfo; imageAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; imageAllocInfo.pNext = nullptr; imageAllocInfo.allocationSize = imageMemReqs.size; imageAllocInfo.memoryTypeIndex = FindMemoryTypeIndex(imageMemReqs.memoryTypeBits, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); if (imageAllocInfo.memoryTypeIndex == 0) { imageAllocInfo.memoryTypeIndex = FindMemoryTypeIndex(imageMemReqs.memoryTypeBits, 0); } result = vkAllocateMemory(vkDevice, &imageAllocInfo, vkAllocator, &pkeDebugHitbox.textureMemory); assert(result == VK_SUCCESS); result = vkBindImageMemory(vkDevice, pkeDebugHitbox.vkImage, pkeDebugHitbox.textureMemory, 0); assert(result == VK_SUCCESS); VkImageViewCreateInfo dbgImageViewCI; dbgImageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; dbgImageViewCI.pNext = nullptr; dbgImageViewCI.flags = 0; dbgImageViewCI.image = pkeDebugHitbox.vkImage; dbgImageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; dbgImageViewCI.format = VK_FORMAT_R8G8B8A8_SRGB; dbgImageViewCI.components = VkComponentMapping { .r = VK_COMPONENT_SWIZZLE_IDENTITY, .g = VK_COMPONENT_SWIZZLE_IDENTITY, .b = VK_COMPONENT_SWIZZLE_IDENTITY, .a = VK_COMPONENT_SWIZZLE_IDENTITY, }; dbgImageViewCI.subresourceRange = VkImageSubresourceRange { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }; result = vkCreateImageView(vkDevice, &dbgImageViewCI, vkAllocator, &pkeDebugHitbox.vkImageView); assert(result == VK_SUCCESS); PKVK_BeginBuffer(transferFamilyIndex, 4 * 4, tmpBufferDetails); memcpy(tmpBufferDetails.deviceData, PKE_MISSING_TEXTURE_DATA, 4 * 4); { VkImageMemoryBarrier vkImageMemoryBarrier; vkImageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; vkImageMemoryBarrier.pNext = nullptr; vkImageMemoryBarrier.srcAccessMask = {}; vkImageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; vkImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; vkImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; vkImageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkImageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkImageMemoryBarrier.image = pkeDebugHitbox.vkImage; vkImageMemoryBarrier.subresourceRange = VkImageSubresourceRange { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }; VkCommandBufferBeginInfo vkCommandBufferBeginInfo; vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkCommandBufferBeginInfo.pNext = nullptr; vkCommandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkCommandBufferBeginInfo.pInheritanceInfo = nullptr; vkBeginCommandBuffer(pkvk_shared.command_buffer.transfer, &vkCommandBufferBeginInfo); vkCmdPipelineBarrier(pkvk_shared.command_buffer.transfer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &vkImageMemoryBarrier); VkBufferImageCopy vkBufferImageCopy; vkBufferImageCopy.bufferOffset = 0; vkBufferImageCopy.bufferRowLength = 2; vkBufferImageCopy.bufferImageHeight = 2; vkBufferImageCopy.imageSubresource = VkImageSubresourceLayers { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .baseArrayLayer = 0, .layerCount = 1, }; vkBufferImageCopy.imageOffset = VkOffset3D { .x = 0, .y = 0, .z = 0, }; vkBufferImageCopy.imageExtent = VkExtent3D { .width = 2, .height = 2, .depth = 1, }; vkCmdCopyBufferToImage(pkvk_shared.command_buffer.transfer, tmpBufferDetails.buffer, pkeDebugHitbox.vkImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &vkBufferImageCopy); vkEndCommandBuffer(pkvk_shared.command_buffer.transfer); VkSubmitInfo submitInfo; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = nullptr; submitInfo.pWaitDstStageMask = nullptr; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &pkvk_shared.command_buffer.transfer; submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = nullptr; vkQueueSubmit(tmpBufferDetails.queue, 1, &submitInfo, nullptr); vkQueueWaitIdle(tmpBufferDetails.queue); vkResetCommandBuffer(pkvk_shared.command_buffer.transfer, 0); } PKVK_EndBuffer(tmpBufferDetails); { VkImageMemoryBarrier vkImageMemoryBarrier; vkImageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; vkImageMemoryBarrier.pNext = nullptr; vkImageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; vkImageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; vkImageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; vkImageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkImageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkImageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; vkImageMemoryBarrier.image = pkeDebugHitbox.vkImage; vkImageMemoryBarrier.subresourceRange = VkImageSubresourceRange { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }; VkCommandBufferBeginInfo vkCommandBufferBeginInfo; vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkCommandBufferBeginInfo.pNext = nullptr; vkCommandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkCommandBufferBeginInfo.pInheritanceInfo = nullptr; vkBeginCommandBuffer(pkvk_shared.command_buffer.graphics, &vkCommandBufferBeginInfo); vkCmdPipelineBarrier(pkvk_shared.command_buffer.graphics, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &vkImageMemoryBarrier); vkEndCommandBuffer(pkvk_shared.command_buffer.graphics); VkSubmitInfo submitInfo; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = nullptr; submitInfo.pWaitDstStageMask = nullptr; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &pkvk_shared.command_buffer.graphics; submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = nullptr; vkQueueSubmit(pkvk_shared.queue.graphics, 1, &submitInfo, nullptr); vkQueueWaitIdle(pkvk_shared.queue.graphics); } } // debug model { // ((sizeof(glm::vec3) + sizeof(glm::vec3) + sizeof(glm::vec2)) * 8) // + (sizeof(uint16_t) * 36); long index = 0; VkMemoryRequirements vkMemoryRequirements[4]; VkBufferCreateInfo dbgModelBufferCI{}; dbgModelBufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; dbgModelBufferCI.pNext = nullptr; dbgModelBufferCI.flags = 0; dbgModelBufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; dbgModelBufferCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; dbgModelBufferCI.queueFamilyIndexCount = 1; dbgModelBufferCI.pQueueFamilyIndices = &graphicsFamilyIndex; // vertex dbgModelBufferCI.size = sizeof(glm::vec3) * 8; vkCreateBuffer(vkDevice, &dbgModelBufferCI, vkAllocator, &pkeDebugHitbox.vertexBuffer); vkGetBufferMemoryRequirements(vkDevice, pkeDebugHitbox.vertexBuffer, &vkMemoryRequirements[index]); index++; // normals dbgModelBufferCI.size = sizeof(glm::vec3) * 8; vkCreateBuffer(vkDevice, &dbgModelBufferCI, vkAllocator, &pkeDebugHitbox.normalsBuffer); vkGetBufferMemoryRequirements(vkDevice, pkeDebugHitbox.normalsBuffer, &vkMemoryRequirements[index]); index++; // uv dbgModelBufferCI.size = sizeof(glm::vec2) * 8; vkCreateBuffer(vkDevice, &dbgModelBufferCI, vkAllocator, &pkeDebugHitbox.uvBuffer); vkGetBufferMemoryRequirements(vkDevice, pkeDebugHitbox.uvBuffer, &vkMemoryRequirements[index]); index++; // index dbgModelBufferCI.size = sizeof(uint16_t) * 36; dbgModelBufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT; vkCreateBuffer(vkDevice, &dbgModelBufferCI, vkAllocator, &pkeDebugHitbox.indexBuffer); vkGetBufferMemoryRequirements(vkDevice, pkeDebugHitbox.indexBuffer, &vkMemoryRequirements[index]); pkeDebugHitbox.indexCount = 36; // index++; VkMemoryRequirements combinedMemReqs{}; combinedMemReqs.alignment = vkMemoryRequirements[0].alignment; combinedMemReqs.memoryTypeBits = 0; combinedMemReqs.size = 0; for (long i = 1; i < 4; ++i) { if (combinedMemReqs.alignment == vkMemoryRequirements[i].alignment) { continue; } int larger, smaller; if (combinedMemReqs.alignment > vkMemoryRequirements[i].alignment) { larger = combinedMemReqs.alignment; smaller = vkMemoryRequirements[i].alignment; } else { larger = vkMemoryRequirements[i].alignment; smaller = combinedMemReqs.alignment; } if (larger % smaller == 0) { combinedMemReqs.alignment = larger; continue; } int combined = larger * smaller; while ((combined / 2) % 2 == 0 && (combined / 2) % larger == 0) { combined /= 2; } combinedMemReqs.alignment = combined; } for (long i = 0; i < 4; ++i) { uint32_t alignmentPadding = vkMemoryRequirements[i].size % combinedMemReqs.alignment; combinedMemReqs.size += vkMemoryRequirements[i].size + (alignmentPadding == 0 ? 0 : combinedMemReqs.alignment - alignmentPadding); combinedMemReqs.memoryTypeBits |= vkMemoryRequirements[i].memoryTypeBits; } VkMemoryAllocateInfo vkMemoryAllocateInfo; vkMemoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; vkMemoryAllocateInfo.pNext = nullptr; vkMemoryAllocateInfo.allocationSize = combinedMemReqs.size; vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(combinedMemReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &pkeDebugHitbox.vertBufferMemory); vkDestroyBuffer(vkDevice, pkeDebugHitbox.indexBuffer, vkAllocator); vkDestroyBuffer(vkDevice, pkeDebugHitbox.uvBuffer, vkAllocator); vkDestroyBuffer(vkDevice, pkeDebugHitbox.normalsBuffer, vkAllocator); vkDestroyBuffer(vkDevice, pkeDebugHitbox.vertexBuffer, vkAllocator); // bind buffers uint32_t runningOffset = 0; uint32_t alignmentPadding; // vertex uint32_t offsetVert = runningOffset; uint32_t sizeVert = sizeof(glm::vec3) * 8; alignmentPadding = sizeVert % combinedMemReqs.alignment; alignmentPadding = alignmentPadding == 0 ? 0 : combinedMemReqs.alignment - alignmentPadding; sizeVert += alignmentPadding; dbgModelBufferCI.size = sizeVert; dbgModelBufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; vkCreateBuffer(vkDevice, &dbgModelBufferCI, vkAllocator, &pkeDebugHitbox.vertexBuffer); vkBindBufferMemory(vkDevice, pkeDebugHitbox.vertexBuffer, pkeDebugHitbox.vertBufferMemory, offsetVert); runningOffset += sizeVert; // norm uint32_t offsetNorm = runningOffset; uint32_t sizeNorm = sizeof(glm::vec3) * 8; alignmentPadding = sizeNorm % combinedMemReqs.alignment; alignmentPadding = alignmentPadding == 0 ? 0 : combinedMemReqs.alignment - alignmentPadding; sizeNorm += alignmentPadding; dbgModelBufferCI.size = sizeNorm; vkCreateBuffer(vkDevice, &dbgModelBufferCI, vkAllocator, &pkeDebugHitbox.normalsBuffer); vkBindBufferMemory(vkDevice, pkeDebugHitbox.normalsBuffer, pkeDebugHitbox.vertBufferMemory, offsetNorm); runningOffset += sizeNorm; // uv uint32_t offsetUV = runningOffset; uint32_t sizeUV = sizeof(glm::vec2) * 8; alignmentPadding = sizeUV % combinedMemReqs.alignment; alignmentPadding = alignmentPadding == 0 ? 0 : combinedMemReqs.alignment - alignmentPadding; sizeUV += alignmentPadding; dbgModelBufferCI.size = sizeUV; vkCreateBuffer(vkDevice, &dbgModelBufferCI, vkAllocator, &pkeDebugHitbox.uvBuffer); vkBindBufferMemory(vkDevice, pkeDebugHitbox.uvBuffer , pkeDebugHitbox.vertBufferMemory, offsetUV); runningOffset += sizeUV; // index uint32_t offsetIndex = runningOffset; uint32_t sizeIndex_unpadded = sizeof(uint16_t) * 36; uint32_t sizeIndex = sizeIndex_unpadded; alignmentPadding = sizeIndex % combinedMemReqs.alignment; alignmentPadding = alignmentPadding == 0 ? 0 : combinedMemReqs.alignment - alignmentPadding; sizeIndex += alignmentPadding; dbgModelBufferCI.size = sizeIndex; dbgModelBufferCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT; vkCreateBuffer(vkDevice, &dbgModelBufferCI, vkAllocator, &pkeDebugHitbox.indexBuffer); vkBindBufferMemory(vkDevice, pkeDebugHitbox.indexBuffer, pkeDebugHitbox.vertBufferMemory, offsetIndex); runningOffset += sizeIndex; assert(runningOffset == combinedMemReqs.size); // create transfer items && transfer { PKVK_BeginBuffer(transferFamilyIndex, combinedMemReqs.size, tmpBufferDetails); memset(tmpBufferDetails.deviceData, '\0', combinedMemReqs.size); char *dstPtr = nullptr; char *srcPtr = nullptr; dstPtr = static_cast(tmpBufferDetails.deviceData) + offsetVert; srcPtr = reinterpret_cast(pkeIntrinsicsCube.vert); memcpy(dstPtr, srcPtr, sizeVert); dstPtr = static_cast(tmpBufferDetails.deviceData) + offsetNorm; srcPtr = reinterpret_cast(pkeIntrinsicsCube.norm); memcpy(dstPtr, srcPtr, sizeNorm); dstPtr = static_cast(tmpBufferDetails.deviceData) + offsetUV; srcPtr = reinterpret_cast(pkeIntrinsicsCube.uv); memcpy(dstPtr, srcPtr, sizeUV); dstPtr = static_cast(tmpBufferDetails.deviceData) + offsetIndex; srcPtr = reinterpret_cast(pkeIntrinsicsCube.index); memcpy(dstPtr, srcPtr, sizeIndex_unpadded); if (sizeIndex != sizeIndex_unpadded) { memset(dstPtr+sizeIndex_unpadded, 0, sizeIndex-sizeIndex_unpadded); } VkCommandBufferBeginInfo vkCommandBufferBeginInfo; vkCommandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkCommandBufferBeginInfo.pNext = nullptr; // TODO consider single-use? vkCommandBufferBeginInfo.flags = 0; vkCommandBufferBeginInfo.pInheritanceInfo = nullptr; vkBeginCommandBuffer(pkvk_shared.command_buffer.transfer, &vkCommandBufferBeginInfo); VkBufferCopy bufferCopys[4]; for (long i = 0; i < 4; ++i) { bufferCopys[i].dstOffset = 0; } index = 0; bufferCopys[index].srcOffset = offsetVert; bufferCopys[index].size = sizeVert; vkCmdCopyBuffer(pkvk_shared.command_buffer.transfer, tmpBufferDetails.buffer, pkeDebugHitbox.vertexBuffer, 1, &bufferCopys[index]); index+=1; bufferCopys[index].srcOffset = offsetNorm; bufferCopys[index].size = sizeNorm; vkCmdCopyBuffer(pkvk_shared.command_buffer.transfer, tmpBufferDetails.buffer, pkeDebugHitbox.normalsBuffer, 1, &bufferCopys[index]); index+=1; bufferCopys[index].srcOffset = offsetUV; bufferCopys[index].size = sizeUV; vkCmdCopyBuffer(pkvk_shared.command_buffer.transfer, tmpBufferDetails.buffer, pkeDebugHitbox.uvBuffer, 1, &bufferCopys[index]); index+=1; bufferCopys[index].srcOffset = offsetIndex; bufferCopys[index].size = sizeIndex; vkCmdCopyBuffer(pkvk_shared.command_buffer.transfer, tmpBufferDetails.buffer, pkeDebugHitbox.indexBuffer, 1, &bufferCopys[index]); // index+=1; vkEndCommandBuffer(pkvk_shared.command_buffer.transfer); VkSubmitInfo submitInfo; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = nullptr; submitInfo.pWaitDstStageMask = nullptr; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &pkvk_shared.command_buffer.transfer; submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = nullptr; vkQueueSubmit(tmpBufferDetails.queue, 1, &submitInfo, nullptr); vkQueueWaitIdle(tmpBufferDetails.queue); PKVK_EndBuffer(tmpBufferDetails); } } } void DestroyDebugDescriptors() { if (pkeDebugHitbox.vkDescriptorPool != VK_NULL_HANDLE) { vkDestroyDescriptorPool(vkDevice, pkeDebugHitbox.vkDescriptorPool, vkAllocator); pk_delete_arr(pkeDebugHitbox.vkDescriptorSets, prevSwapchainLength, MemBkt_Vulkan); pkeDebugHitbox.vkDescriptorPool = VK_NULL_HANDLE; pkeDebugHitbox.vkDescriptorSets = nullptr; } } void CreateDebugDescriptors() { assert(pkeDebugHitbox.vkDescriptorPool == VK_NULL_HANDLE); assert(pkeDebugHitbox.vkDescriptorSets == nullptr || pkeDebugHitbox.vkDescriptorSets == CAFE_BABE(VkDescriptorSet)); // descriptor pool & sets VkDescriptorPoolSize descriptorPoolSizes[2]; descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; descriptorPoolSizes[0].descriptorCount = swapchainLength; descriptorPoolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; descriptorPoolSizes[1].descriptorCount = swapchainLength; VkDescriptorPoolCreateInfo vkDescriptorPoolCreateInfo; vkDescriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; vkDescriptorPoolCreateInfo.pNext = nullptr; vkDescriptorPoolCreateInfo.flags = 0; vkDescriptorPoolCreateInfo.maxSets = swapchainLength; vkDescriptorPoolCreateInfo.poolSizeCount = (uint32_t)2; vkDescriptorPoolCreateInfo.pPoolSizes = descriptorPoolSizes; // consider making me a global pool auto vkResult = vkCreateDescriptorPool(vkDevice, &vkDescriptorPoolCreateInfo, vkAllocator, &pkeDebugHitbox.vkDescriptorPool); assert(vkResult == VK_SUCCESS); VkDescriptorSetLayout *descriptorSets = pk_new_arr(swapchainLength, pkeSettings.mem_bkt.game_transient); for (long i = 0; i < swapchainLength; ++i) { descriptorSets[i] = pkePipelines.descr_layouts.named.ubo_txtr; } VkDescriptorSetAllocateInfo vkDescriptorSetAllocateInfo; vkDescriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; vkDescriptorSetAllocateInfo.pNext = nullptr; vkDescriptorSetAllocateInfo.descriptorPool = pkeDebugHitbox.vkDescriptorPool; vkDescriptorSetAllocateInfo.descriptorSetCount = swapchainLength; vkDescriptorSetAllocateInfo.pSetLayouts = descriptorSets; pkeDebugHitbox.vkDescriptorSets = pk_new_arr(swapchainLength, MemBkt_Vulkan); for (long i = 0; i < swapchainLength; ++i) { pkeDebugHitbox.vkDescriptorSets[i] = VkDescriptorSet{}; } vkResult = vkAllocateDescriptorSets(vkDevice, &vkDescriptorSetAllocateInfo, pkeDebugHitbox.vkDescriptorSets); // descriptorSets = CAFE_BABE(VkDescriptorSetLayout); assert(vkResult == VK_SUCCESS); } void UpdateDebugDescriptors() { VkWriteDescriptorSet *writeDescriptorSets = pk_new_arr(2 * swapchainLength, pkeSettings.mem_bkt.game_transient); for (long i = 0; i < 2 * swapchainLength; ++i) { writeDescriptorSets[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[i].pNext = nullptr; writeDescriptorSets[i].dstSet = nullptr; writeDescriptorSets[i].dstBinding = i % 2; writeDescriptorSets[i].dstArrayElement = 0; writeDescriptorSets[i].descriptorCount = 1; writeDescriptorSets[i].descriptorType = (i % 2) == 0 ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER : VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[i].pImageInfo = nullptr; writeDescriptorSets[i].pBufferInfo = nullptr; writeDescriptorSets[i].pTexelBufferView = nullptr; } VkDescriptorImageInfo textureDescriptorInfo; textureDescriptorInfo.sampler = global_sampler; textureDescriptorInfo.imageView = pkeDebugHitbox.vkImageView; textureDescriptorInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkDescriptorBufferInfo *vkDescriptorBufferInfo = pk_new_arr(swapchainLength, pkeSettings.mem_bkt.game_transient); for (long i = 0; i < swapchainLength; ++i) { vkDescriptorBufferInfo[i].buffer = UniformBuffers[i]; vkDescriptorBufferInfo[i].offset = 0; vkDescriptorBufferInfo[i].range = sizeof(UniformBufferObject); long uboIndex = i * 2; long samplerIndex = uboIndex + 1; writeDescriptorSets[uboIndex].pBufferInfo = &vkDescriptorBufferInfo[i]; writeDescriptorSets[uboIndex].dstSet = pkeDebugHitbox.vkDescriptorSets[i]; writeDescriptorSets[samplerIndex].pImageInfo = &textureDescriptorInfo; writeDescriptorSets[samplerIndex].dstSet = pkeDebugHitbox.vkDescriptorSets[i]; } vkUpdateDescriptorSets(vkDevice, 2 * swapchainLength, writeDescriptorSets, 0, nullptr); } void CreateImGui() { IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; ImGui::StyleColorsDark(); VkDescriptorPoolSize poolSizes[] = { {VK_DESCRIPTOR_TYPE_SAMPLER, 1000}, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000}, {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000}, {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000}, {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000}, {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000}, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000}, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000}, {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000}, }; VkDescriptorPoolCreateInfo descriptorPoolCreateInfo; descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptorPoolCreateInfo.pNext = nullptr; descriptorPoolCreateInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; descriptorPoolCreateInfo.maxSets = 1000 * IM_ARRAYSIZE(poolSizes), descriptorPoolCreateInfo.poolSizeCount = static_cast(IM_ARRAYSIZE(poolSizes)); descriptorPoolCreateInfo.pPoolSizes = poolSizes; vkCreateDescriptorPool(vkDevice, &descriptorPoolCreateInfo, vkAllocator, &imGuiDescriptorPool); ImGui_ImplGlfw_InitForVulkan(window, true); ImGui_ImplVulkan_InitInfo initInfo = {}; initInfo.Allocator = vkAllocator; initInfo.CheckVkResultFn = ImGuiCheckVkResult; initInfo.DescriptorPool = imGuiDescriptorPool; initInfo.Device = vkDevice; initInfo.ImageCount = swapchainLength; initInfo.Instance = vkInstance; initInfo.MSAASamples = VK_SAMPLE_COUNT_1_BIT; initInfo.MinImageCount = swapchainLength; initInfo.PhysicalDevice = vkPhysicalDevice; initInfo.PipelineCache = {}; initInfo.Queue = pkvk_shared.queue.graphics; initInfo.QueueFamily = graphicsFamilyIndex; initInfo.RenderPass = VK_NULL_HANDLE; // dynamic rendering initInfo.Subpass = 0; initInfo.UseDynamicRendering = true; VkPipelineRenderingCreateInfo renderingInfo{}; VkFormat color_format = vkSurfaceFormatKHR.format; renderingInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; renderingInfo.colorAttachmentCount = 1; renderingInfo.pColorAttachmentFormats = &color_format; initInfo.PipelineRenderingCreateInfo = renderingInfo; ImGui_ImplVulkan_Init(&initInfo); pke_window_did_init_imgui = true; // font // { // VkCommandBufferBeginInfo begInfo; // begInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; // begInfo.pNext = nullptr; // begInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; // begInfo.pInheritanceInfo = {}; // vkBeginCommandBuffer(pkvk_present.command_buffers[0], &begInfo); // ImGui_ImplVulkan_CreateFontsTexture(); // ImGui_ImplVulkan_CreateFontsTexture(pkvk_present.command_buffers[0]); // vkEndCommandBuffer(pkvk_present.command_buffers[0]); // VkSubmitInfo submitInfo; // submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; // submitInfo.pNext = nullptr; // submitInfo.waitSemaphoreCount = 0; // submitInfo.pWaitSemaphores = nullptr; // submitInfo.pWaitDstStageMask = nullptr; // submitInfo.commandBufferCount = 1; // submitInfo.pCommandBuffers = pkvk_present.command_buffers; // submitInfo.signalSemaphoreCount = 0; // submitInfo.pSignalSemaphores = nullptr; // vkQueueSubmit(graphicsQueue, 1, &submitInfo, nullptr); // vkQueueWaitIdle(graphicsQueue); // } // ImGui_ImplVulkan_DestroyFontsTexture(); // ImGui_ImplVulkan_DestroyFontUploadObjects(); } void pkvk_transition_image_layout(VkCommandBuffer command_buffer, VkImage image, VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, VkAccessFlags src_access_mask, VkAccessFlags dst_access_mask, VkImageLayout old_layout, VkImageLayout new_layout, VkImageSubresourceRange const &subresource_range) { VkImageMemoryBarrier barrier{}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; barrier.srcAccessMask = src_access_mask; barrier.dstAccessMask = dst_access_mask; barrier.oldLayout = old_layout; barrier.newLayout = new_layout; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.image = image; barrier.subresourceRange = subresource_range; vkCmdPipelineBarrier(command_buffer, src_stage_mask, dst_stage_mask, 0, 0, VK_NULL_HANDLE, 0, VK_NULL_HANDLE, 1, &barrier); } void RecordImGui() { static bool scrollToBottom = true; if (!ImGui::Begin("Console", &pkeSettings.editorSettings.isShowingConsole)) { ImGui::End(); return; } ImVec2 region = ImGui::GetContentRegionAvail(); region.y -= 27; if (ImGui::BeginListBox("##ConsoleHistory", region)) { for (int64_t i = consoleBufferIndex + 1; i < consoleBufferCount; ++i) { ImGui::Text("%s", consoleBuffer[i]); } for (int64_t i = 0; i < consoleBufferIndex; ++i) { ImGui::Text("%s", consoleBuffer[i]); } if (scrollToBottom) ImGui::SetScrollHereY(1); scrollToBottom = false; ImGui::EndListBox(); } ImGui::Separator(); if (ImGui::InputText("##ConsoleInput", consoleBuffer[consoleBufferIndex], consoleLineLength, ImGuiInputTextFlags_EnterReturnsTrue)) { // TODO parse and execute. scrollToBottom = true; consoleBufferIndex = (consoleBufferIndex + 1) % consoleBufferCount; memset(consoleBuffer[consoleBufferIndex], '\0', consoleLineLength); } auto focusedFlags = (ImGuiFocusedFlags_ChildWindows); if (ImGui::IsWindowFocused(focusedFlags) && !ImGui::IsAnyItemFocused() && !ImGui::IsAnyItemHovered() && !ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !ImGui::IsMouseClicked(ImGuiMouseButton_Left, true)) { ImGui::SetKeyboardFocusHere(-1); } ImGui::End(); } void RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { uint32_t i, counter; bool b; vkResetCommandBuffer(commandBuffer, 0); VkCommandBufferBeginInfo beginInfo; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.pNext = nullptr; beginInfo.flags = 0; beginInfo.pInheritanceInfo = nullptr; auto result = vkBeginCommandBuffer(commandBuffer, &beginInfo); if (result != VK_SUCCESS) { throw "failed to begin recording command buffer"; } VkClearColorValue clearColorTransparent = {{0.0f, 0.0f, 0.0f, 0.0f}}; VkClearColorValue clearColorBlack = {{0.0f, 0.0f, 0.0f, 1.0f}}; VkClearValue clearTransparent = { .color = clearColorTransparent }; VkClearValue clearBlack = { .color = clearColorBlack }; VkClearValue clearDepth = { .depthStencil = { 1.f, 0 } }; VkViewport viewport; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = (float)Extent.width; viewport.height = (float)Extent.height; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; VkRect2D scissor; scissor.offset = {0, 0}; scissor.extent = Extent; VkDeviceSize offsets[1] = {0U}; VkImageSubresourceRange range{}; range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; range.baseMipLevel = 0; range.levelCount = VK_REMAINING_MIP_LEVELS; range.baseArrayLayer = 0; range.layerCount = VK_REMAINING_ARRAY_LAYERS; VkImageSubresourceRange depth_range{range}; depth_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; // 0 : color // 1 : color+resolve // 2 : depth/stencil std::array color_attachments; for (long unsigned int i = 0; i < color_attachments.size(); ++i) { color_attachments[i].sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; color_attachments[i].pNext = VK_NULL_HANDLE; color_attachments[i].imageView = VK_NULL_HANDLE; color_attachments[i].imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; color_attachments[i].resolveMode = VK_RESOLVE_MODE_NONE; color_attachments[i].resolveImageView = VK_NULL_HANDLE; color_attachments[i].resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; color_attachments[i].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; color_attachments[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; color_attachments[i].clearValue = clearBlack; } VkRenderingInfo renderingInfo; renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; renderingInfo.pNext = VK_NULL_HANDLE; renderingInfo.flags = 0; renderingInfo.renderArea.offset = {0, 0}; renderingInfo.renderArea.extent = Extent; renderingInfo.layerCount = 1; renderingInfo.viewMask = 0; // 3d pass { // color pkvk_transition_image_layout( commandBuffer, pkvk_3d.render_images[imageIndex], VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range ); // color resolve pkvk_transition_image_layout( commandBuffer, pkvk_3d.images_color_resolve[imageIndex], VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, range ); // depth pkvk_transition_image_layout( commandBuffer, depthImages[imageIndex], VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, VK_ACCESS_NONE, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, depth_range ); color_attachments[1].imageView = pkvk_3d.image_views[imageIndex]; color_attachments[1].clearValue = clearBlack; color_attachments[1].resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT; color_attachments[1].resolveImageView = pkvk_3d.image_views_color_resolve[imageIndex]; color_attachments[1].resolveImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; color_attachments[2].imageView = depthImageViews[imageIndex]; color_attachments[2].imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; color_attachments[2].resolveMode = VK_RESOLVE_MODE_NONE; color_attachments[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; color_attachments[2].storeOp = VK_ATTACHMENT_STORE_OP_STORE; color_attachments[2].clearValue = clearDepth; // skip [0], since we have a resolve on this pass renderingInfo.colorAttachmentCount = 1; renderingInfo.pColorAttachments = &color_attachments.at(1); renderingInfo.pDepthAttachment = &color_attachments.at(2); if (!pkvk_is_depth_only_format(depthFormat)) { renderingInfo.pStencilAttachment = &color_attachments.at(2); } vkCmdBeginRendering(commandBuffer, &renderingInfo); vkCmdSetViewport(commandBuffer, 0, 1, &viewport); vkCmdSetScissor(commandBuffer, 0, 1, &scissor); pk_iter_t gr_binds_iter{}; pk_bkt_arr *ecs_gr_binds_bkt_arr = ECS_GetGrBinds(); b = pk_bkt_arr_iter_begin(ecs_gr_binds_bkt_arr, &gr_binds_iter); CompGrBinds *binder = nullptr; while (b == true) { binder = &*gr_binds_iter; if (binder->grBindsHandle == GrBindsHandle_MAX) { b = pk_bkt_arr_iter_increment(ecs_gr_binds_bkt_arr, &gr_binds_iter); continue; } if (!binder->vkPipelineLayout) { b = pk_bkt_arr_iter_increment(ecs_gr_binds_bkt_arr, &gr_binds_iter); continue; } if (binder->instanceBD.bindingCount < 1) { b = pk_bkt_arr_iter_increment(ecs_gr_binds_bkt_arr, &gr_binds_iter); continue; } vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, binder->graphicsPipeline); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, binder->vkPipelineLayout, 0, 1, &binder->vkDescriptorSets[imageIndex], 0, {}); vkCmdBindVertexBuffers(commandBuffer, 0, 1, &UniformBuffers[imageIndex], offsets); vkCmdBindIndexBuffer(commandBuffer, binder->indexBD.buffer, binder->indexBD.offsets[0], VK_INDEX_TYPE_UINT16); vkCmdBindVertexBuffers(commandBuffer, binder->vertexBD.firstBinding, binder->vertexBD.bindingCount, &binder->vertexBD.buffer, binder->vertexBD.offsets); vkCmdBindVertexBuffers(commandBuffer, binder->normalsBD.firstBinding, binder->normalsBD.bindingCount, &binder->normalsBD.buffer, binder->normalsBD.offsets); vkCmdBindVertexBuffers(commandBuffer, binder->uvBD.firstBinding, binder->uvBD.bindingCount, &binder->uvBD.buffer, binder->uvBD.offsets); vkCmdBindVertexBuffers(commandBuffer, binder->instanceBD.firstBinding, binder->instanceBD.bindingCount, &binder->instanceBD.buffer, binder->instanceBD.offsets); vkCmdDrawIndexed(commandBuffer, binder->indexCount, binder->instanceCounter, 0, 0, 0); if (pkeSettings.isRenderingDebug) { vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkePipelines.pipelines.named.entity_wireframe); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkePipelines.pipe_layouts.named.ubo_txtr, 0, 1, &pkeDebugHitbox.vkDescriptorSets[imageIndex], 0, {}); vkCmdBindVertexBuffers(commandBuffer, binder->physVertBD.firstBinding, 1, &binder->physVertBD.buffer, binder->physVertBD.offsets); vkCmdBindVertexBuffers(commandBuffer, binder->physNormBD.firstBinding, 1, &binder->physNormBD.buffer, binder->physVertBD.offsets); vkCmdBindVertexBuffers(commandBuffer, binder->physUvBD.firstBinding, 1, &binder->physUvBD.buffer, binder->physVertBD.offsets); vkCmdBindIndexBuffer(commandBuffer, binder->physIndxBD.buffer, binder->physIndxBD.offsets[0], VK_INDEX_TYPE_UINT16); vkCmdDrawIndexed(commandBuffer, binder->physIndxBD.bindingCount, binder->instanceCounter, 0, 0, 0); } b = pk_bkt_arr_iter_increment(ecs_gr_binds_bkt_arr, &gr_binds_iter); } if (pkeDebugHitbox.instanceBuffer != VK_NULL_HANDLE) { vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkePipelines.pipelines.named.entity_wireframe); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkePipelines.pipe_layouts.named.ubo_txtr, 0, 1, &pkeDebugHitbox.vkDescriptorSets[imageIndex], 0, {}); vkCmdBindVertexBuffers(commandBuffer, 0, 1, &UniformBuffers[imageIndex], offsets); // TODO don't hardcode firstBinding vkCmdBindVertexBuffers(commandBuffer, 0, 1, &pkeDebugHitbox.vertexBuffer, offsets); vkCmdBindVertexBuffers(commandBuffer, 1, 1, &pkeDebugHitbox.normalsBuffer, offsets); vkCmdBindVertexBuffers(commandBuffer, 2, 1, &pkeDebugHitbox.uvBuffer, offsets); vkCmdBindIndexBuffer(commandBuffer, pkeDebugHitbox.indexBuffer, offsets[0], VK_INDEX_TYPE_UINT16); vkCmdBindVertexBuffers(commandBuffer, 3, 1, &pkeDebugHitbox.instanceBuffer, offsets); vkCmdDrawIndexed(commandBuffer, pkeDebugHitbox.indexCount, pkeDebugHitbox.instanceCount, 0, 0, pkeDebugHitbox.instanceStartingIndex); } vkCmdEndRendering(commandBuffer); } // 2d pass { // color pkvk_transition_image_layout( commandBuffer, pkvk_2d.images[imageIndex], VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range ); // color resolve pkvk_transition_image_layout( commandBuffer, pkvk_2d.images_color_resolve[imageIndex], VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, range ); color_attachments[1].imageView = pkvk_2d.image_views[imageIndex]; color_attachments[1].clearValue = clearTransparent; color_attachments[1].resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT; // TODO verify color_attachments[1].resolveImageView = pkvk_2d.image_views_color_resolve[imageIndex]; color_attachments[1].resolveImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // skip [0], since we have a resolve on this pass renderingInfo.colorAttachmentCount = 1; renderingInfo.pColorAttachments = &color_attachments.at(1); renderingInfo.pDepthAttachment = VK_NULL_HANDLE; renderingInfo.pStencilAttachment = VK_NULL_HANDLE; vkCmdBeginRendering(commandBuffer, &renderingInfo); vkCmdSetViewport(commandBuffer, 0, 1, &viewport); vkCmdSetScissor(commandBuffer, 0, 1, &scissor); // 2d overlay grbinds const pke_ui_graphics_bindings &ui_gr = pke_ui_get_graphics_bindings(); pk_arr_t ui_gr_texture; pke_ui_get_graphics_bindings_texture(&ui_gr_texture); if (ui_gr.instance_counter > 0) { counter = 0; for (i = 0; i < ui_gr_texture.next; ++i) { counter += ui_gr_texture[i].instance_count; } vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkePipelines.pipelines.named.ui_base); vkCmdBindIndexBuffer(commandBuffer, ui_gr.bd_index.buffer, ui_gr.bd_index.offsets[0], VK_INDEX_TYPE_UINT16); vkCmdBindVertexBuffers(commandBuffer, ui_gr.bd_vertex.firstBinding, ui_gr.bd_vertex.bindingCount, &ui_gr.bd_vertex.buffer, ui_gr.bd_vertex.offsets); vkCmdBindVertexBuffers(commandBuffer, ui_gr.bd_uv.firstBinding, ui_gr.bd_uv.bindingCount, &ui_gr.bd_uv.buffer, ui_gr.bd_uv.offsets); vkCmdBindVertexBuffers(commandBuffer, ui_gr.bd_instance.firstBinding, ui_gr.bd_instance.bindingCount, &ui_gr.bd_instance.buffer, ui_gr.bd_instance.offsets); vkCmdDrawIndexed(commandBuffer, ui_gr.index_count, ui_gr.instance_counter - counter, 0, 0, 0); if (counter > 0) { vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkePipelines.pipelines.named.ui_txtr); for (i = 0; i < ui_gr_texture.next; ++i) { vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkePipelines.pipe_layouts.named.txtr, 0, 1, &ui_gr_texture[i].descriptor_sets[imageIndex], 0, {}); vkCmdDrawIndexed(commandBuffer, ui_gr.index_count, ui_gr_texture[i].instance_count, 0, 0, ui_gr_texture[i].instance_offset); } } } vkCmdEndRendering(commandBuffer); } // 2d glyph { // color pkvk_transition_image_layout( commandBuffer, pkvk_glyph.images[imageIndex], VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range ); // color resolve pkvk_transition_image_layout( commandBuffer, pkvk_glyph.images_color_resolve[imageIndex], VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, range ); color_attachments[1].imageView = pkvk_glyph.image_views[imageIndex]; color_attachments[1].clearValue = clearTransparent; color_attachments[1].resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT; // TODO verify color_attachments[1].resolveImageView = pkvk_glyph.image_views_color_resolve[imageIndex]; color_attachments[1].resolveImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // skip [0], since we have a resolve on this pass renderingInfo.colorAttachmentCount = 1; renderingInfo.pColorAttachments = &color_attachments.at(1); renderingInfo.pDepthAttachment = VK_NULL_HANDLE; renderingInfo.pStencilAttachment = VK_NULL_HANDLE; vkCmdBeginRendering(commandBuffer, &renderingInfo); vkCmdSetViewport(commandBuffer, 0, 1, &viewport); vkCmdSetScissor(commandBuffer, 0, 1, &scissor); // 2d - font glyphs pk_iter_t ft{}; pk_bkt_arr *fonts = FontType_GetFonts(); b = pk_bkt_arr_iter_begin(fonts, &ft); while(b) { if (ft->bindings.instance_counter == 0) { b = pk_bkt_arr_iter_increment(fonts, &ft); continue; } vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkePipelines.pipelines.named.font_glyph); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkePipelines.pipe_layouts.named.txtr, 0, 1, &ft->gr.vkDescriptorSet, 0, {}); vkCmdBindIndexBuffer(commandBuffer, ft->bindings.bd_index.buffer, ft->bindings.bd_index.offsets[0], VK_INDEX_TYPE_UINT16); vkCmdBindVertexBuffers(commandBuffer, ft->bindings.bd_vertex.firstBinding, ft->bindings.bd_vertex.bindingCount, &ft->bindings.bd_vertex.buffer, ft->bindings.bd_vertex.offsets); vkCmdBindVertexBuffers(commandBuffer, ft->bindings.bd_uv.firstBinding, ft->bindings.bd_uv.bindingCount, &ft->bindings.bd_uv.buffer, ft->bindings.bd_uv.offsets); vkCmdBindVertexBuffers(commandBuffer, ft->bindings.bd_atlas_size.firstBinding, ft->bindings.bd_atlas_size.bindingCount, &ft->bindings.bd_atlas_size.buffer, ft->bindings.bd_atlas_size.offsets); vkCmdBindVertexBuffers(commandBuffer, ft->bindings.bd_instance.firstBinding, ft->bindings.bd_instance.bindingCount, &ft->bindings.bd_instance.buffer, ft->bindings.bd_instance.offsets); vkCmdDrawIndexed(commandBuffer, ft->bindings.index_count, ft->bindings.instance_counter, 0, 0, 0); b = pk_bkt_arr_iter_increment(fonts, &ft); } vkCmdEndRendering(commandBuffer); } // present pass { // present color pkvk_transition_image_layout( commandBuffer, pkvk_present.images[imageIndex], VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, range ); // 3d color pkvk_transition_image_layout( commandBuffer, pkvk_3d.images_color_resolve[imageIndex], VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range ); // 2d color pkvk_transition_image_layout( commandBuffer, pkvk_2d.images_color_resolve[imageIndex], VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range ); // 2d glyph color pkvk_transition_image_layout( commandBuffer, pkvk_glyph.images_color_resolve[imageIndex], VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range ); color_attachments[0].imageView = pkvk_present.image_views[imageIndex]; color_attachments[0].clearValue = clearBlack; renderingInfo.colorAttachmentCount = 1; renderingInfo.pColorAttachments = &color_attachments.at(0); renderingInfo.pDepthAttachment = VK_NULL_HANDLE; renderingInfo.pStencilAttachment = VK_NULL_HANDLE; vkCmdBeginRendering(commandBuffer, &renderingInfo); vkCmdSetViewport(commandBuffer, 0, 1, &viewport); vkCmdSetScissor(commandBuffer, 0, 1, &scissor); vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkvk_present.pipeline); // reminder that present.vert is a triangle // 3d vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkvk_present.pipeline_layout, 0, 1, &pkvk_present.descriptor_sets[(imageIndex * 3)], 0, nullptr); vkCmdDraw(commandBuffer, 3, 1, 0, 0); // 2d vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkvk_present.pipeline_layout, 0, 1, &pkvk_present.descriptor_sets[(imageIndex * 3) + 1], 0, nullptr); vkCmdDraw(commandBuffer, 3, 1, 0, 0); // 2d glyph vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pkvk_present.pipeline_layout, 0, 1, &pkvk_present.descriptor_sets[(imageIndex * 3) + 2], 0, nullptr); vkCmdDraw(commandBuffer, 3, 1, 0, 0); // ImGui bool any = false; for (uint32_t i = 0; i < LoadedPkePlugins.next; ++i) { if (LoadedPkePlugins[i].OnImGuiRender != nullptr) { any = true; break; } } // TODO move this 'any' check to happen on plugin load and store in global? // Note that drawing imgui requires a plugin is intentional if (any) { ImGui_ImplVulkan_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); RecordImGui(); for (uint32_t i = 0; i < LoadedPkePlugins.next; ++i) { if (LoadedPkePlugins[i].OnImGuiRender != nullptr) { LoadedPkePlugins[i].OnImGuiRender(); } } ImGui::Render(); auto drawData = ImGui::GetDrawData(); const bool isMinimized = drawData->DisplaySize.x <= 0.0f || drawData->DisplaySize.y <= 0.0f; if (isMinimized) return; ImGui_ImplVulkan_RenderDrawData(drawData, commandBuffer); } vkCmdEndRendering(commandBuffer); // color pkvk_transition_image_layout( commandBuffer, pkvk_present.images[imageIndex], VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_NONE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, range ); } vkEndCommandBuffer(commandBuffer); } void DestroySyncObjects() { for (long i = 0; i < prevSwapchainLength; ++i) { if (pkvk_present.semaphores_present_complete[i] != VK_NULL_HANDLE) { vkDestroySemaphore(vkDevice, pkvk_present.semaphores_present_complete[i], vkAllocator); pkvk_present.semaphores_present_complete[i] = VK_NULL_HANDLE; } if (pkvk_present.semaphores_render_finished[i] != VK_NULL_HANDLE) { vkDestroySemaphore(vkDevice, pkvk_present.semaphores_render_finished[i], vkAllocator); pkvk_present.semaphores_render_finished[i] = VK_NULL_HANDLE; } if (pkvk_present.fences_in_flight[i] != VK_NULL_HANDLE) { vkDestroyFence(vkDevice, pkvk_present.fences_in_flight[i], vkAllocator); pkvk_present.fences_in_flight[i] = VK_NULL_HANDLE; } } pk_delete_arr(pkvk_present.semaphores_present_complete, prevSwapchainLength, MemBkt_Vulkan); pkvk_present.semaphores_present_complete = CAFE_BABE(VkSemaphore); pk_delete_arr(pkvk_present.semaphores_render_finished, prevSwapchainLength, MemBkt_Vulkan); pkvk_present.semaphores_render_finished = CAFE_BABE(VkSemaphore); pk_delete_arr(pkvk_present.fences_in_flight, prevSwapchainLength, MemBkt_Vulkan); pkvk_present.fences_in_flight = CAFE_BABE(VkFence); } void DestroyPresentPipeline() { if (pkvk_present.pipeline != VK_NULL_HANDLE) { vkDestroyPipeline(vkDevice, pkvk_present.pipeline, vkAllocator); pkvk_present.pipeline = VK_NULL_HANDLE; } if (pkvk_present.pipeline_layout != VK_NULL_HANDLE) { vkDestroyPipelineLayout(vkDevice, pkvk_present.pipeline_layout, vkAllocator); pkvk_present.pipeline_layout = VK_NULL_HANDLE; } if (pkvk_present.descriptor_sets != nullptr && pkvk_present.descriptor_sets != CAFE_BABE(VkDescriptorSet)) { vkFreeDescriptorSets(vkDevice, pkvk_present.descriptor_pool, prevSwapchainLength * 3, pkvk_present.descriptor_sets); pk_delete_arr(pkvk_present.descriptor_sets, prevSwapchainLength * 3, MemBkt_Vulkan); pkvk_present.descriptor_sets = CAFE_BABE(VkDescriptorSet); } if (pkvk_present.descriptor_pool != VK_NULL_HANDLE) { vkDestroyDescriptorPool(vkDevice, pkvk_present.descriptor_pool, vkAllocator); pkvk_present.descriptor_pool = VK_NULL_HANDLE; } } void DestroySwapchain() { if (depthImageViews != nullptr && depthImageViews != CAFE_BABE(VkImageView)) { for (long i = 0; i < swapchainLength; ++i) { vkDestroyImageView(vkDevice, depthImageViews[i], vkAllocator); vkDestroyImage(vkDevice, depthImages[i], vkAllocator); } pk_delete_arr(depthImageViews, swapchainLength, MemBkt_Vulkan); depthImageViews = CAFE_BABE(VkImageView); pk_delete_arr(depthImages, swapchainLength, MemBkt_Vulkan); depthImages = CAFE_BABE(VkImage); vkFreeMemory(vkDevice, depthImagesMemory, vkAllocator); } if (pkvk_glyph.image_views != nullptr && pkvk_glyph.image_views != CAFE_BABE(VkImageView)) { for (long i = 0; i < swapchainLength; ++i) { vkDestroyImageView(vkDevice, pkvk_glyph.image_views[i], vkAllocator); vkDestroyImage(vkDevice, pkvk_glyph.images[i], vkAllocator); } pk_delete_arr(pkvk_glyph.image_views, swapchainLength, MemBkt_Vulkan); pkvk_glyph.image_views = CAFE_BABE(VkImageView); pk_delete_arr(pkvk_glyph.images, swapchainLength, MemBkt_Vulkan); pkvk_glyph.images = CAFE_BABE(VkImage); vkFreeMemory(vkDevice, pkvk_glyph.images_memory, vkAllocator); } if (pkvk_glyph.image_views_color_resolve != nullptr && pkvk_glyph.image_views_color_resolve != CAFE_BABE(VkImageView)) { for (long i = 0; i < swapchainLength; ++i) { vkDestroyImageView(vkDevice, pkvk_glyph.image_views_color_resolve[i], vkAllocator); vkDestroyImage(vkDevice, pkvk_glyph.images_color_resolve[i], vkAllocator); } pk_delete_arr(pkvk_glyph.image_views_color_resolve, swapchainLength, MemBkt_Vulkan); pkvk_glyph.image_views_color_resolve = CAFE_BABE(VkImageView); pk_delete_arr(pkvk_glyph.images_color_resolve, swapchainLength, MemBkt_Vulkan); pkvk_glyph.images_color_resolve = CAFE_BABE(VkImage); vkFreeMemory(vkDevice, pkvk_glyph.images_memory_color_resolve, vkAllocator); } if (pkvk_2d.image_views != nullptr && pkvk_2d.image_views != CAFE_BABE(VkImageView)) { for (long i = 0; i < swapchainLength; ++i) { vkDestroyImageView(vkDevice, pkvk_2d.image_views[i], vkAllocator); vkDestroyImage(vkDevice, pkvk_2d.images[i], vkAllocator); } pk_delete_arr(pkvk_2d.image_views, swapchainLength, MemBkt_Vulkan); pkvk_2d.image_views = CAFE_BABE(VkImageView); pk_delete_arr(pkvk_2d.images, swapchainLength, MemBkt_Vulkan); pkvk_2d.images = CAFE_BABE(VkImage); vkFreeMemory(vkDevice, pkvk_2d.images_memory, vkAllocator); } if (pkvk_2d.image_views_color_resolve != nullptr && pkvk_2d.image_views_color_resolve != CAFE_BABE(VkImageView)) { for (long i = 0; i < swapchainLength; ++i) { vkDestroyImageView(vkDevice, pkvk_2d.image_views_color_resolve[i], vkAllocator); vkDestroyImage(vkDevice, pkvk_2d.images_color_resolve[i], vkAllocator); } pk_delete_arr(pkvk_2d.image_views_color_resolve, swapchainLength, MemBkt_Vulkan); pkvk_2d.image_views_color_resolve = CAFE_BABE(VkImageView); pk_delete_arr(pkvk_2d.images_color_resolve, swapchainLength, MemBkt_Vulkan); pkvk_2d.images_color_resolve = CAFE_BABE(VkImage); vkFreeMemory(vkDevice, pkvk_2d.images_memory_color_resolve, vkAllocator); } if (pkvk_3d.image_views_color_resolve != nullptr && pkvk_3d.image_views_color_resolve != CAFE_BABE(VkImageView)) { for (long i = 0; i < swapchainLength; ++i) { vkDestroyImageView(vkDevice, pkvk_3d.image_views_color_resolve[i], vkAllocator); vkDestroyImage(vkDevice, pkvk_3d.images_color_resolve[i], vkAllocator); } pk_delete_arr(pkvk_3d.image_views_color_resolve, swapchainLength, MemBkt_Vulkan); pkvk_3d.image_views_color_resolve = CAFE_BABE(VkImageView); vkFreeMemory(vkDevice, pkvk_3d.images_memory_color_resolve, vkAllocator); pk_delete_arr(pkvk_3d.images_color_resolve, swapchainLength, MemBkt_Vulkan); pkvk_3d.images_color_resolve = CAFE_BABE(VkImage); } if (pkvk_3d.image_views != nullptr && pkvk_3d.image_views != CAFE_BABE(VkImageView)) { for (long i = 0; i < swapchainLength; ++i) { vkDestroyImageView(vkDevice, pkvk_3d.image_views[i], vkAllocator); vkDestroyImage(vkDevice, pkvk_3d.render_images[i], vkAllocator); } pk_delete_arr(pkvk_3d.image_views, swapchainLength, MemBkt_Vulkan); pkvk_3d.image_views = CAFE_BABE(VkImageView); pk_delete_arr(pkvk_3d.render_images, swapchainLength, MemBkt_Vulkan); pkvk_3d.render_images = CAFE_BABE(VkImage); vkFreeMemory(vkDevice, pkvk_3d.images_memory, vkAllocator); } if (pkvk_present.image_views!= nullptr && pkvk_present.image_views != CAFE_BABE(VkImageView)) { for (long i = 0; i < swapchainLength; ++i) { vkDestroyImageView(vkDevice, pkvk_present.image_views[i], vkAllocator); } pk_delete_arr(pkvk_present.image_views, swapchainLength, MemBkt_Vulkan); pkvk_present.image_views = CAFE_BABE(VkImageView); pk_delete_arr(pkvk_present.images, swapchainLength, MemBkt_Vulkan); pkvk_present.images = CAFE_BABE(VkImage); // no memory free for present because they are managed by the swapchain } vkDestroySwapchainKHR(vkDevice, vkSwapchainKHR, vkAllocator); } void DestroyUniformBuffers() { for (long i = 0; i < prevSwapchainLength; ++i) { if (UniformBuffers[i] != VK_NULL_HANDLE) { vkDestroyBuffer(vkDevice, UniformBuffers[i], vkAllocator); UniformBuffers[i] = VK_NULL_HANDLE; } } pk_delete_arr(UniformBuffers, prevSwapchainLength, MemBkt_Vulkan); UniformBuffers = CAFE_BABE(VkBuffer); vkFreeMemory(vkDevice, uniformBufferMemory, vkAllocator); uniformBufferMemory = VK_NULL_HANDLE; } void DetermineMonitor() { int monitorCount; GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); assert(monitors != nullptr); // TODO - actually determine monitor monitor = monitors[0]; const auto *videoMode = glfwGetVideoMode(monitor); vidMode.width = videoMode->width; vidMode.height = videoMode->height; vidMode.redBits = videoMode->redBits; vidMode.greenBits = videoMode->greenBits; vidMode.blueBits = videoMode->blueBits; vidMode.refreshRate = videoMode->refreshRate; } void RecreateSwapchain() { pkeSettings.rt.was_framebuffer_resized = true; int width, height = 0; do { glfwGetFramebufferSize(window, &width, &height); glfwWaitEvents(); } while (width == 0 || height == 0); Extent.width = width; Extent.height = height; ActiveCamera->stale = ActiveCamera->stale | PKE_CAMERA_STALE_PERSPECTIVE; DetermineMonitor(); vkWaitForFences(vkDevice, swapchainLength, pkvk_present.fences_in_flight, VK_TRUE, UINT64_MAX); // reminder: prevSwapchainLength set in CreateSwapchain() DestroySwapchain(); CreateSwapchain(); if (prevSwapchainLength != swapchainLength) { DestroySyncObjects(); DestroyPresentPipeline(); DestroyPresentCommandBuffers(); CreatePresentCommandBuffers(); CreatePresentPipeline(); CreateSyncObjects(); } UpdatePresentDescriptorSets(); UpdateCamera(); if (prevSwapchainLength != swapchainLength) { DestroyDebugDescriptors(); DestroyUniformBuffers(); CreateUniformBuffers(); CreateDebugDescriptors(); UpdateDebugDescriptors(); pk_ev_emit(pke_ev_mgr_id_window, pke_ev_id_framebuffer_length_changed, NULL); } shouldRecreateSwapchain = false; prevSwapchainLength = swapchainLength; } void FramebufferResizeCallback(GLFWwindow *window, int width, int height) { (void)window; assert(width > -1); assert(height > -1); if (Extent.width == (uint32_t)width && Extent.height == (uint32_t)height) { return; } shouldRecreateSwapchain = true; } void CreateWindow(PKEWindowProperties wp) { if (vkInstance != nullptr) return; pke_ev_mgr_id_window = pk_ev_create_mgr(); pke_ev_id_framebuffer_length_changed = pk_ev_register_ev(pke_ev_mgr_id_window, NULL); MemBkt_Vulkan = pk_mem_bucket_create("vulkan", PK_MEM_DEFAULT_BUCKET_SIZE, PK_MEMBUCKET_FLAG_NONE); vulkanAllocs.bkt = MemBkt_Vulkan; pk_arr_reserve(&vulkanAllocs, 2048); glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); window = glfwCreateWindow(wp.width, wp.height, "Pikul", nullptr, nullptr); InitVulkan(); glfwSetFramebufferSizeCallback(window, FramebufferResizeCallback); CreateSwapchain(); CreatePresentDescriptorSetLayout(); CreatePresentPipeline(); UpdatePresentDescriptorSets(); UpdateCamera(); CreateCommandPool(); CreatePresentCommandBuffers(); CreateCommandBuffers(); CreateUniformBuffers(); CreateSyncObjects(); CreateGraphicsPipelines(); CreateDebugDescriptors(); UpdateDebugDescriptors(); CreateImGui(); DetermineMonitor(); for (uint64_t i = 0; i < consoleBufferCount; ++i) { memset(consoleBuffer[i], '\0', consoleLineLength); } } void DestroyWindow() { if (vkInstance == nullptr) return; window_tick_late(0.0); if (pke_window_did_init_imgui == true) { ImGui_ImplVulkan_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); } DestroySwapchain(); DestroyDebugDescriptors(); vkDestroyBuffer(vkDevice, pkeDebugHitbox.indexBuffer, vkAllocator); vkDestroyBuffer(vkDevice, pkeDebugHitbox.uvBuffer, vkAllocator); vkDestroyBuffer(vkDevice, pkeDebugHitbox.normalsBuffer, vkAllocator); vkDestroyBuffer(vkDevice, pkeDebugHitbox.vertexBuffer, vkAllocator); vkFreeMemory(vkDevice, pkeDebugHitbox.vertBufferMemory, vkAllocator); vkDestroyImageView(vkDevice, pkeDebugHitbox.vkImageView, vkAllocator); vkDestroyImage(vkDevice, pkeDebugHitbox.vkImage, vkAllocator); vkFreeMemory(vkDevice, pkeDebugHitbox.textureMemory, vkAllocator); if (pkePipelines.pipelines.named.entity_standard != VK_NULL_HANDLE) vkDestroyPipeline(vkDevice, pkePipelines.pipelines.named.entity_standard, vkAllocator); if (pkePipelines.pipelines.named.entity_wireframe != VK_NULL_HANDLE) vkDestroyPipeline(vkDevice, pkePipelines.pipelines.named.entity_wireframe, vkAllocator); if (pkePipelines.pipelines.named.font_glyph != VK_NULL_HANDLE) vkDestroyPipeline(vkDevice, pkePipelines.pipelines.named.font_glyph, vkAllocator); if (pkePipelines.pipelines.named.ui_base != VK_NULL_HANDLE) vkDestroyPipeline(vkDevice, pkePipelines.pipelines.named.ui_base, vkAllocator); if (pkePipelines.pipelines.named.ui_txtr != VK_NULL_HANDLE) vkDestroyPipeline(vkDevice, pkePipelines.pipelines.named.ui_txtr, vkAllocator); if (pkePipelines.pipe_layouts.named.ubo_txtr != VK_NULL_HANDLE) vkDestroyPipelineLayout(vkDevice, pkePipelines.pipe_layouts.named.ubo_txtr, vkAllocator); if (pkePipelines.pipe_layouts.named.txtr != VK_NULL_HANDLE) vkDestroyPipelineLayout(vkDevice, pkePipelines.pipe_layouts.named.txtr, vkAllocator); if (pkePipelines.pipe_layouts.named.base != VK_NULL_HANDLE) vkDestroyPipelineLayout(vkDevice, pkePipelines.pipe_layouts.named.base, vkAllocator); if (pkePipelines.descr_layouts.named.ubo_txtr != VK_NULL_HANDLE) vkDestroyDescriptorSetLayout(vkDevice, pkePipelines.descr_layouts.named.ubo_txtr, vkAllocator); if (pkePipelines.descr_layouts.named.txtr != VK_NULL_HANDLE) vkDestroyDescriptorSetLayout(vkDevice, pkePipelines.descr_layouts.named.txtr, vkAllocator); if (pkePipelines.descr_layouts.named.base != VK_NULL_HANDLE) vkDestroyDescriptorSetLayout(vkDevice, pkePipelines.descr_layouts.named.base, vkAllocator); DestroySyncObjects(); DestroyUniformBuffers(); vkDestroyCommandPool(vkDevice, pkvk_shared.command_pool.graphics, vkAllocator); vkDestroyCommandPool(vkDevice, pkvk_shared.command_pool.transfer, vkAllocator); DestroyPresentPipeline(); vkDestroyDescriptorPool(vkDevice, imGuiDescriptorPool, vkAllocator); vkDestroyDescriptorSetLayout(vkDevice, pkvk_present.descriptor_set_layout, vkAllocator); vkDestroySurfaceKHR(vkInstance, vkSurfaceKHR, vkAllocator); vkDestroySampler(vkDevice, global_sampler, vkAllocator); vkDestroyDevice(vkDevice, vkAllocator); if (VULKAN_DEBUG_REPORT) { auto vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(vkInstance, "vkDestroyDebugReportCallbackEXT"); vkDestroyDebugReportCallbackEXT(vkInstance, vkDebugReport, vkAllocator); } vkDestroyInstance(vkInstance, vkAllocator); pk_delete(vkAllocator, MemBkt_Vulkan); vkAllocator = CAFE_BABE(VkAllocationCallbacks); glfwDestroyWindow(window); glfwTerminate(); if (vulkanAllocs.next > 0) { fprintf(stderr, "VkAllocator has '%u' outstanding allocations!", vulkanAllocs.next); } pk_arr_reset(&vulkanAllocs); pk_arr_reset(&pkvk_actn_queue.destroy.image_view); pk_arr_reset(&pkvk_actn_queue.destroy.image); pk_arr_reset(&pkvk_actn_queue.destroy.buffers); pk_arr_reset(&pkvk_actn_queue.destroy.memory); pk_arr_reset(&pkvk_actn_queue.destroy.descriptor_pool); // TODO there's un-freed vulkan stuff in the bucket // comment this out to see it in the debug printout pk_mem_bucket_destroy(MemBkt_Vulkan); } VkShaderModule UploadShader(AssetHandle handle) { const Asset *asset = AM_Get(handle); if (asset == nullptr || asset->state != PKE_ASSET_LOADING_STATE_LOADED) { fprintf(stderr, "[Window::UploadShader] asset '%.08X %.08X' failed to load.", handle.b, handle.i); return NULL; } #ifndef NDEBUG fprintf(stdout, "Uploading Shader: '%.16s'\n", asset->key); #endif VkShaderModuleCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.pNext = nullptr; createInfo.flags = {}; createInfo.codeSize = asset->size; createInfo.pCode = static_cast(asset->ptr); VkShaderModule vkShaderModule; if (vkCreateShaderModule(vkDevice, &createInfo, vkAllocator, &vkShaderModule) != VK_SUCCESS) { throw "failed to create shader module for asset"; } AM_Release(handle); return vkShaderModule; } void CalculateCombinedMemReqs(uint64_t memReqsCount, VkMemoryRequirements *memReqs, VkMemoryRequirements &combinedMemReqs) { combinedMemReqs.size = 0; combinedMemReqs.alignment = memReqs[0].alignment; combinedMemReqs.memoryTypeBits = memReqs[0].memoryTypeBits; for (uint64_t i = 1; i < memReqsCount; ++i) { combinedMemReqs.memoryTypeBits |= memReqs[i].memoryTypeBits; if (combinedMemReqs.alignment == memReqs[i].alignment) { continue; } VkDeviceSize larger, smaller; if (combinedMemReqs.alignment > memReqs[i].alignment) { larger = combinedMemReqs.alignment; smaller = memReqs[i].alignment; } else { larger = memReqs[i].alignment; smaller = combinedMemReqs.alignment; } if (larger % smaller == 0) { combinedMemReqs.alignment = larger; continue; } int combined = larger * smaller; while ((combined / 2) % 2 == 0 && (combined / 2) % larger == 0) { combined /= 2; } combinedMemReqs.alignment = combined; } for (uint64_t i = 0; i < memReqsCount; ++i) { uint32_t alignmentPadding = memReqs[i].size % combinedMemReqs.alignment; memReqs[i].size += (alignmentPadding == 0 ? 0 : combinedMemReqs.alignment - alignmentPadding); combinedMemReqs.size += memReqs[i].size; } } /* 2025-08-07 - JCB DEBT * This function was a result of buffer synchronization issues. * Specifically, the UI module was trying to reallocate its instance buffer. * Vulkan was emitting an error that the buffer was in use by a command buffer. * Processing these actions after the graphics queue is idle solved that specific issue. * This is a stop-gap measure so I can keep working and is not intended to be permanent. * This might be okay for switching levels (maybe?) * PERF * This will block the main thread until all in-flight renders are no longer using any of the resources needed to render a scene. * Move this to a fire-and-forget background thread that uses a memory barrier and a semaphore specifically for the target resource. */ void window_tick_late(double delta) { (void)delta; uint32_t i; if (pkvk_actn_queue.destroy.image_view.next > 0 || pkvk_actn_queue.destroy.image.next > 0 || pkvk_actn_queue.destroy.buffers.next > 0 || pkvk_actn_queue.destroy.memory.next > 0 || pkvk_actn_queue.destroy.descriptor_pool.next > 0 ) { vkQueueWaitIdle(pkvk_shared.queue.graphics); } for (i = 0; i < pkvk_actn_queue.destroy.image_view.next; ++i) { vkDestroyImageView(vkDevice, pkvk_actn_queue.destroy.image_view[i], vkAllocator); } for (i = 0; i < pkvk_actn_queue.destroy.image.next; ++i) { vkDestroyImage(vkDevice, pkvk_actn_queue.destroy.image[i], vkAllocator); } for (i = 0; i < pkvk_actn_queue.destroy.buffers.next; ++i) { vkDestroyBuffer(vkDevice, pkvk_actn_queue.destroy.buffers[i], vkAllocator); } for (i = 0; i < pkvk_actn_queue.destroy.memory.next; ++i) { vkFreeMemory(vkDevice, pkvk_actn_queue.destroy.memory[i], vkAllocator); } for (i = 0; i < pkvk_actn_queue.destroy.descriptor_pool.next; ++i) { vkDestroyDescriptorPool(vkDevice, pkvk_actn_queue.destroy.descriptor_pool[i], vkAllocator); } pk_arr_clear(&pkvk_actn_queue.destroy.image_view); pk_arr_clear(&pkvk_actn_queue.destroy.image); pk_arr_clear(&pkvk_actn_queue.destroy.buffers); pk_arr_clear(&pkvk_actn_queue.destroy.memory); pk_arr_clear(&pkvk_actn_queue.destroy.descriptor_pool); } void Render() { vkWaitForFences(vkDevice, 1, &pkvk_present.fences_in_flight[pkvk_present.frame_index], VK_TRUE, UINT64_MAX); uint32_t imageIndex; auto result = vkAcquireNextImageKHR(vkDevice, vkSwapchainKHR, UINT64_MAX, pkvk_present.semaphores_present_complete[pkvk_present.semaphore_index], VK_NULL_HANDLE, &imageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR) { RecreateSwapchain(); return; } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { throw "failed to acquire swapchain"; } // update uniform buffer { UpdateCamera(); void *mappedUboMemory; vkMapMemory(vkDevice, uniformBufferMemory, paddedUboBufferSize * imageIndex, sizeof(UniformBufferObject), 0, &mappedUboMemory); memcpy(mappedUboMemory, &UBO, sizeof(UniformBufferObject)); vkUnmapMemory(vkDevice, uniformBufferMemory); } vkResetFences(vkDevice, 1, &pkvk_present.fences_in_flight[pkvk_present.frame_index]); RecordCommandBuffer(pkvk_present.command_buffers[pkvk_present.frame_index], imageIndex); VkSemaphore waitSemaphores[] = {pkvk_present.semaphores_present_complete[pkvk_present.semaphore_index]}; VkSemaphore signalSemaphores[] = {pkvk_present.semaphores_render_finished[imageIndex]}; VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; VkSubmitInfo submitInfo; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &pkvk_present.command_buffers[pkvk_present.frame_index]; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = signalSemaphores; result = vkQueueSubmit(pkvk_shared.queue.graphics, 1, &submitInfo, pkvk_present.fences_in_flight[pkvk_present.frame_index]); if (result != VK_SUCCESS) { throw "failed to submit queue"; } VkSwapchainKHR swapchains[] = {vkSwapchainKHR}; VkPresentInfoKHR presentInfo; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.pNext = nullptr; presentInfo.waitSemaphoreCount = 1; presentInfo.pWaitSemaphores = signalSemaphores; presentInfo.swapchainCount = 1; presentInfo.pSwapchains = swapchains; presentInfo.pImageIndices = &imageIndex; presentInfo.pResults = nullptr; result = vkQueuePresentKHR(presentQueue, &presentInfo); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || shouldRecreateSwapchain) { RecreateSwapchain(); } else if (result != VK_SUCCESS) { throw "failed to present swapchain image"; } pkvk_present.frame_index = (pkvk_present.frame_index + 1) % swapchainLength; pkvk_present.semaphore_index = (pkvk_present.semaphore_index + 1) % swapchainLength; }