#include #include #include "volk.h" #include "vulkan/vk_enum_string_helper.h" #include "SDL3/SDL.h" #include "SDL3/SDL_vulkan.h" #include "directxmath/directxmath.h" #include "new.h" extern "C" { extern uint8_t const _binary_position_normal_texture_vtx_start[]; extern void * const _binary_position_normal_texture_vtx_size; extern uint8_t const _binary_index_idx_start[]; extern void * const _binary_index_idx_size; extern uint8_t const _binary_shader_triangle_vs_spv_start[]; extern void * const _binary_shader_triangle_vs_spv_size; extern uint8_t const _binary_shader_triangle_ps_spv_start[]; extern void * const _binary_shader_triangle_ps_spv_size; extern uint8_t const _binary_sprite_data_start[]; extern void * const _binary_sprite_data_size; } #define vtx_start _binary_position_normal_texture_vtx_start #define vtx_size (size_t)(&_binary_position_normal_texture_vtx_size) #define idx_start _binary_index_idx_start #define idx_size (size_t)(&_binary_index_idx_size) #define vs_start _binary_shader_triangle_vs_spv_start #define vs_size (size_t)(&_binary_shader_triangle_vs_spv_size) #define ps_start _binary_shader_triangle_ps_spv_start #define ps_size (size_t)(&_binary_shader_triangle_ps_spv_size) #define sprite_start _binary_sprite_data_start #define sprite_size (size_t)(&_binary_sprite_data_size) #define SDL_CHECK(f) \ { \ bool result = (f); \ if (result != true) { \ fprintf(stderr, "SDL: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, SDL_GetError()); \ exit(EXIT_FAILURE); \ } \ } #define SDL_CHECK_NONNULL(f) \ { \ void * ptr = (void *)(f); \ if (ptr == nullptr) { \ fprintf(stderr, "SDL: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, SDL_GetError()); \ exit(EXIT_FAILURE); \ } \ } #define VK_CHECK(f) \ { \ VkResult result = (f); \ if (result != VK_SUCCESS) { \ fprintf(stderr, "VK: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, string_VkResult(result)); \ exit(EXIT_FAILURE); \ } \ } #define VK_CHECK_SWAPCHAIN(f) \ { \ VkResult result = (f); \ if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { \ updateSwapchain = true; \ } else if (result != VK_SUCCESS) { \ fprintf(stderr, "VK: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, string_VkResult(result)); \ exit(EXIT_FAILURE); \ } \ } #define ASSERT(expr, msg) \ { \ bool result = (expr); \ if (result != true) { \ fprintf(stderr, "%s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, msg); \ exit(EXIT_FAILURE); \ } \ } #if defined(_MSC_VER) && !defined(__clang__) // MSVC #define UNREACHABLE() __assume(false); #else // GCC, Clang #define UNREACHABLE() __builtin_unreachable(); #endif constexpr uint32_t maxFramesInFlight{ 2 }; VkInstance instance{ VK_NULL_HANDLE }; VkDevice device{ VK_NULL_HANDLE }; VkQueue queue{ VK_NULL_HANDLE }; VkSurfaceKHR surface{ VK_NULL_HANDLE }; VkSwapchainKHR swapchain{ VK_NULL_HANDLE }; uint32_t swapchainImageCount{ 0 }; VkImage * swapchainImages{ nullptr }; VkImageView * swapchainImageViews{ nullptr }; VkImage depthImage{ VK_NULL_HANDLE }; VkImageView depthImageView{ VK_NULL_HANDLE }; VkDeviceMemory depthImageMemory{ VK_NULL_HANDLE }; VkBuffer vertexIndexBuffer{ VK_NULL_HANDLE }; VkDeviceMemory vertexIndexBufferMemory{ VK_NULL_HANDLE }; VkFence fences[maxFramesInFlight]; VkSemaphore presentSemaphores[maxFramesInFlight]; VkSemaphore * renderSemaphores{ nullptr }; VkCommandPool commandPool{ VK_NULL_HANDLE }; VkCommandBuffer commandBuffers[maxFramesInFlight]; VkPipeline pipeline{ VK_NULL_HANDLE }; VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; VkImage textureImage{ VK_NULL_HANDLE }; VkImageView textureImageView{ VK_NULL_HANDLE }; VkDeviceMemory textureImageMemory{ VK_NULL_HANDLE }; VkSampler textureSampler{ VK_NULL_HANDLE }; VkDescriptorPool descriptorPool{ VK_NULL_HANDLE }; VkDescriptorSetLayout textureDescriptorSetLayout{ VK_NULL_HANDLE }; VkDescriptorSet textureDescriptorSet{ VK_NULL_HANDLE }; XMINT2 windowSize{}; struct ShaderData { XMFLOAT4X4 projection; XMFLOAT4X4 view; XMFLOAT4X4 model; //XMFLOAT4X4 model[3]; XMFLOAT4 lightPosition{ 0.0f, -10.0f, 10.0f, 0.0f }; uint32_t selected{ 1 }; } shaderData{}; struct ShaderDataBuffer { VkBuffer buffer{ VK_NULL_HANDLE }; VkDeviceAddress deviceAddress{}; VkDeviceMemory memory; void * mappedData; }; ShaderDataBuffer shaderDataBuffers[maxFramesInFlight]; void print_memoryPropertyFlags(VkMemoryPropertyFlags propertyFlags) { int index = 0; while (propertyFlags) { if (propertyFlags & 1) { if (index != 0) printf("|"); printf(string_VkMemoryPropertyFlagBits((VkMemoryPropertyFlagBits)(1u << index))); } propertyFlags >>= 1; index += 1; }; printf("\n"); } uint32_t findMemoryTypeIndex(VkPhysicalDeviceMemoryProperties const * memoryProperties, uint32_t memoryTypeBits, VkMemoryPropertyFlags propertyFlags) { for (uint32_t i = 0; i < memoryProperties->memoryTypeCount; i++) { if (!(memoryTypeBits & (1u << i))) continue; if (memoryProperties->memoryTypes[i].propertyFlags == propertyFlags) { return i; } } ASSERT(false, "no memory type index matching memoryTypeBits and propertyFlags"); UNREACHABLE(); } XMMATRIX currentProjection() { float fov_angle_y = XMConvertToRadians(45 * 1.0); float aspect_ratio = (float)windowSize.x / (float)windowSize.y; float near_z = 0.1; float far_z = 100.0; XMMATRIX projection = XMMatrixPerspectiveFovRH(fov_angle_y, aspect_ratio, near_z, far_z); return projection; } XMMATRIX currentView() { XMVECTOR eye = XMVectorSet(0, -3, 0, 0); XMVECTOR at = XMVectorSet(0, 0, 0, 0); XMVECTOR up = XMVectorSet(0, 0, 1, 0); XMMATRIX view = XMMatrixLookAtRH(eye, at, up); return view; } float theta = 0; XMMATRIX currentModel() { theta += 0.01; return XMMatrixTranslation(0, 0, -0.5) * XMMatrixRotationX(theta); } void recreateSwapchain(VkSurfaceFormatKHR surfaceFormat, VkFormat depthFormat, VkPhysicalDeviceMemoryProperties2 const & memoryProperties, VkSurfaceCapabilitiesKHR const & surfaceCapabilities) { ////////////////////////////////////////////////////////////////////// // swapchain and images ////////////////////////////////////////////////////////////////////// VkExtent2D imageExtent { .width = surfaceCapabilities.currentExtent.width, .height = surfaceCapabilities.currentExtent.height, }; VkFormat imageFormat{ surfaceFormat.format }; VkColorSpaceKHR imageColorSpace{ surfaceFormat.colorSpace }; VkSwapchainCreateInfoKHR swapchainCreateInfo{ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .surface = surface, .minImageCount = surfaceCapabilities.minImageCount, .imageFormat = imageFormat, .imageColorSpace = imageColorSpace, .imageExtent = imageExtent, .imageArrayLayers = 1, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, .presentMode = VK_PRESENT_MODE_FIFO_KHR }; if (swapchain != VK_NULL_HANDLE) { swapchainCreateInfo.oldSwapchain = swapchain; } VK_CHECK(vkCreateSwapchainKHR(device, &swapchainCreateInfo, nullptr, &swapchain)); if (swapchainImages != nullptr) { free(swapchainImages); } if (swapchainImageViews != nullptr) { for (uint32_t i = 0; i < swapchainImageCount; i++) { vkDestroyImageView(device, swapchainImageViews[i], nullptr); } free(swapchainImageViews); } VK_CHECK(vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, nullptr)); swapchainImages = NewM(swapchainImageCount); VK_CHECK(vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, swapchainImages)); swapchainImageViews = NewM(swapchainImageCount); for (uint32_t i = 0; i < swapchainImageCount; i++) { VkImageViewCreateInfo imageViewCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = swapchainImages[i], .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = imageFormat, .subresourceRange{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = 1, .layerCount = 1 } }; VK_CHECK(vkCreateImageView(device, &imageViewCreateInfo, nullptr, &swapchainImageViews[i])); } if (swapchainCreateInfo.oldSwapchain != nullptr) { vkDestroySwapchainKHR(device, swapchainCreateInfo.oldSwapchain, nullptr); } ////////////////////////////////////////////////////////////////////// // depth ////////////////////////////////////////////////////////////////////// if (depthImage != VK_NULL_HANDLE) { vkDestroyImage(device, depthImage, nullptr); } if (depthImageMemory != VK_NULL_HANDLE) { vkFreeMemory(device, depthImageMemory, nullptr); } if (depthImageView != VK_NULL_HANDLE) { vkDestroyImageView(device, depthImageView, nullptr); } VkImageCreateInfo depthImageCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = depthFormat, .extent{ .width = imageExtent.width, .height = imageExtent.height, .depth = 1, }, .mipLevels = 1, .arrayLayers = 1, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, }; VK_CHECK(vkCreateImage(device, &depthImageCreateInfo, nullptr, &depthImage)); VkMemoryRequirements depthImageMemoryRequirements; vkGetImageMemoryRequirements(device, depthImage, &depthImageMemoryRequirements); VkMemoryPropertyFlags depthImageMemoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT }; uint32_t depthImageMemoryTypeIndex = findMemoryTypeIndex(&memoryProperties.memoryProperties, depthImageMemoryRequirements.memoryTypeBits, depthImageMemoryPropertyFlags); VkMemoryAllocateInfo depthImageAllocateInfo{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = depthImageMemoryRequirements.size, .memoryTypeIndex = depthImageMemoryTypeIndex, }; VK_CHECK(vkAllocateMemory(device, &depthImageAllocateInfo, nullptr, &depthImageMemory)); VK_CHECK(vkBindImageMemory(device, depthImage, depthImageMemory, 0)); VkImageViewCreateInfo depthViewCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = depthImage, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = depthFormat, .subresourceRange{ .aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, .levelCount = 1, .layerCount = 1 } }; VK_CHECK(vkCreateImageView(device, &depthViewCreateInfo, nullptr, &depthImageView)); } int main() { SDL_CHECK(SDL_Init(SDL_INIT_VIDEO)); SDL_CHECK(SDL_Vulkan_LoadLibrary(NULL)); volkInitialize(); VkApplicationInfo appInfo{ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pApplicationName = "Vulkan", .apiVersion = VK_API_VERSION_1_3 }; uint32_t instanceExtensionsCount{ 0 }; char const * const * instanceExtensions{ SDL_Vulkan_GetInstanceExtensions(&instanceExtensionsCount) }; VkInstanceCreateInfo instanceCreateInfo{ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pApplicationInfo = &appInfo, .enabledExtensionCount = instanceExtensionsCount, .ppEnabledExtensionNames = instanceExtensions, }; VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); volkLoadInstance(instance); ////////////////////////////////////////////////////////////////////// // physical device and queue family index ////////////////////////////////////////////////////////////////////// uint32_t physicalDeviceCount{ 0 }; VK_CHECK(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr)); VkPhysicalDevice * physicalDevices = NewM(physicalDeviceCount); VK_CHECK(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices)); uint32_t physicalDeviceIndex{ 0 }; printf("physicalDeviceCount %d\n", physicalDeviceCount); for (uint32_t i = 0; i < physicalDeviceCount; i++) { VkPhysicalDeviceProperties2 properties{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 }; vkGetPhysicalDeviceProperties2(physicalDevices[i], &properties); printf("devices[%d] name: %s%s\n", i, properties.properties.deviceName, (i == physicalDeviceIndex) ? " [selected]" : ""); if (i == physicalDeviceIndex) { printf("limits:\n"); printf(" maxImageDimension1D %u\n", properties.properties.limits.maxImageDimension1D); printf(" maxImageDimension2D %u\n", properties.properties.limits.maxImageDimension2D); printf(" maxMemoryAllocationCount %u\n", properties.properties.limits.maxMemoryAllocationCount); printf(" maxSamplerAllocationCount %u\n", properties.properties.limits.maxSamplerAllocationCount); } } VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex]; free(physicalDevices); uint32_t queueFamilyCount{ 0 }; vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueFamilyCount, nullptr); VkQueueFamilyProperties2 * queueFamilyProperties = NewM(queueFamilyCount); for (uint32_t i = 0; i < queueFamilyCount; i++) { queueFamilyProperties[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2; queueFamilyProperties[i].pNext = nullptr; } vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueFamilyCount, queueFamilyProperties); uint32_t queueFamilyIndex{ ~0u }; for (uint32_t i = 0; i < queueFamilyCount; i++) { VkQueueFlags queueFlags = queueFamilyProperties[i].queueFamilyProperties.queueFlags; if ((queueFlags & VK_QUEUE_GRAPHICS_BIT) && (queueFlags & VK_QUEUE_COMPUTE_BIT)) { queueFamilyIndex = i; break; } } ASSERT(queueFamilyIndex != ~0u, "no queue with VK_QUEUE_GRAPHICS_BIT && VK_QUEUE_COMPUTE_BIT"); free(queueFamilyProperties); SDL_CHECK(SDL_Vulkan_GetPresentationSupport(instance, physicalDevice, queueFamilyIndex)); ////////////////////////////////////////////////////////////////////// // memory ////////////////////////////////////////////////////////////////////// VkPhysicalDeviceMemoryProperties2 memoryProperties{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2, .pNext = nullptr }; vkGetPhysicalDeviceMemoryProperties2(physicalDevice, &memoryProperties); /* for (uint32_t i = 0; i < memoryProperties.memoryProperties.memoryTypeCount; i++) { printf("memoryTypes[%u].propertyFlags: ", i); print_memoryPropertyFlags(memoryProperties.memoryProperties.memoryTypes[i].propertyFlags); printf("memoryTypes[%u].heapIndex: %u\n", i, memoryProperties.memoryProperties.memoryTypes[i].heapIndex); } for (uint32_t i = 0; i < memoryProperties.memoryProperties.memoryHeapCount; i++) { printf("memoryHeaps[%u].size %lu\n", i, memoryProperties.memoryProperties.memoryHeaps[i].size); printf("memoryHeaps[%u].flags %08x\n", i, memoryProperties.memoryProperties.memoryHeaps[i].flags); } */ ////////////////////////////////////////////////////////////////////// // device and queue ////////////////////////////////////////////////////////////////////// const float queuePriorities{ 1.0f }; VkDeviceQueueCreateInfo queueCreateInfo{ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = queueFamilyIndex, .queueCount = 1, .pQueuePriorities = &queuePriorities }; VkPhysicalDeviceVulkan12Features enabledVulkan12Features{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, .descriptorIndexing = true, .shaderSampledImageArrayNonUniformIndexing = true, .descriptorBindingVariableDescriptorCount = true, .runtimeDescriptorArray = true, .bufferDeviceAddress = true }; VkPhysicalDeviceVulkan13Features enabledVulkan13Features{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, .pNext = &enabledVulkan12Features, .synchronization2 = true, .dynamicRendering = true, }; VkPhysicalDeviceFeatures enabledFeatures{ .samplerAnisotropy = VK_TRUE }; constexpr uint32_t enabledExtensionCount = 1; char const * enabledExtensionNames[enabledExtensionCount]{ VK_KHR_SWAPCHAIN_EXTENSION_NAME }; VkDeviceCreateInfo deviceCreateInfo{ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = &enabledVulkan13Features, .queueCreateInfoCount = 1, .pQueueCreateInfos = &queueCreateInfo, .enabledExtensionCount = enabledExtensionCount, .ppEnabledExtensionNames = enabledExtensionNames, .pEnabledFeatures = &enabledFeatures }; VK_CHECK(vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device)); vkGetDeviceQueue(device, queueFamilyIndex, 0, &queue); ////////////////////////////////////////////////////////////////////// // window and surface ////////////////////////////////////////////////////////////////////// SDL_Window * window = SDL_CreateWindow("Vulkan", 1024, 1024, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE); SDL_CHECK_NONNULL(window); SDL_CHECK(SDL_Vulkan_CreateSurface(window, instance, nullptr, &surface)); SDL_CHECK(SDL_GetWindowSize(window, &windowSize.x, &windowSize.y)); VkSurfaceCapabilitiesKHR surfaceCapabilities{}; VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCapabilities)); printf("surfaceCapabilities currentExtent %d %d\n", surfaceCapabilities.currentExtent.width, surfaceCapabilities.currentExtent.height); // surface format uint32_t surfaceFormatCount{ 0 }; VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatCount, nullptr)); VkSurfaceFormatKHR * surfaceFormats = NewM(surfaceFormatCount); VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatCount, surfaceFormats)); uint32_t surfaceFormatIndex{ 0 }; printf("surfaceFormatCount %d\n", surfaceFormatCount); for (uint32_t i = 0; i < surfaceFormatCount; i++) { printf("surfaceFormat[%d] %s %s%s\n", i, string_VkFormat(surfaceFormats[i].format), string_VkColorSpaceKHR(surfaceFormats[i].colorSpace), (i == surfaceFormatIndex) ? " [selected]" : ""); } VkSurfaceFormatKHR surfaceFormat = surfaceFormats[surfaceFormatIndex]; free(surfaceFormats); // depth format VkFormat depthFormat{ VK_FORMAT_UNDEFINED }; constexpr uint32_t depthFormatCount = 2; VkFormat depthFormatList[depthFormatCount]{ VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }; for (uint32_t i = 0; i < depthFormatCount; i++) { VkFormatProperties2 formatProperties{ .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2 }; vkGetPhysicalDeviceFormatProperties2(physicalDevice, depthFormatList[i], &formatProperties); if (formatProperties.formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) { depthFormat = depthFormatList[i]; break; } } ASSERT(depthFormat != VK_FORMAT_UNDEFINED, "no depth format with VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT"); printf("depthFormat: %s\n", string_VkFormat(depthFormat)); recreateSwapchain(surfaceFormat, depthFormat, memoryProperties, surfaceCapabilities); ////////////////////////////////////////////////////////////////////// // mesh ////////////////////////////////////////////////////////////////////// VkDeviceSize vtxBufferSize{ vtx_size }; VkDeviceSize idxBufferSize{ idx_size }; VkBufferCreateInfo vertexIndexBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = vtxBufferSize + idxBufferSize, .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE }; VK_CHECK(vkCreateBuffer(device, &vertexIndexBufferCreateInfo, nullptr, &vertexIndexBuffer)); VkMemoryRequirements vertexIndexBufferMemoryRequirements; vkGetBufferMemoryRequirements(device, vertexIndexBuffer, &vertexIndexBufferMemoryRequirements); VkMemoryPropertyFlags vertexIndexBufferMemoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT }; uint32_t vertexIndexBufferMemoryTypeIndex = findMemoryTypeIndex(&memoryProperties.memoryProperties, vertexIndexBufferMemoryRequirements.memoryTypeBits, vertexIndexBufferMemoryPropertyFlags); VkMemoryAllocateInfo vertexIndexBufferAllocateInfo{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = vertexIndexBufferMemoryRequirements.size, .memoryTypeIndex = vertexIndexBufferMemoryTypeIndex, }; VK_CHECK(vkAllocateMemory(device, &vertexIndexBufferAllocateInfo, nullptr, &vertexIndexBufferMemory)); VK_CHECK(vkBindBufferMemory(device, vertexIndexBuffer, vertexIndexBufferMemory, 0)); void * vertexIndexMappedData; VK_CHECK(vkMapMemory(device, vertexIndexBufferMemory, 0, vertexIndexBufferCreateInfo.size, 0, &vertexIndexMappedData)); memcpy((void *)(((ptrdiff_t)vertexIndexMappedData) + 0), vtx_start, vtx_size); memcpy((void *)(((ptrdiff_t)vertexIndexMappedData) + vtx_size), idx_start, idx_size); vkUnmapMemory(device, vertexIndexBufferMemory); ////////////////////////////////////////////////////////////////////// // shader buffers ////////////////////////////////////////////////////////////////////// for (uint32_t i = 0; i < maxFramesInFlight; i++) { VkBufferCreateInfo shaderBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = (sizeof (ShaderData)), .usage = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE }; VK_CHECK(vkCreateBuffer(device, &shaderBufferCreateInfo, nullptr, &shaderDataBuffers[i].buffer)); VkMemoryRequirements shaderBufferMemoryRequirements; vkGetBufferMemoryRequirements(device, shaderDataBuffers[i].buffer, &shaderBufferMemoryRequirements); VkMemoryPropertyFlags shaderBufferMemoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT }; uint32_t shaderBufferMemoryTypeIndex = findMemoryTypeIndex(&memoryProperties.memoryProperties, shaderBufferMemoryRequirements.memoryTypeBits, shaderBufferMemoryPropertyFlags); VkMemoryAllocateFlagsInfo shaderBufferAllocateFlagsInfo{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO, .flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT, }; VkMemoryAllocateInfo shaderBufferAllocateInfo{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = &shaderBufferAllocateFlagsInfo, .allocationSize = shaderBufferMemoryRequirements.size, .memoryTypeIndex = shaderBufferMemoryTypeIndex, }; VK_CHECK(vkAllocateMemory(device, &shaderBufferAllocateInfo, nullptr, &shaderDataBuffers[i].memory)); VK_CHECK(vkBindBufferMemory(device, shaderDataBuffers[i].buffer, shaderDataBuffers[i].memory, 0)); VkBufferDeviceAddressInfo shaderBufferDeviceAddressInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, .buffer = shaderDataBuffers[i].buffer }; shaderDataBuffers[i].deviceAddress = vkGetBufferDeviceAddress(device, &shaderBufferDeviceAddressInfo); VK_CHECK(vkMapMemory(device, shaderDataBuffers[i].memory, 0, (sizeof (ShaderData)), 0, &shaderDataBuffers[i].mappedData)); } ////////////////////////////////////////////////////////////////////// // synchronization objects ////////////////////////////////////////////////////////////////////// VkSemaphoreCreateInfo semaphoreCreateInfo{ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; VkFenceCreateInfo fenceCreateInfo{ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .flags = VK_FENCE_CREATE_SIGNALED_BIT }; for (uint32_t i = 0; i < maxFramesInFlight; i++) { VK_CHECK(vkCreateFence(device, &fenceCreateInfo, nullptr, &fences[i])); VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &presentSemaphores[i])); } renderSemaphores = NewM(swapchainImageCount); for (uint32_t i = 0; i < swapchainImageCount; i++) { VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &renderSemaphores[i])); } ////////////////////////////////////////////////////////////////////// // command ////////////////////////////////////////////////////////////////////// VkCommandPoolCreateInfo commandPoolCreateInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, .queueFamilyIndex = queueFamilyIndex }; VK_CHECK(vkCreateCommandPool(device, &commandPoolCreateInfo, nullptr, &commandPool)); VkCommandBufferAllocateInfo commandBufferAllocateCreateInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = commandPool, .commandBufferCount = maxFramesInFlight }; VK_CHECK(vkAllocateCommandBuffers(device, &commandBufferAllocateCreateInfo, commandBuffers)); ////////////////////////////////////////////////////////////////////// // texture ////////////////////////////////////////////////////////////////////// VkFormat textureFormat{ VK_FORMAT_B8G8R8A8_SRGB }; VkImageCreateInfo textureImageCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = textureFormat, .extent = {.width = 858, .height = 858, .depth = 1 }, .mipLevels = 1, .arrayLayers = 1, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED }; VK_CHECK(vkCreateImage(device, &textureImageCreateInfo, nullptr, &textureImage)); VkMemoryRequirements textureImageMemoryRequirements; vkGetImageMemoryRequirements(device, textureImage, &textureImageMemoryRequirements); VkMemoryPropertyFlags textureImageMemoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT }; uint32_t textureImageMemoryTypeIndex = findMemoryTypeIndex(&memoryProperties.memoryProperties, textureImageMemoryRequirements.memoryTypeBits, textureImageMemoryPropertyFlags); VkMemoryAllocateInfo textureImageAllocateInfo{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = textureImageMemoryRequirements.size, .memoryTypeIndex = textureImageMemoryTypeIndex, }; VK_CHECK(vkAllocateMemory(device, &textureImageAllocateInfo, nullptr, &textureImageMemory)); VK_CHECK(vkBindImageMemory(device, textureImage, textureImageMemory, 0)); VkImageViewCreateInfo textureViewCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = textureImage, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = textureFormat, .subresourceRange{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = 1, .layerCount = 1 } }; VK_CHECK(vkCreateImageView(device, &textureViewCreateInfo, nullptr, &textureImageView)); // texture transfer: source buffer VkBuffer textureSourceBuffer{}; VkBufferCreateInfo textureSourceBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = sprite_size, .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT }; VK_CHECK(vkCreateBuffer(device, &textureSourceBufferCreateInfo, nullptr, &textureSourceBuffer)); VkMemoryRequirements textureSourceBufferMemoryRequirements; vkGetBufferMemoryRequirements(device, textureSourceBuffer, &textureSourceBufferMemoryRequirements); VkMemoryPropertyFlags textureSourceBufferMemoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT }; uint32_t textureSourceBufferMemoryTypeIndex = findMemoryTypeIndex(&memoryProperties.memoryProperties, textureSourceBufferMemoryRequirements.memoryTypeBits, textureSourceBufferMemoryPropertyFlags); VkMemoryAllocateInfo textureSourceBufferAllocateInfo{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = textureSourceBufferMemoryRequirements.size, .memoryTypeIndex = textureSourceBufferMemoryTypeIndex, }; VkDeviceMemory textureSourceBufferMemory; VK_CHECK(vkAllocateMemory(device, &textureSourceBufferAllocateInfo, nullptr, &textureSourceBufferMemory)); VK_CHECK(vkBindBufferMemory(device, textureSourceBuffer, textureSourceBufferMemory, 0)); void * textureSourceMappedData; VK_CHECK(vkMapMemory(device, textureSourceBufferMemory, 0, textureSourceBufferCreateInfo.size, 0, &textureSourceMappedData)); memcpy((void *)(((ptrdiff_t)textureSourceMappedData) + 0), sprite_start, sprite_size); vkUnmapMemory(device, textureSourceBufferMemory); VkFenceCreateInfo textureFenceCreateInfo{ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; VkFence textureFence{}; VK_CHECK(vkCreateFence(device, &textureFenceCreateInfo, nullptr, &textureFence)); // texture transfer: command buffer VkCommandBuffer textureCommandBuffer{}; VkCommandBufferAllocateInfo textureCommandBufferAllocateInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = commandPool, .commandBufferCount = 1 }; VK_CHECK(vkAllocateCommandBuffers(device, &textureCommandBufferAllocateInfo, &textureCommandBuffer)); VkCommandBufferBeginInfo textureCommandBufferBeginInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT }; VK_CHECK(vkBeginCommandBuffer(textureCommandBuffer, &textureCommandBufferBeginInfo)); VkImageMemoryBarrier2 barrierTextureImage{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .srcStageMask = VK_PIPELINE_STAGE_2_NONE, .srcAccessMask = VK_ACCESS_2_NONE, .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .image = textureImage, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = 1, .layerCount = 1 } }; VkDependencyInfo barrierTextureImageDependencyInfo{ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &barrierTextureImage }; vkCmdPipelineBarrier2(textureCommandBuffer, &barrierTextureImageDependencyInfo); VkBufferImageCopy copyRegion{ .bufferOffset = 0, .imageSubresource{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .layerCount = 1 }, .imageExtent{ .width = 858, .height = 858, .depth = 1 }, }; vkCmdCopyBufferToImage(textureCommandBuffer, textureSourceBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region); VkImageMemoryBarrier2 barrierTextureRead{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL, .image = textureImage, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = 1, .layerCount = 1 } }; VkDependencyInfo barrierTextureReadDependencyInfo{ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &barrierTextureRead }; vkCmdPipelineBarrier2(textureCommandBuffer, &barrierTextureReadDependencyInfo); VK_CHECK(vkEndCommandBuffer(textureCommandBuffer)); VkSubmitInfo textureSubmitInfo{ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = 1, .pCommandBuffers = &textureCommandBuffer }; VK_CHECK(vkQueueSubmit(queue, 1, &textureSubmitInfo, textureFence)); VK_CHECK(vkWaitForFences(device, 1, &textureFence, VK_TRUE, UINT64_MAX)); vkDestroyFence(device, textureFence, nullptr); vkDestroyBuffer(device, textureSourceBuffer, nullptr); vkFreeMemory(device, textureSourceBufferMemory, nullptr); // texture sampler VkSamplerCreateInfo samplerCreateInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .magFilter = VK_FILTER_LINEAR, .minFilter = VK_FILTER_LINEAR, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, .anisotropyEnable = VK_FALSE, .maxLod = 1, }; VK_CHECK(vkCreateSampler(device, &samplerCreateInfo, nullptr, &textureSampler)); VkDescriptorImageInfo textureDescriptorImageInfo = { .sampler = textureSampler, .imageView = textureImageView, .imageLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL }; ////////////////////////////////////////////////////////////////////// // descriptors ////////////////////////////////////////////////////////////////////// VkDescriptorBindingFlags descriptorBindingFlags{ VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT }; VkDescriptorSetLayoutBindingFlagsCreateInfo descriptorSetLayoutBindingFlagsCreateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, .bindingCount = 1, .pBindingFlags = &descriptorBindingFlags }; VkDescriptorSetLayoutBinding textureDescriptorSetLayoutBinding{ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT }; VkDescriptorSetLayoutCreateInfo textureDescriptorSetLayoutCreateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .pNext = &descriptorSetLayoutBindingFlagsCreateInfo, .bindingCount = 1, .pBindings = &textureDescriptorSetLayoutBinding }; VK_CHECK(vkCreateDescriptorSetLayout(device, &textureDescriptorSetLayoutCreateInfo, nullptr, &textureDescriptorSetLayout)); // pool VkDescriptorPoolSize poolSize{ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1, }; VkDescriptorPoolCreateInfo descriptorPoolCreateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .maxSets = 1, .poolSizeCount = 1, .pPoolSizes = &poolSize }; VK_CHECK(vkCreateDescriptorPool(device, &descriptorPoolCreateInfo, nullptr, &descriptorPool)); // uint32_t variableDescriptorCount{ 1 }; VkDescriptorSetVariableDescriptorCountAllocateInfo descriptorSetVariableDescriptorCountAllocateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT, .descriptorSetCount = 1, .pDescriptorCounts = &variableDescriptorCount }; VkDescriptorSetAllocateInfo descriptorSetAllocateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .pNext = &descriptorSetVariableDescriptorCountAllocateInfo, .descriptorPool = descriptorPool, .descriptorSetCount = 1, .pSetLayouts = &textureDescriptorSetLayout }; VK_CHECK(vkAllocateDescriptorSets(device, &descriptorSetAllocateInfo, &textureDescriptorSet)); // VkWriteDescriptorSet writeDescriptorSet{ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = textureDescriptorSet, .dstBinding = 0, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .pImageInfo = &textureDescriptorImageInfo }; vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr); ////////////////////////////////////////////////////////////////////// // shaders ////////////////////////////////////////////////////////////////////// VkShaderModuleCreateInfo vertexShaderModuleCreateInfo{ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = vs_size, .pCode = (uint32_t *)vs_start }; VkShaderModule vertexShaderModule{}; VK_CHECK(vkCreateShaderModule(device, &vertexShaderModuleCreateInfo, nullptr, &vertexShaderModule)); VkShaderModuleCreateInfo pixelShaderModuleCreateInfo{ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = ps_size, .pCode = (uint32_t *)ps_start }; VkShaderModule pixelShaderModule{}; VK_CHECK(vkCreateShaderModule(device, &pixelShaderModuleCreateInfo, nullptr, &pixelShaderModule)); ////////////////////////////////////////////////////////////////////// // pipeline ////////////////////////////////////////////////////////////////////// VkPushConstantRange pushConstantRange{ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, .size = (sizeof (VkDeviceAddress)) }; VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = 1, .pSetLayouts = &textureDescriptorSetLayout, .pushConstantRangeCount = 1, .pPushConstantRanges = &pushConstantRange }; VK_CHECK(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); VkVertexInputBindingDescription vertexBindingDescriptions[1]{ { .binding = 0, .stride = (3 * (sizeof (float)) * 3), .inputRate = VK_VERTEX_INPUT_RATE_VERTEX } }; VkVertexInputAttributeDescription vertexAttributeDescriptions[3]{ { .location = 0, .binding = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = 3 * 4 * 0 }, { .location = 1, .binding = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = 3 * 4 * 1 }, { .location = 2, .binding = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = 3 * 4 * 2 }, }; VkPipelineVertexInputStateCreateInfo vertexInputState{ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .vertexBindingDescriptionCount = 1, .pVertexBindingDescriptions = vertexBindingDescriptions, .vertexAttributeDescriptionCount = 3, .pVertexAttributeDescriptions = vertexAttributeDescriptions, }; VkPipelineInputAssemblyStateCreateInfo inputAssemblyState{ .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST }; VkPipelineShaderStageCreateInfo shaderStages[2]{ { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = vertexShaderModule, .pName = "VSMain" }, { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = pixelShaderModule, .pName = "PSMain" } }; VkPipelineViewportStateCreateInfo viewportState{ .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .viewportCount = 1, .scissorCount = 1 }; VkDynamicState dynamicStates[2]{ VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamicState{ .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .dynamicStateCount = 2, .pDynamicStates = dynamicStates }; VkPipelineDepthStencilStateCreateInfo depthStencilState{ .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .depthTestEnable = VK_TRUE, .depthWriteEnable = VK_TRUE, .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL }; VkPipelineRenderingCreateInfo renderingCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, .colorAttachmentCount = 1, .pColorAttachmentFormats = &surfaceFormat.format, .depthAttachmentFormat = depthFormat }; VkPipelineColorBlendAttachmentState blendAttachment{ .colorWriteMask = 0xF }; VkPipelineColorBlendStateCreateInfo colorBlendState{ .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .attachmentCount = 1, .pAttachments = &blendAttachment }; VkPipelineRasterizationStateCreateInfo rasterizationState{ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .lineWidth = 1.0f }; VkPipelineMultisampleStateCreateInfo multisampleState{ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT }; VkGraphicsPipelineCreateInfo pipelineCreateInfo{ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = &renderingCreateInfo, .stageCount = 2, .pStages = shaderStages, .pVertexInputState = &vertexInputState, .pInputAssemblyState = &inputAssemblyState, .pViewportState = &viewportState, .pRasterizationState = &rasterizationState, .pMultisampleState = &multisampleState, .pDepthStencilState = &depthStencilState, .pColorBlendState = &colorBlendState, .pDynamicState = &dynamicState, .layout = pipelineLayout }; VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, nullptr, &pipeline)); ////////////////////////////////////////////////////////////////////// // loop ////////////////////////////////////////////////////////////////////// bool updateSwapchain{ false }; uint32_t frameIndex{ 0 }; uint32_t imageIndex{ 0 }; bool quit{ false }; while (quit == false) { SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_EVENT_QUIT) { quit = true; } if (event.type == SDL_EVENT_WINDOW_RESIZED) { SDL_CHECK(SDL_GetWindowSize(window, &windowSize.x, &windowSize.y)); } } // wait for fence VK_CHECK(vkWaitForFences(device, 1, &fences[frameIndex], true, UINT64_MAX)); VK_CHECK(vkResetFences(device, 1, &fences[frameIndex])); // acquire next image VK_CHECK_SWAPCHAIN(vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, presentSemaphores[frameIndex], VK_NULL_HANDLE, &imageIndex)); // shader data XMStoreFloat4x4(&shaderData.projection, currentProjection()); XMStoreFloat4x4(&shaderData.view, currentView()); XMStoreFloat4x4(&shaderData.model, currentModel()); memcpy(shaderDataBuffers[frameIndex].mappedData, &shaderData, (sizeof (ShaderData))); // command buffer VkCommandBuffer& commandBuffer = commandBuffers[frameIndex]; VK_CHECK(vkResetCommandBuffer(commandBuffer, 0)); VkCommandBufferBeginInfo commandBufferBeginInfo { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT }; VK_CHECK(vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo)); VkImageMemoryBarrier2 outputBarriers[2]{ VkImageMemoryBarrier2{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, .srcAccessMask = 0, .dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL, .image = swapchainImages[imageIndex], .subresourceRange{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = 1, .layerCount = 1 } }, VkImageMemoryBarrier2{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .srcStageMask = VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, .srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, .dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT, .dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL, .image = depthImage, .subresourceRange{ .aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, .levelCount = 1, .layerCount = 1 } } }; VkDependencyInfo barrierDependencyInfo{ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, .imageMemoryBarrierCount = 2, .pImageMemoryBarriers = outputBarriers }; vkCmdPipelineBarrier2(commandBuffer, &barrierDependencyInfo); VkRenderingAttachmentInfo colorRenderingAttachmentInfo{ .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, .imageView = swapchainImageViews[imageIndex], .imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .clearValue{ .color{ 0.0f, 0.0f, 0.2f, 1.0f } } }; VkRenderingAttachmentInfo depthRenderingAttachmentInfo{ .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, .imageView = depthImageView, .imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .clearValue{ .depthStencil{ 1.0f, 0 } } }; VkRenderingInfo renderingInfo{ .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, .renderArea{ .extent{ .width = surfaceCapabilities.currentExtent.width, .height = surfaceCapabilities.currentExtent.height } }, .layerCount = 1, .colorAttachmentCount = 1, .pColorAttachments = &colorRenderingAttachmentInfo, .pDepthAttachment = &depthRenderingAttachmentInfo }; vkCmdBeginRendering(commandBuffer, &renderingInfo); VkViewport viewport{ .width = static_cast(windowSize.x), .height = static_cast(windowSize.y), .minDepth = 0.0f, .maxDepth = 1.0f }; vkCmdSetViewport(commandBuffer, 0, 1, &viewport); VkRect2D scissor{ .extent{ .width = (uint32_t)windowSize.x, .height = (uint32_t)windowSize.y } }; vkCmdSetScissor(commandBuffer, 0, 1, &scissor); vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); VkDeviceSize vertexOffset{ 0 }; vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &textureDescriptorSet, 0, nullptr); vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexIndexBuffer, &vertexOffset); VkDeviceSize indexOffset{ vtxBufferSize }; vkCmdBindIndexBuffer(commandBuffer, vertexIndexBuffer, indexOffset, VK_INDEX_TYPE_UINT32); vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, (sizeof (VkDeviceAddress)), &shaderDataBuffers[frameIndex].deviceAddress); VkDeviceSize indexCount{ 9216 }; vkCmdDrawIndexed(commandBuffer, indexCount, 3, 0, 0, 0); vkCmdEndRendering(commandBuffer); VkImageMemoryBarrier2 barrierPresent{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, .dstAccessMask = 0, .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, .image = swapchainImages[imageIndex], .subresourceRange{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = 1, .layerCount = 1 } }; VkDependencyInfo barrierPresentDependencyInfo{ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &barrierPresent }; vkCmdPipelineBarrier2(commandBuffer, &barrierPresentDependencyInfo); VK_CHECK(vkEndCommandBuffer(commandBuffer)); // submit to graphics queue VkPipelineStageFlags waitStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo submitInfo{ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = 1, .pWaitSemaphores = &presentSemaphores[frameIndex], .pWaitDstStageMask = &waitStages, .commandBufferCount = 1, .pCommandBuffers = &commandBuffer, .signalSemaphoreCount = 1, .pSignalSemaphores = &renderSemaphores[imageIndex], }; VK_CHECK(vkQueueSubmit(queue, 1, &submitInfo, fences[frameIndex])); frameIndex = (frameIndex + 1) % maxFramesInFlight; // present VkPresentInfoKHR presentInfo{ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .waitSemaphoreCount = 1, .pWaitSemaphores = &renderSemaphores[imageIndex], .swapchainCount = 1, .pSwapchains = &swapchain, .pImageIndices = &imageIndex }; VK_CHECK_SWAPCHAIN(vkQueuePresentKHR(queue, &presentInfo)); if (updateSwapchain) { ////////////////////////////////////////////////////////////////////// // recreate swapchain ////////////////////////////////////////////////////////////////////// updateSwapchain = false; VK_CHECK(vkDeviceWaitIdle(device)); VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCapabilities)); recreateSwapchain(surfaceFormat, depthFormat, memoryProperties, surfaceCapabilities); } } VK_CHECK(vkDeviceWaitIdle(device)); for (uint32_t i = 0; i < maxFramesInFlight; i++) { vkDestroyFence(device, fences[i], nullptr); vkDestroySemaphore(device, presentSemaphores[i], nullptr); vkUnmapMemory(device, shaderDataBuffers[i].memory); vkDestroyBuffer(device, shaderDataBuffers[i].buffer, nullptr); vkFreeMemory(device, shaderDataBuffers[i].memory, nullptr); } for (uint32_t i = 0; i < swapchainImageCount; i++) { vkDestroySemaphore(device, renderSemaphores[i], nullptr); vkDestroyImageView(device, swapchainImageViews[i], nullptr); } vkDestroyImage(device, depthImage, nullptr); vkFreeMemory(device, depthImageMemory, nullptr); vkDestroyImageView(device, depthImageView, nullptr); vkDestroyBuffer(device, vertexIndexBuffer, nullptr); vkFreeMemory(device, vertexIndexBufferMemory, nullptr); vkDestroyImageView(device, textureImageView, nullptr); vkDestroySampler(device, textureSampler, nullptr); vkDestroyImage(device, textureImage, nullptr); vkFreeMemory(device, textureImageMemory, nullptr); vkDestroyDescriptorSetLayout(device, textureDescriptorSetLayout, nullptr); vkDestroyDescriptorPool(device, descriptorPool, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr); vkDestroyPipeline(device, pipeline, nullptr); vkDestroySwapchainKHR(device, swapchain, nullptr); vkDestroySurfaceKHR(instance, surface, nullptr); vkDestroyCommandPool(device, commandPool, nullptr); vkDestroyShaderModule(device, vertexShaderModule, nullptr); vkDestroyShaderModule(device, pixelShaderModule, nullptr); SDL_DestroyWindow(window); SDL_QuitSubSystem(SDL_INIT_VIDEO); SDL_Quit(); vkDestroyDevice(device, nullptr); vkDestroyInstance(instance, nullptr); }