diff --git a/Blaze.cpp b/Blaze.cpp index 53aaeb1..e917224 100644 --- a/Blaze.cpp +++ b/Blaze.cpp @@ -1,9 +1,11 @@ // Blaze.cpp : This file contains the 'main' function. Program execution begins and ends there. // +#include #include #include +#include #include #include @@ -15,6 +17,12 @@ constexpr uint32_t WIDTH = 1280; constexpr uint32_t HEIGHT = 720; +constexpr uint32_t NUM_FRAMES = 3; + +uint32_t Clamp(uint32_t const val, uint32_t const minVal, uint32_t const maxVal) +{ + return std::min(maxVal, std::max(val, minVal)); +} int main() { @@ -24,7 +32,7 @@ int main() SDL_Window* window = SDL_CreateWindow("Blaze Test", WIDTH, HEIGHT, SDL_WINDOW_VULKAN); - VkApplicationInfo applicationInfo{ + VkApplicationInfo constexpr applicationInfo = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pNext = nullptr, .pApplicationName = "Test", @@ -37,7 +45,7 @@ int main() uint32_t instanceExtensionCount; char const* const* instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceExtensionCount); - VkInstanceCreateInfo createInfo{ + VkInstanceCreateInfo const instanceCreateInfo = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pNext = nullptr, .flags = 0, @@ -49,19 +57,401 @@ int main() }; VkInstance instance; - VK_CHECK(vkCreateInstance(&createInfo, nullptr, &instance)); + VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); volkLoadInstance(instance); VkSurfaceKHR surface; ASSERT(SDL_Vulkan_CreateSurface(window, instance, nullptr, &surface)); + VkPhysicalDevice physicalDeviceInUse = nullptr; + VkDevice device = nullptr; + uint32_t directQueueFamilyIndex = 0; + VkQueue directQueue; + { + uint32_t physicalDeviceCount; + VK_CHECK(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr)); + SDL_Log("Found %u GPUs", physicalDeviceCount); + + std::vector physicalDevices(physicalDeviceCount); + VK_CHECK(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices.data())); + + + std::vector queueFamilyProperties; + for (VkPhysicalDevice const physicalDevice : physicalDevices) + { + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(physicalDevice, &properties); + + SDL_Log("GPU: %s", properties.deviceName); + + SDL_Log("- API Version %d.%d.%d", + VK_API_VERSION_MAJOR(properties.apiVersion), + VK_API_VERSION_MINOR(properties.apiVersion), + VK_API_VERSION_PATCH(properties.apiVersion)); + + constexpr static uint32_t API_PATCH_BITS = 0xFFF; + if ((properties.apiVersion & (~API_PATCH_BITS)) < VK_API_VERSION_1_3) + { + continue; + } + + if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) + { + continue; + } + + uint32_t queueFamilyCount; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); + queueFamilyProperties.resize(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilyProperties.data()); + + for (uint32_t queueFamilyIndex = 0; VkQueueFamilyProperties const qfp : queueFamilyProperties) + { + bool hasGraphicsSupport = false; + bool hasComputeSupport = false; + bool hasTransferSupport = false; + bool hasPresentSupport = false; + + SDL_Log("- Queue [%d]", queueFamilyIndex); + if (qfp.queueFlags & VK_QUEUE_GRAPHICS_BIT) + { + hasGraphicsSupport = true; + SDL_Log("-- Graphic"); + } + if (qfp.queueFlags & VK_QUEUE_COMPUTE_BIT) + { + hasComputeSupport = true; + SDL_Log("-- Compute"); + } + if (qfp.queueFlags & VK_QUEUE_TRANSFER_BIT) + { + hasTransferSupport = true; + SDL_Log("-- Transfer"); + } + + VkBool32 isSurfaceSupported; + VK_CHECK(vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface, &isSurfaceSupported)); + + if (isSurfaceSupported) + { + hasPresentSupport = true; + SDL_Log("-- Present"); + } + + if (hasGraphicsSupport and hasComputeSupport and hasTransferSupport and hasPresentSupport) + { + physicalDeviceInUse = physicalDevice; + directQueueFamilyIndex = queueFamilyIndex; + break; + } + + ++queueFamilyIndex; + } + } + + ASSERT(physicalDeviceInUse); + + float priority = 1.0f; + VkDeviceQueueCreateInfo queueCreateInfo = { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueFamilyIndex = directQueueFamilyIndex, + .queueCount = 1, + .pQueuePriorities = &priority, + }; + + VkPhysicalDeviceVulkan13Features constexpr features13 = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, + .pNext = nullptr, + .synchronization2 = true, + .dynamicRendering = true, + }; + + VkPhysicalDeviceFeatures features = { + .depthClamp = true, + .samplerAnisotropy = true, + }; + + std::array enabledDeviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME + }; + + VkDeviceCreateInfo const deviceCreateInfo = { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = &features13, + .flags = 0, + .queueCreateInfoCount = 1, + .pQueueCreateInfos = &queueCreateInfo, + .enabledLayerCount = 0, + .ppEnabledLayerNames = nullptr, + .enabledExtensionCount = enabledDeviceExtensions.size(), + .ppEnabledExtensionNames = enabledDeviceExtensions.data(), + .pEnabledFeatures = &features, + }; + + VK_CHECK(vkCreateDevice(physicalDeviceInUse, &deviceCreateInfo, nullptr, &device)); + vkGetDeviceQueue(device, directQueueFamilyIndex, 0, &directQueue); + } + + VkExtent2D swapchainExtent = { WIDTH, HEIGHT }; + VkSwapchainKHR swapchain; + std::vector swapchainImages; + std::vector swapchainViews; + { + VkSurfaceCapabilitiesKHR capabilities; + VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDeviceInUse, surface, &capabilities)); + + // Image Count Calculation + uint32_t swapchainImageCount = NUM_FRAMES; + if (capabilities.maxImageCount > 0) + { + swapchainImageCount = std::min(swapchainImageCount, capabilities.maxImageCount); + } + swapchainImageCount = std::max(swapchainImageCount, capabilities.minImageCount); + + // Image Size calculation + { + auto [minWidth, minHeight] = capabilities.minImageExtent; + auto [maxWidth, maxHeight] = capabilities.maxImageExtent; + swapchainExtent.width = Clamp(swapchainExtent.width, minWidth, maxWidth); + swapchainExtent.height = Clamp(swapchainExtent.height, minHeight, maxHeight); + } + + uint32_t surfaceFormatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDeviceInUse, surface, &surfaceFormatCount, nullptr); + std::vector surfaceFormats(surfaceFormatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDeviceInUse, surface, &surfaceFormatCount, surfaceFormats.data()); + + VkSurfaceFormatKHR format; + format.format = VK_FORMAT_UNDEFINED; + for (auto& surfaceFormat : surfaceFormats) + { + if (surfaceFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + { + SDL_Log("Color Space SRGB Found %d", surfaceFormat.format); + if (surfaceFormat.format == VK_FORMAT_R8G8B8A8_SRGB) { + format = surfaceFormat; + break; + } + if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_SRGB) { + format = surfaceFormat; + break; + } + if (surfaceFormat.format == VK_FORMAT_R8G8B8A8_UNORM) { + format = surfaceFormat; + } + } + } + ASSERT(format.format != VK_FORMAT_UNDEFINED); + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDeviceInUse, surface, &presentModeCount, nullptr); + std::vector presentModes(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDeviceInUse, surface, &presentModeCount, presentModes.data()); + + VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; + for (VkPresentModeKHR presentModeIter : presentModes) + { + if (presentModeIter == VK_PRESENT_MODE_FIFO_RELAXED_KHR) + { + presentMode = presentModeIter; + break; + } + + if (presentModeIter == VK_PRESENT_MODE_MAILBOX_KHR) + { + presentMode = presentModeIter; + } + } + + VkSwapchainCreateInfoKHR const swapchainCreateInfo = { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .pNext = nullptr, + .flags = 0, + .surface = surface, + .minImageCount = swapchainImageCount, + .imageFormat = format.format, + .imageColorSpace = format.colorSpace, + .imageExtent = swapchainExtent, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = presentMode, + .clipped = false, + .oldSwapchain = nullptr, + }; + + VK_CHECK(vkCreateSwapchainKHR(device, &swapchainCreateInfo, nullptr, &swapchain)); + + swapchainImageCount = 0; + vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, nullptr); + swapchainImages.resize(swapchainImageCount); + vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, swapchainImages.data()); + + for (VkImage image : swapchainImages) { + + VkImageViewCreateInfo const viewCreateInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .image = image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = format.format, + .components = { + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY + }, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + } + }; + + VkImageView view; + VK_CHECK(vkCreateImageView(device, &viewCreateInfo, nullptr, &view)); + + swapchainViews.push_back(view); + } + } + + // Create 1 command pool per frame. + std::array commandPools; + { + VkCommandPoolCreateInfo const commandPoolCreateInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, + .queueFamilyIndex = directQueueFamilyIndex, + }; + for (auto& commandPool : commandPools) { + VK_CHECK(vkCreateCommandPool(device, &commandPoolCreateInfo, nullptr, &commandPool)); + } + } + + std::array commandBuffers; + { + uint32_t index = 0; + for (auto& commandPool : commandPools) { + VkCommandBufferAllocateInfo const commandBufferAllocateInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .pNext = nullptr, + .commandPool = commandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + VK_CHECK(vkAllocateCommandBuffers(device, &commandBufferAllocateInfo, &commandBuffers[index])); + + ++index; + } + } + + std::array imageAcquiredSemaphores; + std::array renderFinishedSemaphores; + std::array frameInUseFences; + { + VkSemaphoreCreateInfo constexpr semaphoreCreateInfo = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = nullptr, + .flags = 0 + }; + for (auto& semaphore : imageAcquiredSemaphores) { + VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphore)); + } + for (auto& semaphore : renderFinishedSemaphores) { + VK_CHECK(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphore)); + } + + VkFenceCreateInfo constexpr fenceCreateInfo = { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .pNext = nullptr, + .flags = VK_FENCE_CREATE_SIGNALED_BIT, + }; + for (auto& fence : frameInUseFences) { + VK_CHECK(vkCreateFence(device, &fenceCreateInfo, nullptr, &fence)); + } + } + + VkImageMemoryBarrier2 acquireToRenderBarrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + } + }; + VkDependencyInfo acquireToRenderDependency = { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pNext = nullptr, + .dependencyFlags = 0, + .memoryBarrierCount = 0, + .pMemoryBarriers = nullptr, + .bufferMemoryBarrierCount = 0, + .pBufferMemoryBarriers = nullptr, + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &acquireToRenderBarrier, + }; + + VkImageMemoryBarrier2 renderToPresentBarrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT, + .dstAccessMask = VK_ACCESS_2_NONE, + .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + } + }; + VkDependencyInfo renderToPresentDependency = { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pNext = nullptr, + .dependencyFlags = 0, + .memoryBarrierCount = 0, + .pMemoryBarriers = nullptr, + .bufferMemoryBarrierCount = 0, + .pBufferMemoryBarriers = nullptr, + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &renderToPresentBarrier, + }; + + + uint32_t frameIndex = 0; bool isRunning = true; while (isRunning) { SDL_PumpEvents(); SDL_Event event; - while(SDL_PollEvent(&event)) + while (SDL_PollEvent(&event)) { switch (event.type) { @@ -72,16 +462,114 @@ int main() break; } } + + VkFence fence = frameInUseFences[frameIndex]; + + VK_CHECK(vkWaitForFences(device, 1, &fence, VK_TRUE, std::numeric_limits::max())); + // All resources of frame 'frameIndex' are free. + + VkSemaphore imageAcquiredSemaphore = imageAcquiredSemaphores[frameIndex]; + + uint32_t currentImageIndex; + VK_CHECK(vkAcquireNextImageKHR(device, swapchain, std::numeric_limits::max(), imageAcquiredSemaphore, nullptr, ¤tImageIndex)); + + vkResetFences(device, 1, &fence); + + acquireToRenderBarrier.image = swapchainImages[currentImageIndex]; + renderToPresentBarrier.image = swapchainImages[currentImageIndex]; + + VkCommandBuffer cmd = commandBuffers[frameIndex]; + + VkCommandBufferBeginInfo constexpr beginInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + .flags = 0, + .pInheritanceInfo = nullptr, + }; + + VK_CHECK(vkBeginCommandBuffer(cmd, &beginInfo)); + { + VkRenderingAttachmentInfo const attachmentInfo = { + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = swapchainViews[currentImageIndex], + .imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .resolveMode = VK_RESOLVE_MODE_NONE, + .resolveImageView = nullptr, + .resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .clearValue = {.color = {{100.0f / 255.0f, 149.0f / 255.0f, 237.0f / 255.0f}}}, + }; + + VkRenderingInfo renderingInfo = { + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + .pNext = nullptr, + .flags = 0, + .renderArea = {.offset = {0,0}, .extent = swapchainExtent}, + .layerCount = 1, + .viewMask = 0, + .colorAttachmentCount = 1, + .pColorAttachments = &attachmentInfo, + .pDepthAttachment = nullptr, + .pStencilAttachment = nullptr, + }; + + vkCmdPipelineBarrier2(cmd, &acquireToRenderDependency); + vkCmdBeginRendering(cmd, &renderingInfo); + + // Render Something? + + vkCmdEndRendering(cmd); + vkCmdPipelineBarrier2(cmd, &renderToPresentDependency); + } + VK_CHECK(vkEndCommandBuffer(cmd)); + + VkSemaphore renderingFinishedSemaphore = renderFinishedSemaphores[frameIndex]; + + VkPipelineStageFlags stageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo const submitInfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = nullptr, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &imageAcquiredSemaphore, + .pWaitDstStageMask = &stageMask, + .commandBufferCount = 1, + .pCommandBuffers = &cmd, + .signalSemaphoreCount = 1, + .pSignalSemaphores = &renderingFinishedSemaphore, + }; + VK_CHECK(vkQueueSubmit(directQueue, 1, &submitInfo, fence)); + + VkPresentInfoKHR const presentInfo = { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pNext = nullptr, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &renderingFinishedSemaphore, + .swapchainCount = 1, + .pSwapchains = &swapchain, + .pImageIndices = ¤tImageIndex, + .pResults = nullptr, + }; + + VK_CHECK(vkQueuePresentKHR(directQueue, &presentInfo)); + + frameIndex = (frameIndex + 1) % NUM_FRAMES; } + for (VkImageView view : swapchainViews) { + vkDestroyImageView(device, view, nullptr); + } + vkDestroySwapchainKHR(device, swapchain, nullptr); + + vkDestroyDevice(device, nullptr); + SDL_Vulkan_DestroySurface(instance, surface, nullptr); vkDestroyInstance(instance, nullptr); volkFinalize(); - //Blaze blaze; - SDL_DestroyWindow(window); SDL_Quit(); diff --git a/PLAN.md b/PLAN.md index b680a7d..77ae754 100644 --- a/PLAN.md +++ b/PLAN.md @@ -6,13 +6,15 @@ - [X] compile flags etc. - [X] SDL3 - [X] volk -- [ ] Create a Triangle +- [X] Clear Screen - [X] Create Window - [X] Load instance - [X] Create surface - - [ ] Choose physical device - - [ ] Create logical device - - [ ] Create swapchain + - [X] Choose physical device + - [X] Create logical device + - [X] Create swapchain + - [X] Clear Render Attachments +- [ ] Create a Triangle - [ ] Create pipeline - [ ] Draw - [ ] Create a Box