#include #include #include "volk/volk.h" #include "vulkan/vk_enum_string_helper.h" #include "SDL3/SDL.h" #include "SDL3/SDL_vulkan.h" #include "directxmath/directxmath.h" #include "check.h" #include "new.h" #include "file.h" #include "dds_validate.h" #include "vulkan_helper.h" #include "shader_data.h" #include "collada/scene.h" #include "collada/scene/vulkan.h" #include "scenes/shadow_test/shadow_test.h" template inline static constexpr T min(T a, T b) { return (a < b) ? a : b; } template inline static constexpr T max(T a, T b) { return (a > b) ? a : b; } template inline static constexpr T clamp(T n, T minVal, T maxVal) { return min(max(n, minVal), maxVal); } 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 }; VkDeviceSize vertexBufferSize{ 0 }; VkDeviceSize indexBufferSize{ 0 }; VkFence fences[maxFramesInFlight]; VkSemaphore presentSemaphores[maxFramesInFlight]; VkSemaphore * renderSemaphores{ nullptr }; VkCommandPool commandPool{ VK_NULL_HANDLE }; VkCommandBuffer commandBuffers[maxFramesInFlight]; enum { MAIN_PIPELINE = 0, OUTLINE_PIPELINE = 1, }; VkPipeline pipelines[2]{ VK_NULL_HANDLE, VK_NULL_HANDLE }; VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; VkImage textureImage{ VK_NULL_HANDLE }; VkImageView textureImageView{ VK_NULL_HANDLE }; VkDeviceMemory textureImageMemory{ VK_NULL_HANDLE }; VkSampler textureSamplers[3]{ VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE }; VkDescriptorPool descriptorPool{ VK_NULL_HANDLE }; VkDescriptorSetLayout uniformBufferDescriptorSetLayout{ VK_NULL_HANDLE }; VkDescriptorSet uniformBufferDescriptorSets[maxFramesInFlight]; VkDescriptorSetLayout textureDescriptorSetLayout{ VK_NULL_HANDLE }; VkDescriptorSet textureDescriptorSet{ VK_NULL_HANDLE }; XMINT2 windowSize{}; ShaderData shaderData{}; ShaderDataDevice shaderDataDevice{}; 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"); } 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 = 1000.0; XMMATRIX projection = XMMatrixPerspectiveFovLH(fov_angle_y, aspect_ratio, near_z, far_z); return projection; } XMMATRIX currentView(collada::instance_types::node const & camera_node, collada::instance_types::node const & camera_target_node) { XMVECTOR eye = XMVector3Transform(XMVectorZero(), camera_node.world); XMVECTOR at = XMVector3Transform(XMVectorZero(), camera_target_node.world); XMVECTOR up = XMVectorSet(0, 0, 1, 0); XMMATRIX view = XMMatrixLookAtLH(eye, at, up); return view; } float theta = 0; XMMATRIX currentModel() { theta += 0.01; return XMMatrixTranslation(0, 0, 0.0) * XMMatrixRotationX(theta) * XMMatrixRotationZ(XM_PI * 0.5f); } void recreateSwapchain(VkSurfaceFormatKHR surfaceFormat, VkFormat depthFormat, VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, 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); } if (renderSemaphores != nullptr) { for (uint32_t i = 0; i < swapchainImageCount; i++) { vkDestroySemaphore(device, renderSemaphores[i], nullptr); } free(renderSemaphores); } 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); } ////////////////////////////////////////////////////////////////////// // render semaphores ////////////////////////////////////////////////////////////////////// VkSemaphoreCreateInfo semaphoreCreateInfo{ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; renderSemaphores = NewM(swapchainImageCount); for (uint32_t i = 0; i < swapchainImageCount; i++) { VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &renderSemaphores[i])); } ////////////////////////////////////////////////////////////////////// // 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 }; VkMemoryAllocateFlags depthImageMemoryAllocateFlags{ }; allocateFromMemoryRequirements(device, physicalDeviceMemoryProperties, depthImageMemoryRequirements, depthImageMemoryPropertyFlags, depthImageMemoryAllocateFlags, 1, &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 | VK_IMAGE_ASPECT_STENCIL_BIT, .levelCount = 1, .layerCount = 1 } }; VK_CHECK(vkCreateImageView(device, &depthViewCreateInfo, nullptr, &depthImageView)); } inline static int positive_modulo(int i, unsigned int n) { return (i % n + n) % n; } inline static double getTime(int64_t start_time) { int64_t current_time; SDL_GetCurrentTime(¤t_time); int64_t time = current_time - start_time; return (double)(time / 1000) * 0.000001; } 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); VkPhysicalDeviceProperties physicalDeviceProperties; 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); printf(" nonCoherentAtomSize %lu\n", properties.properties.limits.nonCoherentAtomSize); printf(" minUniformBufferOffsetAlignment %lu\n", properties.properties.limits.minUniformBufferOffsetAlignment); printf(" maxSamplerLodBias %f\n", properties.properties.limits.maxSamplerLodBias); printf(" maxSamplerAnisotropy %f\n", properties.properties.limits.maxSamplerAnisotropy); physicalDeviceProperties = properties.properties; } } 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 physicalDeviceMemoryProperties2{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2, .pNext = nullptr }; vkGetPhysicalDeviceMemoryProperties2(physicalDevice, &physicalDeviceMemoryProperties2); VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties = physicalDeviceMemoryProperties2.memoryProperties; if constexpr (true) { for (uint32_t i = 0; i < physicalDeviceMemoryProperties.memoryTypeCount; i++) { printf("memoryTypes[%u].propertyFlags: ", i); print_memoryPropertyFlags(physicalDeviceMemoryProperties.memoryTypes[i].propertyFlags); printf("memoryTypes[%u].heapIndex: %u\n", i, physicalDeviceMemoryProperties.memoryTypes[i].heapIndex); } for (uint32_t i = 0; i < physicalDeviceMemoryProperties.memoryHeapCount; i++) { printf("memoryHeaps[%u].size %lu\n", i, physicalDeviceMemoryProperties.memoryHeaps[i].size); printf("memoryHeaps[%u].flags %08x\n", i, physicalDeviceMemoryProperties.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, }; 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, physicalDeviceMemoryProperties, surfaceCapabilities); ////////////////////////////////////////////////////////////////////// // mesh ////////////////////////////////////////////////////////////////////// { uint32_t vertexSize; void const * vertexStart = file::open("checker.vtx", &vertexSize); uint32_t indexSize; void const * indexStart = file::open("checker.idx", &indexSize); vertexBufferSize = vertexSize; indexBufferSize = indexSize; VkDeviceSize bufferSize{ vertexSize + indexSize }; VkBufferCreateInfo vertexIndexBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = bufferSize, .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 memoryRequirements; vkGetBufferMemoryRequirements(device, vertexIndexBuffer, &memoryRequirements); VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT }; VkMemoryAllocateFlags memoryAllocateFlags{}; allocateFromMemoryRequirements(device, physicalDeviceMemoryProperties, memoryRequirements, memoryPropertyFlags, memoryAllocateFlags, 1, &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), vertexStart, vertexSize); memcpy((void *)(((ptrdiff_t)vertexIndexMappedData) + vertexSize), indexStart, indexSize); VkMappedMemoryRange mappedMemoryRange{ .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, .memory = vertexIndexBufferMemory, .offset = 0, .size = VK_WHOLE_SIZE, }; vkFlushMappedMemoryRanges(device, 1, &mappedMemoryRange); vkUnmapMemory(device, vertexIndexBufferMemory); } ////////////////////////////////////////////////////////////////////// // shader buffers ////////////////////////////////////////////////////////////////////// { for (uint32_t i = 0; i < maxFramesInFlight; i++) { VkBufferCreateInfo bufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = (sizeof (ShaderData)), .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE }; VK_CHECK(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &shaderDataDevice.frame[i].buffer)); } VkMemoryRequirements memoryRequirements; vkGetBufferMemoryRequirements(device, shaderDataDevice.frame[0].buffer, &memoryRequirements); VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT }; VkMemoryAllocateFlags memoryAllocateFlags{ }; shaderDataDevice.stride = allocateFromMemoryRequirements(device, physicalDeviceMemoryProperties, memoryRequirements, memoryPropertyFlags, memoryAllocateFlags, maxFramesInFlight, &shaderDataDevice.memory); VkDeviceSize offset{ 0 }; VkDeviceSize size{ VK_WHOLE_SIZE }; VkMemoryMapFlags flags{ 0 }; VK_CHECK(vkMapMemory(device, shaderDataDevice.memory, offset, size, flags, &shaderDataDevice.mappedData)); for (uint32_t i = 0; i < maxFramesInFlight; i++) { VkDeviceSize offset{ shaderDataDevice.stride * i }; VK_CHECK(vkBindBufferMemory(device, shaderDataDevice.frame[i].buffer, shaderDataDevice.memory, offset)); } } ////////////////////////////////////////////////////////////////////// // 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])); } ////////////////////////////////////////////////////////////////////// // 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 ////////////////////////////////////////////////////////////////////// uint32_t checkerSize; void const * checkerStart = file::open("checker.dds", &checkerSize); void * checkerData; uint32_t * mipOffsets; DDS_FILE const * ddsFile = dds_validate(checkerStart, checkerSize, &mipOffsets, &checkerData); uint32_t checkerDataSize = checkerSize - (sizeof (DDS_FILE)); VkFormat textureFormat{ VK_FORMAT_B8G8R8A8_SRGB }; VkImageCreateInfo textureImageCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = textureFormat, .extent = { .width = ddsFile->header.dwWidth, .height = ddsFile->header.dwHeight, .depth = 1 }, .mipLevels = ddsFile->header.dwMipMapCount, .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 }; VkMemoryAllocateFlags textureImageMemoryAllocateFlags{ }; allocateFromMemoryRequirements(device, physicalDeviceMemoryProperties, textureImageMemoryRequirements, textureImageMemoryPropertyFlags, textureImageMemoryAllocateFlags, 1, &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 = ddsFile->header.dwMipMapCount, .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 = checkerDataSize, .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_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT }; VkMemoryAllocateFlags textureSourceBufferMemoryAllocateFlags{ }; VkDeviceMemory textureSourceBufferMemory; allocateFromMemoryRequirements(device, physicalDeviceMemoryProperties, textureSourceBufferMemoryRequirements, textureSourceBufferMemoryPropertyFlags, textureSourceBufferMemoryAllocateFlags, 1, &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), checkerData, checkerDataSize); 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 = ddsFile->header.dwMipMapCount, .layerCount = 1 } }; VkDependencyInfo barrierTextureImageDependencyInfo{ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &barrierTextureImage }; vkCmdPipelineBarrier2(textureCommandBuffer, &barrierTextureImageDependencyInfo); VkBufferImageCopy * copyRegions = NewM(ddsFile->header.dwMipMapCount); for (uint32_t level = 0; level < ddsFile->header.dwMipMapCount; level++) { copyRegions[level] = { .bufferOffset = mipOffsets[level], .imageSubresource{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = level, .layerCount = 1 }, .imageExtent{ .width = max(1u, ddsFile->header.dwWidth >> level), .height = max(1u, ddsFile->header.dwHeight >> level), .depth = 1 }, }; } vkCmdCopyBufferToImage(textureCommandBuffer, textureSourceBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, ddsFile->header.dwMipMapCount, copyRegions); free(mipOffsets); free(copyRegions); 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 = ddsFile->header.dwMipMapCount, .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 samplerCreateInfo0{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .magFilter = VK_FILTER_LINEAR, .minFilter = VK_FILTER_LINEAR, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .anisotropyEnable = VK_FALSE, .maxLod = 0.0, // (float)ddsFile->header.dwMipMapCount }; VK_CHECK(vkCreateSampler(device, &samplerCreateInfo0, nullptr, &textureSamplers[0])); VkSamplerCreateInfo samplerCreateInfo1{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .magFilter = VK_FILTER_LINEAR, .minFilter = VK_FILTER_LINEAR, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .anisotropyEnable = VK_FALSE, .maxLod = VK_LOD_CLAMP_NONE, // (float)ddsFile->header.dwMipMapCount, }; VK_CHECK(vkCreateSampler(device, &samplerCreateInfo1, nullptr, &textureSamplers[1])); VkSamplerCreateInfo samplerCreateInfo2{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .magFilter = VK_FILTER_LINEAR, .minFilter = VK_FILTER_LINEAR, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .anisotropyEnable = VK_TRUE, .maxAnisotropy = 16.0f, .maxLod = VK_LOD_CLAMP_NONE, // (float)ddsFile->header.dwMipMapCount, }; VK_CHECK(vkCreateSampler(device, &samplerCreateInfo2, nullptr, &textureSamplers[2])); ////////////////////////////////////////////////////////////////////// // descriptors ////////////////////////////////////////////////////////////////////// // // pool // VkDescriptorPoolSize descriptorPoolSizes[3]{ { .type = VK_DESCRIPTOR_TYPE_SAMPLER, .descriptorCount = 3, }, { .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, .descriptorCount = 1, }, { .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = maxFramesInFlight, } }; VkDescriptorPoolCreateInfo descriptorPoolCreateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .maxSets = 3, .poolSizeCount = 3, .pPoolSizes = descriptorPoolSizes }; VK_CHECK(vkCreateDescriptorPool(device, &descriptorPoolCreateInfo, nullptr, &descriptorPool)); // // uniform buffer descriptor set layout/allocation // VkDescriptorSetLayoutBinding uniformBufferDescriptorSetLayoutBinding{ .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT }; VkDescriptorSetLayoutCreateInfo uniformBufferDescriptorSetLayoutCreateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .bindingCount = 1, .pBindings = &uniformBufferDescriptorSetLayoutBinding }; VK_CHECK(vkCreateDescriptorSetLayout(device, &uniformBufferDescriptorSetLayoutCreateInfo, nullptr, &uniformBufferDescriptorSetLayout)); VkDescriptorSetLayout uniformBufferDescriptorSetLayouts[maxFramesInFlight]; for (uint32_t i = 0; i < maxFramesInFlight; i++) { uniformBufferDescriptorSetLayouts[i] = uniformBufferDescriptorSetLayout; }; VkDescriptorSetAllocateInfo uniformBufferDescriptorSetAllocateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .descriptorPool = descriptorPool, .descriptorSetCount = maxFramesInFlight, .pSetLayouts = uniformBufferDescriptorSetLayouts }; VK_CHECK(vkAllocateDescriptorSets(device, &uniformBufferDescriptorSetAllocateInfo, uniformBufferDescriptorSets)); // // texture descriptor set layout/allocation // VkDescriptorSetLayoutBinding textureDescriptorSetLayoutBindings[2]{ { .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER, .descriptorCount = 3, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT }, { .binding = 1, .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT } }; VkDescriptorSetLayoutCreateInfo textureDescriptorSetLayoutCreateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .bindingCount = 2, .pBindings = textureDescriptorSetLayoutBindings }; VK_CHECK(vkCreateDescriptorSetLayout(device, &textureDescriptorSetLayoutCreateInfo, nullptr, &textureDescriptorSetLayout)); VkDescriptorSetAllocateInfo textureDescriptorSetAllocateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .descriptorPool = descriptorPool, .descriptorSetCount = 1, .pSetLayouts = &textureDescriptorSetLayout }; VK_CHECK(vkAllocateDescriptorSets(device, &textureDescriptorSetAllocateInfo, &textureDescriptorSet)); ////////////////////////////////////////////////////////////////////// // descriptor set writes ////////////////////////////////////////////////////////////////////// constexpr int writeDescriptorSetsCount = 2 + maxFramesInFlight; VkWriteDescriptorSet writeDescriptorSets[writeDescriptorSetsCount]; VkDescriptorImageInfo textureSamplerDescriptorImageInfos[3] = { { .sampler = textureSamplers[0], }, { .sampler = textureSamplers[1], }, { .sampler = textureSamplers[2], }, }; writeDescriptorSets[0] = { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = textureDescriptorSet, .dstBinding = 0, .descriptorCount = 3, .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER, .pImageInfo = textureSamplerDescriptorImageInfos }; VkDescriptorImageInfo textureImageDescriptorImageInfo = { .imageView = textureImageView, .imageLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL }; writeDescriptorSets[1] = { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = textureDescriptorSet, .dstBinding = 1, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, .pImageInfo = &textureImageDescriptorImageInfo }; for (uint32_t i = 0; i < maxFramesInFlight; i++) { VkDescriptorBufferInfo descriptorBufferInfo { .buffer = shaderDataDevice.frame[i].buffer, .offset = 0, .range = VK_WHOLE_SIZE, }; writeDescriptorSets[2 + i] = { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = uniformBufferDescriptorSets[i], .dstBinding = 0, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .pBufferInfo = &descriptorBufferInfo }; } // update all three descriptor sets vkUpdateDescriptorSets(device, writeDescriptorSetsCount, writeDescriptorSets, 0, nullptr); ////////////////////////////////////////////////////////////////////// // shaders ////////////////////////////////////////////////////////////////////// uint32_t triangleSize; void const * triangleStart = file::open("shader/triangle.spv", &triangleSize); VkShaderModuleCreateInfo shaderModuleCreateInfo{ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = triangleSize, .pCode = (uint32_t *)triangleStart }; VkShaderModule shaderModule{}; VK_CHECK(vkCreateShaderModule(device, &shaderModuleCreateInfo, nullptr, &shaderModule)); ////////////////////////////////////////////////////////////////////// // pipeline ////////////////////////////////////////////////////////////////////// VkDescriptorSetLayout descriptorSetLayouts[2] = { uniformBufferDescriptorSetLayout, textureDescriptorSetLayout, }; VkPushConstantRange pushConstantRange{ .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .size = (sizeof (int32_t)) }; VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = 2, .pSetLayouts = descriptorSetLayouts, .pushConstantRangeCount = 1, .pPushConstantRanges = &pushConstantRange }; VK_CHECK(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); VkVertexInputBindingDescription vertexBindingDescriptions[1]{ { .binding = 0, .stride = ((3 + 3 + 2) * (sizeof (float))), .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_R32G32_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 = shaderModule, .pName = "VSMain" }, { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = shaderModule, .pName = "PSMain" } }; VkPipelineShaderStageCreateInfo outlineShaderStages[2]{ { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = shaderModule, .pName = "VSOutlineMain" }, { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = shaderModule, .pName = "PSOutlineMain" } }; VkPipelineViewportStateCreateInfo viewportState{ .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .viewportCount = 1, .scissorCount = 1 }; constexpr uint32_t dynamicStateCount = 2; VkDynamicState dynamicStates[dynamicStateCount]{ VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, }; VkPipelineDynamicStateCreateInfo dynamicState{ .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .dynamicStateCount = dynamicStateCount, .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, .stencilTestEnable = VK_TRUE, .front = { .failOp = VK_STENCIL_OP_REPLACE, .passOp = VK_STENCIL_OP_REPLACE, .depthFailOp = VK_STENCIL_OP_REPLACE, .compareOp = VK_COMPARE_OP_ALWAYS, .compareMask = 0x01, .writeMask = 0x01, .reference = 1, }, }; VkPipelineDepthStencilStateCreateInfo outlineDepthStencilState{ .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .depthTestEnable = VK_TRUE, .depthWriteEnable = VK_TRUE, .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL, .stencilTestEnable = VK_TRUE, .front = { .failOp = VK_STENCIL_OP_KEEP, .passOp = VK_STENCIL_OP_REPLACE, .depthFailOp = VK_STENCIL_OP_KEEP, .compareOp = VK_COMPARE_OP_NOT_EQUAL, .compareMask = 0x01, .writeMask = 0x00, .reference = 1, }, }; VkPipelineRenderingCreateInfo renderingCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, .colorAttachmentCount = 1, .pColorAttachmentFormats = &surfaceFormat.format, .depthAttachmentFormat = depthFormat, .stencilAttachmentFormat = 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, .cullMode = VK_CULL_MODE_BACK_BIT, .frontFace = VK_FRONT_FACE_CLOCKWISE, .lineWidth = 1.0f }; VkPipelineMultisampleStateCreateInfo multisampleState{ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT }; VkGraphicsPipelineCreateInfo pipelineCreateInfos[2]{ { .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 }, { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = &renderingCreateInfo, .stageCount = 2, .pStages = outlineShaderStages, .pVertexInputState = &vertexInputState, .pInputAssemblyState = &inputAssemblyState, .pViewportState = &viewportState, .pRasterizationState = &rasterizationState, .pMultisampleState = &multisampleState, .pDepthStencilState = &outlineDepthStencilState, .pColorBlendState = &colorBlendState, .pDynamicState = &dynamicState, .layout = pipelineLayout } }; VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 2, pipelineCreateInfos, nullptr, pipelines)); ////////////////////////////////////////////////////////////////////// // initialize collada ////////////////////////////////////////////////////////////////////// collada::scene::state collada_state; collada_state.vulkan.initial_state(instance, device, physicalDeviceProperties, physicalDeviceMemoryProperties, surfaceFormat.format, depthFormat); collada::types::descriptor const * collada_scene_descriptor = &shadow_test::descriptor; collada_state.load_scene(collada_scene_descriptor); ////////////////////////////////////////////////////////////////////// // loop ////////////////////////////////////////////////////////////////////// bool updateSwapchain{ false }; uint32_t frameIndex{ 0 }; uint32_t imageIndex{ 0 }; bool quit{ false }; int32_t samplerIndex{ 0 }; int64_t start_time; SDL_GetCurrentTime(&start_time); int cameraIndex = collada_state.find_node_index_by_name("Camera001"); int cameraTargetIndex = collada_state.find_node_index_by_name("Camera001.Target"); while (quit == false) { SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_EVENT_QUIT) { quit = true; } if (event.type == SDL_EVENT_KEY_DOWN) { if (event.key.key == SDLK_ESCAPE) { quit = true; } if (!event.key.repeat) { switch (event.key.key) { case SDLK_LEFT: samplerIndex = positive_modulo(samplerIndex - 1, 3); printf("samplerIndex: %d\n", samplerIndex); break; case SDLK_RIGHT: samplerIndex = positive_modulo(samplerIndex + 1, 3); printf("samplerIndex: %d\n", samplerIndex); break; default: break; } } } if (event.type == SDL_EVENT_WINDOW_RESIZED) { SDL_CHECK(SDL_GetWindowSize(window, &windowSize.x, &windowSize.y)); } } // shader data /* XMMATRIX model = currentModel(); XMMATRIX view = currentView(); XMMATRIX modelView = model * view; XMMATRIX transform = modelView * currentProjection(); XMStoreFloat4x4(&shaderData.transform, transform); XMStoreFloat4x4(&shaderData.modelView, modelView); XMVECTOR lightPosition = XMVector3Transform(XMVectorSet(-3, -3, 0, 0), view); XMStoreFloat4(&shaderData.lightPosition, lightPosition); size_t frameOffset = shaderDataDevice.stride * frameIndex; void * frameData = (void *)(((VkDeviceSize)shaderDataDevice.mappedData) + frameOffset); VkDeviceSize frameSize{ (sizeof (ShaderData)) }; memcpy(frameData, &shaderData, frameSize); VkDeviceSize flushSize{ roundAlignment(frameSize, physicalDeviceProperties.limits.nonCoherentAtomSize) }; VkMappedMemoryRange shaderDataMemoryRange{ .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, .memory = shaderDataDevice.memory, .offset = frameOffset, .size = flushSize, }; vkFlushMappedMemoryRanges(device, 1, &shaderDataMemoryRange); */ // 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)); // 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_EARLY_FRAGMENT_TESTS_BIT | 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 | 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, .pStencilAttachment = &depthRenderingAttachmentInfo, }; vkCmdBeginRendering(commandBuffer, &renderingInfo); VkViewport viewport{ .x = 0, .y = 0,//static_cast(windowSize.y), .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); /* VkDescriptorSet descriptorSets[2] = { uniformBufferDescriptorSets[frameIndex], textureDescriptorSet, }; vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 2, descriptorSets, 0, nullptr); VkDeviceSize vertexOffset{ 0 }; vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexIndexBuffer, &vertexOffset); VkDeviceSize indexOffset{ vertexBufferSize }; vkCmdBindIndexBuffer(commandBuffer, vertexIndexBuffer, indexOffset, VK_INDEX_TYPE_UINT32); vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, (sizeof (int32_t)), &samplerIndex); VkDeviceSize indexCount{ 2400 }; vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[MAIN_PIPELINE]); vkCmdDrawIndexed(commandBuffer, indexCount, 1, 0, 0, 0); //vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[OUTLINE_PIPELINE]); //vkCmdDrawIndexed(commandBuffer, indexCount, 1, 0, 0, 0); */ collada_state.vulkan.change_frame(commandBuffer, frameIndex); double time = getTime(start_time); collada_state.update(time / 5.0f); XMMATRIX projection = currentProjection(); XMMATRIX view = currentView(collada_state.node_state.node_instances[cameraIndex], collada_state.node_state.node_instances[cameraTargetIndex]); collada_state.vulkan.transfer_transforms(projection, view, collada_state.descriptor->nodes_count, collada_state.node_state.node_instances); collada_state.draw(); 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, physicalDeviceMemoryProperties, surfaceCapabilities); } } VK_CHECK(vkDeviceWaitIdle(device)); collada_state.vulkan.destroy_all(collada_scene_descriptor); collada_state.unload_scene(); for (uint32_t i = 0; i < maxFramesInFlight; i++) { vkDestroyFence(device, fences[i], nullptr); vkDestroySemaphore(device, presentSemaphores[i], nullptr); vkDestroyBuffer(device, shaderDataDevice.frame[i].buffer, nullptr); } vkUnmapMemory(device, shaderDataDevice.memory); vkFreeMemory(device, shaderDataDevice.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, textureSamplers[0], nullptr); vkDestroySampler(device, textureSamplers[1], nullptr); vkDestroySampler(device, textureSamplers[2], nullptr); vkDestroyImage(device, textureImage, nullptr); vkFreeMemory(device, textureImageMemory, nullptr); vkDestroyDescriptorSetLayout(device, uniformBufferDescriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(device, textureDescriptorSetLayout, nullptr); vkDestroyDescriptorPool(device, descriptorPool, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr); vkDestroyPipeline(device, pipelines[0], nullptr); vkDestroyPipeline(device, pipelines[1], nullptr); vkDestroySwapchainKHR(device, swapchain, nullptr); vkDestroySurfaceKHR(instance, surface, nullptr); vkDestroyCommandPool(device, commandPool, nullptr); vkDestroyShaderModule(device, shaderModule, nullptr); SDL_DestroyWindow(window); SDL_QuitSubSystem(SDL_INIT_VIDEO); SDL_Quit(); vkDestroyDevice(device, nullptr); vkDestroyInstance(instance, nullptr); }