2024-10-10 20:39:04 -04:00
|
|
|
#include <chrono>
|
2024-09-30 00:57:13 -04:00
|
|
|
#include <fmt/format.h>
|
2024-10-01 18:30:31 -04:00
|
|
|
#include <fstream>
|
2024-09-28 18:13:24 -04:00
|
|
|
#include <iostream>
|
2024-09-30 00:57:13 -04:00
|
|
|
#include <set>
|
2024-09-25 23:03:56 -04:00
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
2024-10-11 20:04:28 -04:00
|
|
|
#define GLM_ENABLE_EXPERIMENTAL
|
2024-10-11 19:09:37 -04:00
|
|
|
#include <glm/glm.hpp>
|
|
|
|
#include <glm/gtc/matrix_transform.hpp>
|
2024-10-11 20:04:28 -04:00
|
|
|
#include <glm/gtx/hash.hpp>
|
2024-10-11 19:09:37 -04:00
|
|
|
|
2024-10-11 00:42:58 -04:00
|
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
|
|
#include <stb_image.h>
|
2024-10-10 20:39:04 -04:00
|
|
|
|
2024-10-11 20:04:28 -04:00
|
|
|
#define TINYOBJLOADER_IMPLEMENTATION
|
|
|
|
#include <tiny_obj_loader.h>
|
|
|
|
|
2024-10-06 12:26:44 -04:00
|
|
|
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
|
2024-09-30 21:21:38 -04:00
|
|
|
#define VK_ENABLE_BETA_EXTENSIONS
|
2024-09-29 23:02:04 -04:00
|
|
|
#define VULKAN_HPP_NO_CONSTRUCTORS
|
|
|
|
#include <vulkan/vulkan.hpp>
|
|
|
|
|
2024-10-06 12:26:44 -04:00
|
|
|
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
|
|
|
|
2024-09-28 19:38:13 -04:00
|
|
|
#include "util/types.h"
|
2024-10-05 23:08:12 -04:00
|
|
|
|
|
|
|
#define VKFW_NO_STD_FUNCTION_CALLBACKS
|
2024-09-30 17:52:49 -04:00
|
|
|
#include "vkfw.hpp"
|
2024-09-25 23:03:56 -04:00
|
|
|
|
2024-09-30 17:52:49 -04:00
|
|
|
constexpr i32 WIDTH = 800;
|
|
|
|
constexpr i32 HEIGHT = 600;
|
2024-09-25 23:03:56 -04:00
|
|
|
|
2024-10-11 20:04:28 -04:00
|
|
|
constexpr const char* MODEL_PATH = "models/viking_room.obj";
|
|
|
|
constexpr const char* TEXTURE_PATH = "textures/viking_room.png";
|
|
|
|
|
2024-10-05 23:08:12 -04:00
|
|
|
constexpr i32 MAX_FRAMES_IN_FLIGHT = 2;
|
|
|
|
|
2024-10-10 20:39:04 -04:00
|
|
|
constexpr std::array<const char*, 1> validationLayers = { "VK_LAYER_KHRONOS_validation" };
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
constexpr std::array<const char*, 2> deviceExtensions = { vk::KHRSwapchainExtensionName,
|
|
|
|
vk::KHRPortabilitySubsetExtensionName };
|
|
|
|
#else
|
|
|
|
constexpr std::array<const char*, 1> deviceExtensions = { vk::KHRSwapchainExtensionName };
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef NDEBUG
|
|
|
|
constexpr bool enableValidationLayers = false;
|
|
|
|
#else
|
|
|
|
constexpr bool enableValidationLayers = true;
|
|
|
|
#endif
|
|
|
|
|
2024-10-10 18:51:20 -04:00
|
|
|
struct Vertex {
|
2024-10-11 19:09:37 -04:00
|
|
|
glm::vec3 pos;
|
2024-10-10 18:51:20 -04:00
|
|
|
glm::vec3 color;
|
2024-10-11 00:42:58 -04:00
|
|
|
glm::vec2 tex_coord;
|
2024-10-10 18:51:20 -04:00
|
|
|
|
|
|
|
static fn getBindingDescription() -> vk::VertexInputBindingDescription {
|
2024-10-10 20:39:04 -04:00
|
|
|
return { .binding = 0, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex };
|
2024-10-10 18:51:20 -04:00
|
|
|
}
|
|
|
|
|
2024-10-11 00:42:58 -04:00
|
|
|
static fn getAttributeDescriptions() -> std::array<vk::VertexInputAttributeDescription, 3> {
|
2024-10-10 20:39:04 -04:00
|
|
|
return {
|
2024-10-11 19:09:37 -04:00
|
|
|
{ { 0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, pos) },
|
2024-10-11 00:42:58 -04:00
|
|
|
{ 1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color) },
|
|
|
|
{ 2, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, tex_coord) } }
|
2024-10-10 18:51:20 -04:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-10-11 20:04:28 -04:00
|
|
|
fn operator==(const Vertex& other) const->bool {
|
|
|
|
return pos == other.pos && color == other.color && tex_coord == other.tex_coord;
|
|
|
|
}
|
2024-10-10 18:51:20 -04:00
|
|
|
};
|
|
|
|
|
2024-10-11 20:04:28 -04:00
|
|
|
namespace std {
|
|
|
|
template <>
|
|
|
|
struct hash<Vertex> {
|
|
|
|
fn operator()(Vertex const& vertex) const->size_t {
|
|
|
|
return ((hash<glm::vec3>()(vertex.pos) ^ (hash<glm::vec3>()(vertex.color) << 1)) >> 1) ^
|
|
|
|
(hash<glm::vec2>()(vertex.tex_coord) << 1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2024-10-10 18:51:20 -04:00
|
|
|
|
2024-09-29 23:11:12 -04:00
|
|
|
class VulkanApp {
|
2024-09-25 23:03:56 -04:00
|
|
|
public:
|
2024-09-30 00:57:13 -04:00
|
|
|
fn run() -> void {
|
2024-09-25 23:03:56 -04:00
|
|
|
initWindow();
|
|
|
|
initVulkan();
|
|
|
|
mainLoop();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2024-09-30 16:46:17 -04:00
|
|
|
vkfw::UniqueInstance mGLFWInstance;
|
|
|
|
vkfw::UniqueWindow mWindow;
|
2024-09-28 18:13:24 -04:00
|
|
|
|
2024-09-29 23:02:04 -04:00
|
|
|
vk::UniqueInstance mInstance;
|
2024-09-25 23:03:56 -04:00
|
|
|
|
2024-10-06 12:26:44 -04:00
|
|
|
vk::UniqueDebugUtilsMessengerEXT mDebugMessenger;
|
|
|
|
vk::UniqueSurfaceKHR mSurface;
|
2024-09-30 00:31:08 -04:00
|
|
|
|
|
|
|
vk::PhysicalDevice mPhysicalDevice;
|
2024-09-30 00:57:13 -04:00
|
|
|
vk::UniqueDevice mDevice;
|
|
|
|
|
|
|
|
vk::Queue mGraphicsQueue;
|
|
|
|
vk::Queue mPresentQueue;
|
|
|
|
|
2024-10-01 18:54:41 -04:00
|
|
|
vk::UniqueSwapchainKHR mSwapChain;
|
|
|
|
std::vector<vk::Image> mSwapChainImages;
|
|
|
|
vk::Format mSwapChainImageFormat;
|
|
|
|
vk::Extent2D mSwapChainExtent;
|
|
|
|
std::vector<vk::UniqueImageView> mSwapChainImageViews;
|
|
|
|
std::vector<vk::UniqueFramebuffer> mSwapChainFramebuffers;
|
2024-10-01 16:57:40 -04:00
|
|
|
|
2024-10-10 20:39:04 -04:00
|
|
|
vk::UniqueRenderPass mRenderPass;
|
|
|
|
vk::UniqueDescriptorSetLayout mDescriptorSetLayout;
|
|
|
|
vk::UniquePipelineLayout mPipelineLayout;
|
|
|
|
vk::UniquePipeline mGraphicsPipeline;
|
2024-10-01 18:30:31 -04:00
|
|
|
|
2024-10-10 20:39:04 -04:00
|
|
|
vk::UniqueCommandPool mCommandPool;
|
2024-10-01 18:54:41 -04:00
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
vk::UniqueImage mDepthImage;
|
|
|
|
vk::UniqueDeviceMemory mDepthImageMemory;
|
|
|
|
vk::UniqueImageView mDepthImageView;
|
|
|
|
|
2024-10-11 20:50:23 -04:00
|
|
|
u32 mMipLevels;
|
2024-10-11 00:42:58 -04:00
|
|
|
vk::UniqueImage mTextureImage;
|
|
|
|
vk::UniqueDeviceMemory mTextureImageMemory;
|
|
|
|
vk::UniqueImageView mTextureImageView;
|
|
|
|
vk::UniqueSampler mTextureSampler;
|
|
|
|
|
2024-10-11 20:04:28 -04:00
|
|
|
std::vector<Vertex> mVertices;
|
|
|
|
std::vector<u32> mIndices;
|
2024-10-09 23:00:45 -04:00
|
|
|
vk::UniqueBuffer mVertexBuffer;
|
|
|
|
vk::UniqueDeviceMemory mVertexBufferMemory;
|
2024-10-10 18:51:20 -04:00
|
|
|
vk::UniqueBuffer mIndexBuffer;
|
|
|
|
vk::UniqueDeviceMemory mIndexBufferMemory;
|
2024-10-09 23:00:45 -04:00
|
|
|
|
2024-10-10 20:39:04 -04:00
|
|
|
std::vector<vk::UniqueBuffer> mUniformBuffers;
|
|
|
|
std::vector<vk::UniqueDeviceMemory> mUniformBuffersMemory;
|
|
|
|
std::vector<void*> mUniformBuffersMapped;
|
|
|
|
|
|
|
|
vk::UniqueDescriptorPool mDescriptorPool;
|
|
|
|
std::vector<vk::DescriptorSet> mDescriptorSets;
|
|
|
|
|
|
|
|
std::vector<vk::UniqueCommandBuffer> mCommandBuffers;
|
|
|
|
|
|
|
|
std::vector<vk::UniqueSemaphore> mImageAvailableSemaphores;
|
|
|
|
std::vector<vk::UniqueSemaphore> mRenderFinishedSemaphores;
|
|
|
|
std::vector<vk::UniqueFence> mInFlightFences;
|
|
|
|
|
2024-10-05 23:08:12 -04:00
|
|
|
bool mFramebufferResized = false;
|
|
|
|
u32 mCurrentFrame = 0;
|
2024-10-01 18:33:05 -04:00
|
|
|
|
2024-09-30 00:31:08 -04:00
|
|
|
struct QueueFamilyIndices {
|
|
|
|
std::optional<u32> graphics_family;
|
2024-09-30 00:57:13 -04:00
|
|
|
std::optional<u32> present_family;
|
2024-09-30 00:31:08 -04:00
|
|
|
|
2024-09-30 00:57:13 -04:00
|
|
|
fn isComplete() -> bool { return graphics_family.has_value() && present_family.has_value(); }
|
2024-09-30 00:31:08 -04:00
|
|
|
};
|
2024-09-28 21:55:26 -04:00
|
|
|
|
2024-10-01 16:10:20 -04:00
|
|
|
struct SwapChainSupportDetails {
|
|
|
|
vk::SurfaceCapabilitiesKHR capabilities;
|
|
|
|
std::vector<vk::SurfaceFormatKHR> formats;
|
|
|
|
std::vector<vk::PresentModeKHR> present_modes;
|
|
|
|
};
|
|
|
|
|
2024-10-10 20:39:04 -04:00
|
|
|
struct UniformBufferObject {
|
|
|
|
alignas(16) glm::mat4 model;
|
|
|
|
alignas(16) glm::mat4 view;
|
|
|
|
alignas(16) glm::mat4 proj;
|
|
|
|
};
|
|
|
|
|
2024-10-01 18:30:31 -04:00
|
|
|
static fn readFile(const std::string& filename) -> std::vector<char> {
|
|
|
|
std::ifstream file(filename, std::ios::ate | std::ios::binary);
|
|
|
|
|
|
|
|
if (!file.is_open())
|
|
|
|
throw std::runtime_error("Failed to open file! " + filename);
|
|
|
|
|
|
|
|
usize fileSize = static_cast<usize>(file.tellg());
|
|
|
|
std::vector<char> buffer(fileSize);
|
|
|
|
|
|
|
|
file.seekg(0);
|
|
|
|
file.read(buffer.data(), static_cast<std::streamsize>(fileSize));
|
|
|
|
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
2024-09-30 00:57:13 -04:00
|
|
|
fn initWindow() -> void {
|
2024-09-30 16:50:58 -04:00
|
|
|
mGLFWInstance = vkfw::initUnique();
|
2024-09-25 23:03:56 -04:00
|
|
|
|
2024-09-30 16:50:58 -04:00
|
|
|
vkfw::WindowHints hints;
|
2024-09-25 23:03:56 -04:00
|
|
|
|
2024-09-30 16:50:58 -04:00
|
|
|
hints.clientAPI = vkfw::ClientAPI::eNone;
|
2024-09-30 16:46:17 -04:00
|
|
|
|
2024-09-30 16:50:58 -04:00
|
|
|
mWindow = vkfw::createWindowUnique(WIDTH, HEIGHT, "Vulkan", hints);
|
2024-10-05 23:08:12 -04:00
|
|
|
mWindow->setUserPointer(this);
|
|
|
|
mWindow->setFramebufferSizeCallback(framebufferResizeCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
static fn framebufferResizeCallback(GLFWwindow* window, int /*width*/, int /*height*/) -> void {
|
|
|
|
auto* app = std::bit_cast<VulkanApp*>(glfwGetWindowUserPointer(window));
|
|
|
|
|
|
|
|
app->mFramebufferResized = true;
|
2024-09-25 23:03:56 -04:00
|
|
|
}
|
|
|
|
|
2024-09-30 00:57:13 -04:00
|
|
|
fn initVulkan() -> void {
|
2024-09-28 18:13:24 -04:00
|
|
|
createInstance();
|
|
|
|
setupDebugMessenger();
|
2024-09-30 00:57:13 -04:00
|
|
|
createSurface();
|
2024-09-30 00:31:08 -04:00
|
|
|
pickPhysicalDevice();
|
2024-09-30 00:57:13 -04:00
|
|
|
createLogicalDevice();
|
2024-10-01 16:57:40 -04:00
|
|
|
createSwapChain();
|
2024-10-01 17:06:14 -04:00
|
|
|
createImageViews();
|
2024-10-01 18:30:31 -04:00
|
|
|
createRenderPass();
|
2024-10-10 20:39:04 -04:00
|
|
|
createDescriptorSetLayout();
|
2024-10-01 18:30:31 -04:00
|
|
|
createGraphicsPipeline();
|
2024-10-01 18:54:41 -04:00
|
|
|
createCommandPool();
|
2024-10-11 19:09:37 -04:00
|
|
|
createDepthResources();
|
|
|
|
createFramebuffers();
|
2024-10-11 00:42:58 -04:00
|
|
|
createTextureImage();
|
|
|
|
createTextureImageView();
|
|
|
|
createTextureSampler();
|
2024-10-11 20:04:28 -04:00
|
|
|
loadModel();
|
2024-10-09 23:00:45 -04:00
|
|
|
createVertexBuffer();
|
2024-10-10 18:51:20 -04:00
|
|
|
createIndexBuffer();
|
2024-10-10 20:39:04 -04:00
|
|
|
createUniformBuffers();
|
|
|
|
createDescriptorPool();
|
|
|
|
createDescriptorSets();
|
2024-10-05 23:08:12 -04:00
|
|
|
createCommandBuffers();
|
2024-10-01 18:54:41 -04:00
|
|
|
createSyncObjects();
|
2024-09-29 23:02:04 -04:00
|
|
|
}
|
2024-09-28 14:54:39 -04:00
|
|
|
|
2024-09-30 00:57:13 -04:00
|
|
|
fn mainLoop() -> void {
|
2024-10-01 18:54:41 -04:00
|
|
|
while (!mWindow->shouldClose()) {
|
2024-10-10 21:36:42 -04:00
|
|
|
vkfw::pollEvents();
|
2024-10-01 18:54:41 -04:00
|
|
|
drawFrame();
|
|
|
|
}
|
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
mDevice->waitIdle();
|
2024-09-28 14:54:39 -04:00
|
|
|
}
|
|
|
|
|
2024-10-05 23:08:12 -04:00
|
|
|
fn cleanupSwapChain() -> void {
|
2024-10-11 19:09:37 -04:00
|
|
|
for (vk::UniqueFramebuffer& mSwapChainFramebuffer : mSwapChainFramebuffers) mSwapChainFramebuffer.reset();
|
|
|
|
for (vk::UniqueImageView& mSwapChainImageView : mSwapChainImageViews) mSwapChainImageView.reset();
|
2024-10-05 23:08:12 -04:00
|
|
|
|
|
|
|
mSwapChain.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn recreateSwapChain() -> void {
|
|
|
|
u32 width = 0, height = 0;
|
|
|
|
std::tie(width, height) = mWindow->getFramebufferSize();
|
|
|
|
|
|
|
|
while (width == 0 || height == 0) {
|
|
|
|
std::tie(width, height) = mWindow->getFramebufferSize();
|
|
|
|
vkfw::waitEvents();
|
|
|
|
}
|
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
mDevice->waitIdle();
|
2024-10-05 23:08:12 -04:00
|
|
|
|
|
|
|
cleanupSwapChain();
|
|
|
|
|
|
|
|
createSwapChain();
|
|
|
|
createImageViews();
|
2024-10-11 19:09:37 -04:00
|
|
|
createDepthResources();
|
2024-10-05 23:08:12 -04:00
|
|
|
createFramebuffers();
|
|
|
|
}
|
|
|
|
|
2024-09-25 23:03:56 -04:00
|
|
|
fn createInstance() -> void {
|
|
|
|
if (enableValidationLayers && !checkValidationLayerSupport())
|
2024-09-30 21:21:38 -04:00
|
|
|
throw std::runtime_error("Validation layers requested, but not available!");
|
2024-09-25 23:03:56 -04:00
|
|
|
|
2024-09-29 20:12:56 -04:00
|
|
|
vk::ApplicationInfo appInfo { .pApplicationName = "Hello Triangle",
|
|
|
|
.applicationVersion = 1,
|
|
|
|
.pEngineName = "No Engine",
|
|
|
|
.engineVersion = 1,
|
2024-10-06 12:26:44 -04:00
|
|
|
.apiVersion = vk::ApiVersion12 };
|
2024-09-25 23:03:56 -04:00
|
|
|
|
2024-09-29 20:12:56 -04:00
|
|
|
// Retrieve extensions using custom function
|
2024-09-28 18:13:24 -04:00
|
|
|
std::vector<const char*> extensions = getRequiredExtensions();
|
2024-09-26 19:56:19 -04:00
|
|
|
|
2024-10-01 16:10:20 -04:00
|
|
|
#ifdef __APPLE__
|
2024-09-30 00:31:08 -04:00
|
|
|
// Enable the portability extension and set flags
|
2024-10-01 14:15:39 -04:00
|
|
|
extensions.emplace_back(vk::KHRPortabilityEnumerationExtensionName);
|
|
|
|
// Technically deprecated but vulkan complains if I don't include it for macOS
|
|
|
|
// So instead of using the vk::KHRPortabilitySubsetExtensionName, I just use
|
|
|
|
// the direct string.
|
|
|
|
extensions.emplace_back("VK_KHR_get_physical_device_properties2");
|
2024-09-30 21:21:38 -04:00
|
|
|
#endif
|
2024-09-30 00:31:08 -04:00
|
|
|
|
2024-09-29 23:05:15 -04:00
|
|
|
vk::InstanceCreateInfo createInfo {
|
2024-10-01 16:57:40 -04:00
|
|
|
#ifdef __APPLE__
|
|
|
|
.flags = vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR,
|
|
|
|
#endif
|
2024-09-30 00:31:08 -04:00
|
|
|
.pApplicationInfo = &appInfo,
|
2024-09-30 17:52:49 -04:00
|
|
|
.enabledLayerCount = enableValidationLayers ? static_cast<u32>(validationLayers.size()) : 0,
|
2024-09-30 00:31:08 -04:00
|
|
|
.ppEnabledLayerNames = enableValidationLayers ? validationLayers.data() : nullptr,
|
2024-09-30 17:52:49 -04:00
|
|
|
.enabledExtensionCount = static_cast<u32>(extensions.size()),
|
2024-09-30 00:31:08 -04:00
|
|
|
.ppEnabledExtensionNames = extensions.data()
|
2024-09-29 23:05:15 -04:00
|
|
|
};
|
2024-09-26 17:18:45 -04:00
|
|
|
|
2024-09-30 00:31:08 -04:00
|
|
|
#ifndef NDEBUG
|
2024-09-29 23:02:04 -04:00
|
|
|
fmt::println("Available extensions:");
|
|
|
|
|
|
|
|
for (const char* extension : extensions) fmt::println("\t{}", extension);
|
2024-09-30 00:31:08 -04:00
|
|
|
#endif
|
2024-09-25 23:03:56 -04:00
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
mInstance = vk::createInstanceUnique(createInfo);
|
2024-10-06 17:34:25 -04:00
|
|
|
|
2024-10-06 12:26:44 -04:00
|
|
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(mInstance.get());
|
2024-09-28 14:54:39 -04:00
|
|
|
}
|
|
|
|
|
2024-09-30 00:57:13 -04:00
|
|
|
fn setupDebugMessenger() -> void {
|
2024-09-30 00:31:08 -04:00
|
|
|
if (!enableValidationLayers)
|
|
|
|
return;
|
2024-09-28 18:13:24 -04:00
|
|
|
|
2024-09-30 00:31:08 -04:00
|
|
|
vk::DebugUtilsMessengerCreateInfoEXT messengerCreateInfo {
|
|
|
|
.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose |
|
|
|
|
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
|
|
|
|
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError,
|
|
|
|
.messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
|
|
|
|
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
|
|
|
|
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
|
|
|
|
.pfnUserCallback = debugCallback,
|
|
|
|
};
|
2024-09-28 18:13:24 -04:00
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
mDebugMessenger = mInstance->createDebugUtilsMessengerEXTUnique(messengerCreateInfo, nullptr);
|
2024-09-30 00:31:08 -04:00
|
|
|
}
|
2024-09-28 18:13:24 -04:00
|
|
|
|
2024-09-30 16:46:17 -04:00
|
|
|
fn createSurface() -> void { mSurface = vkfw::createWindowSurfaceUnique(mInstance.get(), mWindow.get()); }
|
2024-09-30 00:57:13 -04:00
|
|
|
|
2024-09-30 00:31:08 -04:00
|
|
|
fn pickPhysicalDevice() -> void {
|
2024-10-06 18:14:15 -04:00
|
|
|
std::vector<vk::PhysicalDevice> devices = mInstance->enumeratePhysicalDevices();
|
2024-09-30 00:31:08 -04:00
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
if (devices.empty())
|
2024-09-30 00:31:08 -04:00
|
|
|
throw std::runtime_error("Failed to find GPUs with Vulkan support!");
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
fmt::println("Available devices:");
|
|
|
|
#endif
|
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
for (const vk::PhysicalDevice& device : devices) {
|
2024-09-30 00:31:08 -04:00
|
|
|
#ifndef NDEBUG
|
|
|
|
vk::PhysicalDeviceProperties properties = device.getProperties();
|
2024-09-30 19:49:44 -04:00
|
|
|
fmt::println("\t{}", properties.deviceName.data());
|
2024-09-30 00:31:08 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (isDeviceSuitable(device)) {
|
|
|
|
mPhysicalDevice = device;
|
|
|
|
break;
|
|
|
|
}
|
2024-09-28 18:13:24 -04:00
|
|
|
}
|
|
|
|
|
2024-09-30 00:31:08 -04:00
|
|
|
if (!mPhysicalDevice)
|
|
|
|
throw std::runtime_error("Failed to find a suitable GPU!");
|
|
|
|
}
|
|
|
|
|
2024-09-30 00:57:13 -04:00
|
|
|
fn createLogicalDevice() -> void {
|
2024-10-10 18:51:20 -04:00
|
|
|
QueueFamilyIndices qfIndices = findQueueFamilies(mPhysicalDevice);
|
2024-09-30 00:57:13 -04:00
|
|
|
|
|
|
|
std::vector<vk::DeviceQueueCreateInfo> queueCreateInfos;
|
2024-10-10 18:51:20 -04:00
|
|
|
std::set<u32> uniqueQueueFamilies = { qfIndices.graphics_family.value(),
|
|
|
|
qfIndices.present_family.value() };
|
2024-09-30 00:57:13 -04:00
|
|
|
|
|
|
|
f32 queuePriority = 1.0F;
|
|
|
|
|
|
|
|
for (u32 queueFamily : uniqueQueueFamilies) {
|
|
|
|
vk::DeviceQueueCreateInfo queueCreateInfo { .queueFamilyIndex = queueFamily,
|
|
|
|
.queueCount = 1,
|
|
|
|
.pQueuePriorities = &queuePriority };
|
|
|
|
|
2024-10-01 14:15:39 -04:00
|
|
|
queueCreateInfos.emplace_back(queueCreateInfo);
|
2024-09-30 00:57:13 -04:00
|
|
|
}
|
|
|
|
|
2024-10-11 00:42:58 -04:00
|
|
|
vk::PhysicalDeviceFeatures deviceFeatures {
|
|
|
|
.samplerAnisotropy = vk::True,
|
|
|
|
};
|
2024-09-30 00:57:13 -04:00
|
|
|
|
|
|
|
vk::DeviceCreateInfo createInfo { .queueCreateInfoCount = static_cast<u32>(queueCreateInfos.size()),
|
|
|
|
.pQueueCreateInfos = queueCreateInfos.data(),
|
2024-10-01 16:10:20 -04:00
|
|
|
.enabledExtensionCount = static_cast<u32>(deviceExtensions.size()),
|
|
|
|
.ppEnabledExtensionNames = deviceExtensions.data(),
|
2024-09-30 00:57:13 -04:00
|
|
|
.pEnabledFeatures = &deviceFeatures };
|
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
mDevice = mPhysicalDevice.createDeviceUnique(createInfo);
|
2024-10-10 18:51:20 -04:00
|
|
|
mGraphicsQueue = mDevice->getQueue(qfIndices.graphics_family.value(), 0);
|
|
|
|
mPresentQueue = mDevice->getQueue(qfIndices.present_family.value(), 0);
|
2024-09-30 00:57:13 -04:00
|
|
|
}
|
|
|
|
|
2024-10-01 16:57:40 -04:00
|
|
|
fn createSwapChain() -> void {
|
|
|
|
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(mPhysicalDevice);
|
|
|
|
|
|
|
|
vk::SurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
|
|
|
|
vk::PresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.present_modes);
|
|
|
|
vk::Extent2D extent = chooseSwapExtent(swapChainSupport.capabilities);
|
|
|
|
|
|
|
|
u32 imageCount = swapChainSupport.capabilities.minImageCount + 1;
|
|
|
|
|
|
|
|
if (swapChainSupport.capabilities.maxImageCount > 0 &&
|
|
|
|
imageCount > swapChainSupport.capabilities.maxImageCount)
|
|
|
|
imageCount = swapChainSupport.capabilities.maxImageCount;
|
|
|
|
|
2024-10-10 18:51:20 -04:00
|
|
|
QueueFamilyIndices qfIndices = findQueueFamilies(mPhysicalDevice);
|
|
|
|
std::array<u32, 2> queueFamilyIndices = { qfIndices.graphics_family.value(),
|
|
|
|
qfIndices.present_family.value() };
|
2024-10-01 16:57:40 -04:00
|
|
|
|
|
|
|
vk::SwapchainCreateInfoKHR createInfo {
|
|
|
|
.surface = mSurface.get(),
|
|
|
|
.minImageCount = imageCount,
|
|
|
|
.imageFormat = surfaceFormat.format,
|
|
|
|
.imageColorSpace = surfaceFormat.colorSpace,
|
|
|
|
.imageExtent = extent,
|
|
|
|
.imageArrayLayers = 1,
|
|
|
|
.imageUsage = vk::ImageUsageFlagBits::eColorAttachment,
|
2024-10-10 18:51:20 -04:00
|
|
|
.imageSharingMode = qfIndices.graphics_family != qfIndices.present_family ? vk::SharingMode::eConcurrent
|
|
|
|
: vk::SharingMode::eExclusive,
|
|
|
|
.queueFamilyIndexCount =
|
|
|
|
static_cast<u32>(qfIndices.graphics_family != qfIndices.present_family ? 2 : 0),
|
2024-10-01 16:57:40 -04:00
|
|
|
.pQueueFamilyIndices =
|
2024-10-10 18:51:20 -04:00
|
|
|
qfIndices.graphics_family != qfIndices.present_family ? queueFamilyIndices.data() : nullptr,
|
2024-10-01 16:57:40 -04:00
|
|
|
.preTransform = swapChainSupport.capabilities.currentTransform,
|
|
|
|
.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque,
|
|
|
|
.presentMode = presentMode,
|
2024-10-01 18:30:31 -04:00
|
|
|
.clipped = vk::True,
|
2024-10-01 16:57:40 -04:00
|
|
|
.oldSwapchain = nullptr,
|
|
|
|
};
|
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
mSwapChain = mDevice->createSwapchainKHRUnique(createInfo);
|
2024-10-06 17:34:25 -04:00
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
mSwapChainImages = mDevice->getSwapchainImagesKHR(mSwapChain.get());
|
2024-10-01 16:57:40 -04:00
|
|
|
mSwapChainImageFormat = surfaceFormat.format;
|
|
|
|
mSwapChainExtent = extent;
|
|
|
|
}
|
|
|
|
|
2024-10-01 17:06:14 -04:00
|
|
|
fn createImageViews() -> void {
|
|
|
|
mSwapChainImageViews.resize(mSwapChainImages.size());
|
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
for (u32 i = 0; i < mSwapChainImages.size(); i++)
|
|
|
|
mSwapChainImageViews[i] =
|
2024-10-11 20:50:23 -04:00
|
|
|
createImageView(mSwapChainImages[i], mSwapChainImageFormat, vk::ImageAspectFlagBits::eColor, 1);
|
2024-10-01 16:10:20 -04:00
|
|
|
}
|
|
|
|
|
2024-10-01 18:30:31 -04:00
|
|
|
fn createRenderPass() -> void {
|
|
|
|
vk::AttachmentDescription colorAttachment {
|
|
|
|
.format = mSwapChainImageFormat,
|
|
|
|
.samples = vk::SampleCountFlagBits::e1,
|
|
|
|
.loadOp = vk::AttachmentLoadOp::eClear,
|
|
|
|
.storeOp = vk::AttachmentStoreOp::eStore,
|
|
|
|
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
|
|
|
|
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
|
|
|
|
.initialLayout = vk::ImageLayout::eUndefined,
|
|
|
|
.finalLayout = vk::ImageLayout::ePresentSrcKHR,
|
|
|
|
};
|
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
vk::AttachmentDescription depthAttachment { .format = findDepthFormat(),
|
|
|
|
.samples = vk::SampleCountFlagBits::e1,
|
|
|
|
.loadOp = vk::AttachmentLoadOp::eClear,
|
|
|
|
.storeOp = vk::AttachmentStoreOp::eDontCare,
|
|
|
|
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
|
|
|
|
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
|
|
|
|
.initialLayout = vk::ImageLayout::eUndefined,
|
|
|
|
.finalLayout =
|
|
|
|
vk::ImageLayout::eDepthStencilAttachmentOptimal };
|
|
|
|
|
2024-10-01 18:30:31 -04:00
|
|
|
vk::AttachmentReference colorAttachmentRef {
|
|
|
|
.attachment = 0,
|
|
|
|
.layout = vk::ImageLayout::eColorAttachmentOptimal,
|
|
|
|
};
|
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
vk::AttachmentReference depthAttachmentRef { .attachment = 1,
|
|
|
|
.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal };
|
|
|
|
|
|
|
|
vk::SubpassDescription subpass { .pipelineBindPoint = vk::PipelineBindPoint::eGraphics,
|
|
|
|
.colorAttachmentCount = 1,
|
|
|
|
.pColorAttachments = &colorAttachmentRef,
|
|
|
|
.pDepthStencilAttachment = &depthAttachmentRef };
|
|
|
|
|
|
|
|
vk::SubpassDependency dependency { .srcSubpass = vk::SubpassExternal,
|
|
|
|
.dstSubpass = {},
|
|
|
|
.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput |
|
|
|
|
vk::PipelineStageFlagBits::eEarlyFragmentTests,
|
|
|
|
.dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput |
|
|
|
|
vk::PipelineStageFlagBits::eEarlyFragmentTests,
|
|
|
|
.srcAccessMask = {},
|
|
|
|
.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite |
|
|
|
|
vk::AccessFlagBits::eDepthStencilAttachmentWrite };
|
|
|
|
|
|
|
|
std::array<vk::AttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
|
|
|
|
|
|
|
|
vk::RenderPassCreateInfo renderPassInfo { .attachmentCount = static_cast<u32>(attachments.size()),
|
|
|
|
.pAttachments = attachments.data(),
|
|
|
|
.subpassCount = 1,
|
|
|
|
.pSubpasses = &subpass,
|
|
|
|
.dependencyCount = 1,
|
|
|
|
.pDependencies = &dependency };
|
2024-10-01 18:30:31 -04:00
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
mRenderPass = mDevice->createRenderPassUnique(renderPassInfo);
|
2024-10-01 18:30:31 -04:00
|
|
|
}
|
|
|
|
|
2024-10-10 20:39:04 -04:00
|
|
|
fn createDescriptorSetLayout() -> void {
|
|
|
|
vk::DescriptorSetLayoutBinding uboLayoutBinding {
|
|
|
|
.binding = 0,
|
|
|
|
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
|
|
|
.descriptorCount = 1,
|
|
|
|
.stageFlags = vk::ShaderStageFlagBits::eVertex,
|
|
|
|
.pImmutableSamplers = nullptr,
|
|
|
|
};
|
|
|
|
|
2024-10-11 00:42:58 -04:00
|
|
|
vk::DescriptorSetLayoutBinding samplerLayoutBinding {
|
|
|
|
.binding = 1,
|
|
|
|
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
|
|
|
.descriptorCount = 1,
|
|
|
|
.stageFlags = vk::ShaderStageFlagBits::eFragment,
|
|
|
|
.pImmutableSamplers = nullptr,
|
|
|
|
};
|
|
|
|
|
|
|
|
std::array<vk::DescriptorSetLayoutBinding, 2> bindings = { uboLayoutBinding, samplerLayoutBinding };
|
|
|
|
|
2024-10-10 20:39:04 -04:00
|
|
|
vk::DescriptorSetLayoutCreateInfo layoutInfo {
|
2024-10-11 00:42:58 -04:00
|
|
|
.bindingCount = static_cast<u32>(bindings.size()),
|
|
|
|
.pBindings = bindings.data(),
|
2024-10-10 20:39:04 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
mDescriptorSetLayout = mDevice->createDescriptorSetLayoutUnique(layoutInfo);
|
|
|
|
}
|
|
|
|
|
2024-10-01 18:30:31 -04:00
|
|
|
fn createGraphicsPipeline() -> void {
|
2024-10-11 20:04:28 -04:00
|
|
|
std::vector<char> vertShaderCode = readFile("shaders/vert.spv");
|
|
|
|
std::vector<char> fragShaderCode = readFile("shaders/frag.spv");
|
2024-10-01 18:30:31 -04:00
|
|
|
|
|
|
|
vk::UniqueShaderModule vertShaderModule = createShaderModule(vertShaderCode);
|
|
|
|
vk::UniqueShaderModule fragShaderModule = createShaderModule(fragShaderCode);
|
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
vk::PipelineShaderStageCreateInfo vertShaderStageInfo { .stage = vk::ShaderStageFlagBits::eVertex,
|
|
|
|
.module = vertShaderModule.get(),
|
|
|
|
.pName = "main" };
|
2024-10-01 18:30:31 -04:00
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
vk::PipelineShaderStageCreateInfo fragShaderStageInfo { .stage = vk::ShaderStageFlagBits::eFragment,
|
|
|
|
.module = fragShaderModule.get(),
|
|
|
|
.pName = "main" };
|
2024-10-01 18:30:31 -04:00
|
|
|
|
|
|
|
std::array<vk::PipelineShaderStageCreateInfo, 2> shaderStages = { vertShaderStageInfo,
|
|
|
|
fragShaderStageInfo };
|
|
|
|
|
2024-10-09 23:00:45 -04:00
|
|
|
vk::VertexInputBindingDescription bindingDescription = Vertex::getBindingDescription();
|
2024-10-11 00:42:58 -04:00
|
|
|
std::array<vk::VertexInputAttributeDescription, 3> attributeDescriptions =
|
2024-10-09 23:00:45 -04:00
|
|
|
Vertex::getAttributeDescriptions();
|
|
|
|
|
2024-10-01 18:30:31 -04:00
|
|
|
vk::PipelineVertexInputStateCreateInfo vertexInputInfo {
|
2024-10-09 23:00:45 -04:00
|
|
|
.vertexBindingDescriptionCount = 1,
|
|
|
|
.pVertexBindingDescriptions = &bindingDescription,
|
|
|
|
.vertexAttributeDescriptionCount = static_cast<u32>(attributeDescriptions.size()),
|
2024-10-11 19:09:37 -04:00
|
|
|
.pVertexAttributeDescriptions = attributeDescriptions.data()
|
2024-10-01 18:30:31 -04:00
|
|
|
};
|
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
vk::PipelineInputAssemblyStateCreateInfo inputAssembly { .topology = vk::PrimitiveTopology::eTriangleList,
|
|
|
|
.primitiveRestartEnable = vk::False };
|
2024-10-01 18:30:31 -04:00
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
vk::PipelineViewportStateCreateInfo viewportState { .viewportCount = 1, .scissorCount = 1 };
|
2024-10-01 18:30:31 -04:00
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
vk::PipelineRasterizationStateCreateInfo rasterizer { .depthClampEnable = vk::False,
|
|
|
|
.rasterizerDiscardEnable = vk::False,
|
|
|
|
.polygonMode = vk::PolygonMode::eFill,
|
|
|
|
.cullMode = vk::CullModeFlagBits::eBack,
|
|
|
|
.frontFace = vk::FrontFace::eCounterClockwise,
|
|
|
|
.depthBiasEnable = vk::False,
|
|
|
|
.lineWidth = 1.0F };
|
2024-10-01 18:30:31 -04:00
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
vk::PipelineMultisampleStateCreateInfo multisampling { .rasterizationSamples =
|
|
|
|
vk::SampleCountFlagBits::e1,
|
|
|
|
.sampleShadingEnable = vk::False };
|
|
|
|
|
|
|
|
vk::PipelineDepthStencilStateCreateInfo depthStencil { .depthTestEnable = vk::True,
|
|
|
|
.depthWriteEnable = vk::True,
|
|
|
|
.depthCompareOp = vk::CompareOp::eLess,
|
|
|
|
.depthBoundsTestEnable = vk::False,
|
|
|
|
.stencilTestEnable = vk::False };
|
2024-10-01 18:30:31 -04:00
|
|
|
|
|
|
|
vk::PipelineColorBlendAttachmentState colorBlendAttachment {
|
|
|
|
.blendEnable = vk::False,
|
|
|
|
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
2024-10-11 19:09:37 -04:00
|
|
|
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
|
2024-10-01 18:30:31 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
vk::PipelineColorBlendStateCreateInfo colorBlending {
|
|
|
|
.logicOpEnable = vk::False,
|
|
|
|
.logicOp = vk::LogicOp::eCopy,
|
|
|
|
.attachmentCount = 1,
|
|
|
|
.pAttachments = &colorBlendAttachment,
|
2024-10-11 19:09:37 -04:00
|
|
|
.blendConstants = std::array<float, 4> { 0.0F, 0.0F, 0.0F, 0.0F }
|
2024-10-01 18:30:31 -04:00
|
|
|
};
|
2024-10-10 20:39:04 -04:00
|
|
|
|
2024-10-01 18:30:31 -04:00
|
|
|
std::vector<vk::DynamicState> dynamicStates = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
|
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
vk::PipelineDynamicStateCreateInfo dynamicState { .dynamicStateCount =
|
|
|
|
static_cast<u32>(dynamicStates.size()),
|
|
|
|
.pDynamicStates = dynamicStates.data() };
|
2024-10-01 18:30:31 -04:00
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
vk::PipelineLayoutCreateInfo pipelineLayoutInfo { .setLayoutCount = 1,
|
|
|
|
.pSetLayouts = &mDescriptorSetLayout.get() };
|
2024-10-01 18:30:31 -04:00
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
mPipelineLayout = mDevice->createPipelineLayoutUnique(pipelineLayoutInfo);
|
2024-10-01 18:30:31 -04:00
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
vk::GraphicsPipelineCreateInfo pipelineInfo { .stageCount = static_cast<u32>(shaderStages.size()),
|
|
|
|
.pStages = shaderStages.data(),
|
|
|
|
.pVertexInputState = &vertexInputInfo,
|
|
|
|
.pInputAssemblyState = &inputAssembly,
|
|
|
|
.pViewportState = &viewportState,
|
|
|
|
.pRasterizationState = &rasterizer,
|
|
|
|
.pMultisampleState = &multisampling,
|
|
|
|
.pDepthStencilState = &depthStencil,
|
|
|
|
.pColorBlendState = &colorBlending,
|
|
|
|
.pDynamicState = &dynamicState,
|
|
|
|
.layout = mPipelineLayout.get(),
|
|
|
|
.renderPass = mRenderPass.get(),
|
|
|
|
.subpass = 0 };
|
2024-10-01 18:30:31 -04:00
|
|
|
|
2024-10-06 17:34:25 -04:00
|
|
|
vk::Result graphicsPipelineResult = vk::Result::eSuccess;
|
|
|
|
vk::UniquePipeline graphicsPipelineValue;
|
|
|
|
|
|
|
|
std::tie(graphicsPipelineResult, graphicsPipelineValue) =
|
|
|
|
mDevice->createGraphicsPipelineUnique(nullptr, pipelineInfo).asTuple();
|
|
|
|
|
|
|
|
if (graphicsPipelineResult != vk::Result::eSuccess)
|
|
|
|
throw std::runtime_error("Failed to create graphics pipeline!");
|
|
|
|
|
|
|
|
mGraphicsPipeline = std::move(graphicsPipelineValue);
|
2024-10-01 18:30:31 -04:00
|
|
|
}
|
|
|
|
|
2024-10-01 18:33:05 -04:00
|
|
|
fn createFramebuffers() -> void {
|
|
|
|
mSwapChainFramebuffers.resize(mSwapChainImageViews.size());
|
|
|
|
|
|
|
|
for (usize i = 0; i < mSwapChainImageViews.size(); i++) {
|
2024-10-11 19:09:37 -04:00
|
|
|
std::array<vk::ImageView, 2> attachments = { mSwapChainImageViews[i].get(), mDepthImageView.get() };
|
|
|
|
|
|
|
|
vk::FramebufferCreateInfo framebufferInfo { .renderPass = mRenderPass.get(),
|
|
|
|
.attachmentCount = static_cast<u32>(attachments.size()),
|
|
|
|
.pAttachments = attachments.data(),
|
|
|
|
.width = mSwapChainExtent.width,
|
|
|
|
.height = mSwapChainExtent.height,
|
|
|
|
.layers = 1 };
|
2024-10-01 18:33:05 -04:00
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
mSwapChainFramebuffers[i] = mDevice->createFramebufferUnique(framebufferInfo);
|
2024-10-01 18:33:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-01 18:54:41 -04:00
|
|
|
fn createCommandPool() -> void {
|
|
|
|
QueueFamilyIndices queueFamilyIndices = findQueueFamilies(mPhysicalDevice);
|
|
|
|
|
|
|
|
vk::CommandPoolCreateInfo poolInfo {
|
|
|
|
.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer,
|
|
|
|
.queueFamilyIndex = queueFamilyIndices.graphics_family.value(),
|
|
|
|
};
|
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
mCommandPool = mDevice->createCommandPoolUnique(poolInfo);
|
2024-10-01 18:54:41 -04:00
|
|
|
}
|
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
fn createDepthResources() -> void {
|
|
|
|
vk::Format depthFormat = findDepthFormat();
|
|
|
|
|
|
|
|
createImage(
|
|
|
|
mSwapChainExtent.width,
|
|
|
|
mSwapChainExtent.height,
|
2024-10-11 20:50:23 -04:00
|
|
|
1,
|
2024-10-11 19:09:37 -04:00
|
|
|
depthFormat,
|
|
|
|
vk::ImageTiling::eOptimal,
|
|
|
|
vk::ImageUsageFlagBits::eDepthStencilAttachment,
|
|
|
|
vk::MemoryPropertyFlagBits::eDeviceLocal,
|
|
|
|
mDepthImage,
|
|
|
|
mDepthImageMemory
|
|
|
|
);
|
|
|
|
|
2024-10-11 20:50:23 -04:00
|
|
|
mDepthImageView = createImageView(mDepthImage.get(), depthFormat, vk::ImageAspectFlagBits::eDepth, 1);
|
2024-10-11 19:09:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn findSupportedFormat(
|
|
|
|
const std::vector<vk::Format>& candidates,
|
|
|
|
vk::ImageTiling tiling,
|
|
|
|
vk::FormatFeatureFlags features
|
|
|
|
) -> vk::Format {
|
|
|
|
for (vk::Format format : candidates) {
|
|
|
|
vk::FormatProperties props = mPhysicalDevice.getFormatProperties(format);
|
|
|
|
if (tiling == vk::ImageTiling::eLinear && (props.linearTilingFeatures & features) == features)
|
|
|
|
return format;
|
|
|
|
if (tiling == vk::ImageTiling::eOptimal && (props.optimalTilingFeatures & features) == features)
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw std::runtime_error("Failed to find supported format!");
|
|
|
|
}
|
|
|
|
|
|
|
|
fn findDepthFormat() -> vk::Format {
|
|
|
|
return findSupportedFormat(
|
|
|
|
{ vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint },
|
|
|
|
vk::ImageTiling::eOptimal,
|
|
|
|
vk::FormatFeatureFlagBits::eDepthStencilAttachment
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
static fn hasStencilComponent(vk::Format format) {
|
|
|
|
return format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint;
|
|
|
|
}
|
|
|
|
|
2024-10-11 00:42:58 -04:00
|
|
|
fn createTextureImage() -> void {
|
2024-10-11 00:45:36 -04:00
|
|
|
i32 texWidth = 0, texHeight = 0, texChannels = 0;
|
2024-10-11 20:50:23 -04:00
|
|
|
|
2024-10-11 20:04:28 -04:00
|
|
|
u8* pixels = stbi_load(TEXTURE_PATH, &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
|
2024-10-11 00:42:58 -04:00
|
|
|
|
|
|
|
vk::DeviceSize imageSize =
|
|
|
|
static_cast<vk::DeviceSize>(texWidth) * static_cast<vk::DeviceSize>(texHeight) * 4;
|
|
|
|
|
2024-10-11 20:50:23 -04:00
|
|
|
mMipLevels = static_cast<u32>(std::floor(std::log2(std::max(texWidth, texHeight)))) + 1;
|
|
|
|
|
2024-10-11 00:42:58 -04:00
|
|
|
if (!pixels)
|
|
|
|
throw std::runtime_error("Failed to load texture image!");
|
|
|
|
|
|
|
|
vk::UniqueBuffer stagingBuffer;
|
|
|
|
vk::UniqueDeviceMemory stagingBufferMemory;
|
|
|
|
|
|
|
|
createBuffer(
|
|
|
|
imageSize,
|
|
|
|
vk::BufferUsageFlagBits::eTransferSrc,
|
|
|
|
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
|
|
|
|
stagingBuffer,
|
|
|
|
stagingBufferMemory
|
|
|
|
);
|
|
|
|
|
|
|
|
copyData(stagingBufferMemory.get(), imageSize, pixels);
|
|
|
|
|
|
|
|
stbi_image_free(pixels);
|
|
|
|
|
|
|
|
createImage(
|
|
|
|
static_cast<u32>(texWidth),
|
|
|
|
static_cast<u32>(texHeight),
|
2024-10-11 20:50:23 -04:00
|
|
|
mMipLevels,
|
2024-10-11 00:42:58 -04:00
|
|
|
vk::Format::eR8G8B8A8Srgb,
|
|
|
|
vk::ImageTiling::eOptimal,
|
2024-10-11 20:50:23 -04:00
|
|
|
vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst |
|
|
|
|
vk::ImageUsageFlagBits::eSampled,
|
2024-10-11 00:42:58 -04:00
|
|
|
vk::MemoryPropertyFlagBits::eDeviceLocal,
|
|
|
|
mTextureImage,
|
|
|
|
mTextureImageMemory
|
|
|
|
);
|
|
|
|
|
|
|
|
transitionImageLayout(
|
2024-10-11 20:50:23 -04:00
|
|
|
mTextureImage.get(), vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, mMipLevels
|
2024-10-11 00:42:58 -04:00
|
|
|
);
|
|
|
|
|
|
|
|
copyBufferToImage(
|
|
|
|
stagingBuffer.get(), mTextureImage.get(), static_cast<u32>(texWidth), static_cast<u32>(texHeight)
|
|
|
|
);
|
|
|
|
|
2024-10-11 20:50:23 -04:00
|
|
|
generateMipmaps(mTextureImage.get(), vk::Format::eR8G8B8A8Srgb, texWidth, texHeight, mMipLevels);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn generateMipmaps(vk::Image image, vk::Format imageFormat, i32 texWidth, i32 texHeight, u32 mipLevels)
|
|
|
|
-> void {
|
|
|
|
vk::FormatProperties formatProperties = mPhysicalDevice.getFormatProperties(imageFormat);
|
|
|
|
|
|
|
|
if (!(formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImageFilterLinear))
|
|
|
|
throw std::runtime_error("Texture image format does not support linear blitting!");
|
|
|
|
|
|
|
|
vk::UniqueCommandBuffer commandBuffer = beginSingleTimeCommands();
|
|
|
|
|
|
|
|
vk::ImageMemoryBarrier barrier {
|
|
|
|
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
|
|
|
|
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
|
|
|
|
.image = image,
|
|
|
|
.subresourceRange = { .aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
|
|
.levelCount = 1,
|
|
|
|
.baseArrayLayer = 0,
|
|
|
|
.layerCount = 1 }
|
|
|
|
};
|
|
|
|
|
|
|
|
i32 mipWidth = texWidth;
|
|
|
|
i32 mipHeight = texHeight;
|
|
|
|
|
|
|
|
for (u32 i = 1; i < mipLevels; i++) {
|
|
|
|
barrier.subresourceRange.baseMipLevel = i - 1;
|
|
|
|
barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
|
|
|
|
barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
|
|
|
|
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
|
|
|
barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
|
|
|
|
|
|
|
|
commandBuffer->pipelineBarrier(
|
|
|
|
vk::PipelineStageFlagBits::eTransfer,
|
|
|
|
vk::PipelineStageFlagBits::eTransfer,
|
|
|
|
{},
|
|
|
|
nullptr,
|
|
|
|
nullptr,
|
|
|
|
barrier
|
|
|
|
);
|
|
|
|
|
|
|
|
vk::ImageBlit blit {
|
|
|
|
.srcSubresource = { .aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
|
|
.mipLevel = i - 1,
|
|
|
|
.baseArrayLayer = 0,
|
|
|
|
.layerCount = 1 },
|
|
|
|
.srcOffsets = std::array<vk::Offset3D, 2> { { { 0, 0, 0 }, { mipWidth, mipHeight, 1 } } },
|
|
|
|
.dstSubresource = { .aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
|
|
.mipLevel = i,
|
|
|
|
.baseArrayLayer = 0,
|
|
|
|
.layerCount = 1 },
|
|
|
|
.dstOffsets =
|
|
|
|
std::array<vk::Offset3D, 2> {
|
|
|
|
{ { 0, 0, 0 }, { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 } } }
|
|
|
|
};
|
|
|
|
|
|
|
|
commandBuffer->blitImage(
|
|
|
|
image,
|
|
|
|
vk::ImageLayout::eTransferSrcOptimal,
|
|
|
|
image,
|
|
|
|
vk::ImageLayout::eTransferDstOptimal,
|
|
|
|
blit,
|
|
|
|
vk::Filter::eLinear
|
|
|
|
);
|
|
|
|
|
|
|
|
barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal;
|
|
|
|
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
|
|
|
barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
|
|
|
|
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
|
|
|
|
|
|
|
|
commandBuffer->pipelineBarrier(
|
|
|
|
vk::PipelineStageFlagBits::eTransfer,
|
|
|
|
vk::PipelineStageFlagBits::eFragmentShader,
|
|
|
|
{},
|
|
|
|
nullptr,
|
|
|
|
nullptr,
|
|
|
|
barrier
|
|
|
|
);
|
|
|
|
|
|
|
|
if (mipWidth > 1)
|
|
|
|
mipWidth /= 2;
|
|
|
|
if (mipHeight > 1)
|
|
|
|
mipHeight /= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
barrier.subresourceRange.baseMipLevel = mMipLevels - 1;
|
|
|
|
barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
|
|
|
|
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
|
|
|
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
|
|
|
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
|
|
|
|
|
|
|
|
commandBuffer->pipelineBarrier(
|
|
|
|
vk::PipelineStageFlagBits::eTransfer,
|
|
|
|
vk::PipelineStageFlagBits::eFragmentShader,
|
|
|
|
{},
|
|
|
|
nullptr,
|
|
|
|
nullptr,
|
|
|
|
barrier
|
2024-10-11 00:42:58 -04:00
|
|
|
);
|
2024-10-11 20:50:23 -04:00
|
|
|
|
|
|
|
endSingleTimeCommands(std::move(commandBuffer));
|
2024-10-11 00:42:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn createTextureImageView() -> void {
|
2024-10-11 20:50:23 -04:00
|
|
|
mTextureImageView = createImageView(
|
|
|
|
mTextureImage.get(), vk::Format::eR8G8B8A8Srgb, vk::ImageAspectFlagBits::eColor, mMipLevels
|
|
|
|
);
|
2024-10-11 00:42:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn createTextureSampler() -> void {
|
|
|
|
vk::PhysicalDeviceProperties properties = mPhysicalDevice.getProperties();
|
|
|
|
|
|
|
|
vk::SamplerCreateInfo samplerInfo {
|
|
|
|
.magFilter = vk::Filter::eLinear,
|
|
|
|
.minFilter = vk::Filter::eLinear,
|
|
|
|
.mipmapMode = vk::SamplerMipmapMode::eLinear,
|
|
|
|
.addressModeU = vk::SamplerAddressMode::eRepeat,
|
|
|
|
.addressModeV = vk::SamplerAddressMode::eRepeat,
|
|
|
|
.addressModeW = vk::SamplerAddressMode::eRepeat,
|
|
|
|
.mipLodBias = 0.0F,
|
|
|
|
.anisotropyEnable = vk::False,
|
|
|
|
.maxAnisotropy = properties.limits.maxSamplerAnisotropy,
|
|
|
|
.compareEnable = vk::False,
|
|
|
|
.compareOp = vk::CompareOp::eAlways,
|
|
|
|
.minLod = 0.0F,
|
2024-10-11 20:50:23 -04:00
|
|
|
.maxLod = static_cast<f32>(mMipLevels),
|
2024-10-11 00:42:58 -04:00
|
|
|
.borderColor = vk::BorderColor::eIntOpaqueBlack,
|
|
|
|
.unnormalizedCoordinates = vk::False,
|
|
|
|
};
|
|
|
|
|
|
|
|
mTextureSampler = mDevice->createSamplerUnique(samplerInfo);
|
|
|
|
}
|
|
|
|
|
2024-10-11 20:50:23 -04:00
|
|
|
fn createImageView(vk::Image image, vk::Format format, vk::ImageAspectFlags aspectFlags, u32 mipLevels)
|
2024-10-11 00:42:58 -04:00
|
|
|
-> vk::UniqueImageView {
|
|
|
|
vk::ImageViewCreateInfo viewInfo {
|
|
|
|
.image = image, .viewType = vk::ImageViewType::e2D, .format = format, .subresourceRange = {
|
|
|
|
.aspectMask = aspectFlags,
|
|
|
|
.baseMipLevel = 0,
|
2024-10-11 20:50:23 -04:00
|
|
|
.levelCount = mipLevels,
|
2024-10-11 00:42:58 -04:00
|
|
|
.baseArrayLayer = 0,
|
|
|
|
.layerCount = 1,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return mDevice->createImageViewUnique(viewInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn createImage(
|
|
|
|
u32 width,
|
|
|
|
u32 height,
|
2024-10-11 20:50:23 -04:00
|
|
|
u32 mipLevels,
|
2024-10-11 00:42:58 -04:00
|
|
|
vk::Format format,
|
|
|
|
vk::ImageTiling tiling,
|
|
|
|
vk::ImageUsageFlags usage,
|
|
|
|
vk::MemoryPropertyFlags properties,
|
|
|
|
vk::UniqueImage& image,
|
|
|
|
vk::UniqueDeviceMemory& imageMemory
|
|
|
|
) -> void {
|
|
|
|
vk::ImageCreateInfo imageInfo {
|
|
|
|
.imageType = vk::ImageType::e2D,
|
|
|
|
.format = format,
|
|
|
|
.extent = { .width = width, .height = height, .depth = 1 },
|
2024-10-11 20:50:23 -04:00
|
|
|
.mipLevels = mipLevels,
|
2024-10-11 00:42:58 -04:00
|
|
|
.arrayLayers = 1,
|
|
|
|
.samples = vk::SampleCountFlagBits::e1,
|
|
|
|
.tiling = tiling,
|
|
|
|
.usage = usage,
|
|
|
|
.sharingMode = vk::SharingMode::eExclusive,
|
|
|
|
.initialLayout = vk::ImageLayout::eUndefined,
|
|
|
|
};
|
|
|
|
|
|
|
|
image = mDevice->createImageUnique(imageInfo);
|
|
|
|
|
|
|
|
vk::MemoryRequirements memRequirements = mDevice->getImageMemoryRequirements(image.get());
|
|
|
|
|
|
|
|
vk::MemoryAllocateInfo allocInfo {
|
|
|
|
.allocationSize = memRequirements.size,
|
|
|
|
.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties),
|
|
|
|
};
|
|
|
|
|
|
|
|
imageMemory = mDevice->allocateMemoryUnique(allocInfo);
|
|
|
|
|
|
|
|
mDevice->bindImageMemory(image.get(), imageMemory.get(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn transitionImageLayout(
|
|
|
|
vk::Image image,
|
|
|
|
vk::ImageLayout oldLayout,
|
2024-10-11 20:50:23 -04:00
|
|
|
vk::ImageLayout newLayout,
|
|
|
|
u32 mipLevels
|
2024-10-11 00:42:58 -04:00
|
|
|
) -> void {
|
|
|
|
vk::UniqueCommandBuffer commandBuffer = beginSingleTimeCommands();
|
|
|
|
|
|
|
|
vk::ImageMemoryBarrier barrier {
|
|
|
|
.oldLayout = oldLayout,
|
|
|
|
.newLayout = newLayout,
|
|
|
|
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
|
|
|
|
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
|
|
|
|
.image = image,
|
|
|
|
// clang-format off
|
|
|
|
.subresourceRange = { .aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
|
|
.baseMipLevel = 0,
|
2024-10-11 20:50:23 -04:00
|
|
|
.levelCount = mipLevels,
|
2024-10-11 00:42:58 -04:00
|
|
|
.baseArrayLayer = 0,
|
2024-10-11 19:09:37 -04:00
|
|
|
.layerCount = 1 }
|
2024-10-11 00:42:58 -04:00
|
|
|
// clang-format on
|
|
|
|
};
|
|
|
|
|
|
|
|
vk::PipelineStageFlags sourceStage;
|
|
|
|
vk::PipelineStageFlags destinationStage;
|
|
|
|
|
|
|
|
if (oldLayout == vk::ImageLayout::eUndefined && newLayout == vk::ImageLayout::eTransferDstOptimal) {
|
|
|
|
barrier.srcAccessMask = {};
|
|
|
|
barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
|
|
|
|
|
|
|
|
sourceStage = vk::PipelineStageFlagBits::eTopOfPipe;
|
|
|
|
destinationStage = vk::PipelineStageFlagBits::eTransfer;
|
|
|
|
} else if (oldLayout == vk::ImageLayout::eTransferDstOptimal &&
|
|
|
|
newLayout == vk::ImageLayout::eShaderReadOnlyOptimal) {
|
|
|
|
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
|
|
|
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
|
|
|
|
|
|
|
|
sourceStage = vk::PipelineStageFlagBits::eTransfer;
|
|
|
|
destinationStage = vk::PipelineStageFlagBits::eFragmentShader;
|
|
|
|
} else {
|
|
|
|
throw std::invalid_argument("Unsupported layout transition!");
|
|
|
|
}
|
|
|
|
|
|
|
|
commandBuffer->pipelineBarrier(sourceStage, destinationStage, {}, {}, {}, barrier);
|
|
|
|
|
|
|
|
endSingleTimeCommands(std::move(commandBuffer));
|
|
|
|
}
|
|
|
|
|
|
|
|
fn copyBufferToImage(vk::Buffer buffer, vk::Image image, u32 width, u32 height) -> void {
|
|
|
|
vk::UniqueCommandBuffer commandBuffer = beginSingleTimeCommands();
|
|
|
|
|
|
|
|
vk::BufferImageCopy region {
|
|
|
|
.bufferOffset = 0,
|
|
|
|
.bufferRowLength = 0,
|
|
|
|
.bufferImageHeight = 0,
|
|
|
|
.imageSubresource = { .aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
|
|
.mipLevel = 0,
|
|
|
|
.baseArrayLayer = 0,
|
|
|
|
.layerCount = 1 },
|
|
|
|
.imageOffset = { 0, 0, 0 },
|
|
|
|
.imageExtent = { width, height, 1 },
|
|
|
|
};
|
|
|
|
|
|
|
|
commandBuffer->copyBufferToImage(buffer, image, vk::ImageLayout::eTransferDstOptimal, 1, ®ion);
|
|
|
|
|
|
|
|
endSingleTimeCommands(std::move(commandBuffer));
|
|
|
|
}
|
|
|
|
|
2024-10-11 20:04:28 -04:00
|
|
|
fn loadModel() -> void {
|
|
|
|
tinyobj::attrib_t attrib;
|
|
|
|
std::vector<tinyobj::shape_t> shapes;
|
|
|
|
std::vector<tinyobj::material_t> materials;
|
|
|
|
std::string warn, err;
|
|
|
|
|
|
|
|
if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, MODEL_PATH))
|
|
|
|
throw std::runtime_error(warn + err);
|
|
|
|
|
|
|
|
std::unordered_map<Vertex, u32> uniqueVertices {};
|
|
|
|
|
|
|
|
for (const tinyobj::shape_t& shape : shapes) {
|
|
|
|
for (const tinyobj::index_t& index : shape.mesh.indices) {
|
|
|
|
Vertex vertex {
|
|
|
|
.pos = {
|
|
|
|
attrib.vertices[static_cast<u32>(3 * index.vertex_index + 0)],
|
|
|
|
attrib.vertices[static_cast<u32>(3 * index.vertex_index + 1)],
|
|
|
|
attrib.vertices[static_cast<u32>(3 * index.vertex_index + 2)],
|
|
|
|
},
|
|
|
|
.color = { 1.0F, 1.0F, 1.0F },
|
|
|
|
.tex_coord = {
|
|
|
|
attrib.texcoords[static_cast<u32>(2 * index.texcoord_index + 0)],
|
|
|
|
1.0F - attrib.texcoords[static_cast<u32>(2 * index.texcoord_index + 1)],
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!uniqueVertices.contains(vertex)) {
|
|
|
|
uniqueVertices[vertex] = static_cast<u32>(mVertices.size());
|
|
|
|
mVertices.push_back(vertex);
|
|
|
|
}
|
|
|
|
|
|
|
|
mIndices.push_back(uniqueVertices[vertex]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-09 23:00:45 -04:00
|
|
|
fn createVertexBuffer() -> void {
|
2024-10-11 20:04:28 -04:00
|
|
|
vk::DeviceSize bufferSize = sizeof(mVertices[0]) * mVertices.size();
|
2024-10-10 16:04:46 -04:00
|
|
|
|
|
|
|
vk::UniqueBuffer stagingBuffer;
|
|
|
|
vk::UniqueDeviceMemory stagingBufferMemory;
|
|
|
|
|
|
|
|
createBuffer(
|
|
|
|
bufferSize,
|
2024-10-10 20:39:04 -04:00
|
|
|
vk::BufferUsageFlagBits::eTransferSrc,
|
2024-10-10 16:04:46 -04:00
|
|
|
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
|
|
|
|
stagingBuffer,
|
|
|
|
stagingBufferMemory
|
|
|
|
);
|
|
|
|
|
2024-10-11 20:04:28 -04:00
|
|
|
copyData(stagingBufferMemory.get(), bufferSize, mVertices.data());
|
2024-10-10 16:04:46 -04:00
|
|
|
|
|
|
|
createBuffer(
|
|
|
|
bufferSize,
|
|
|
|
vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst,
|
|
|
|
vk::MemoryPropertyFlagBits::eDeviceLocal,
|
|
|
|
mVertexBuffer,
|
|
|
|
mVertexBufferMemory
|
|
|
|
);
|
|
|
|
|
|
|
|
copyBuffer(stagingBuffer.get(), mVertexBuffer.get(), bufferSize);
|
|
|
|
|
|
|
|
stagingBuffer.reset();
|
|
|
|
stagingBufferMemory.reset();
|
|
|
|
}
|
|
|
|
|
2024-10-10 18:51:20 -04:00
|
|
|
fn createIndexBuffer() -> void {
|
2024-10-11 20:04:28 -04:00
|
|
|
vk::DeviceSize bufferSize = sizeof(mIndices[0]) * mIndices.size();
|
2024-10-10 18:51:20 -04:00
|
|
|
|
|
|
|
vk::UniqueBuffer stagingBuffer;
|
|
|
|
vk::UniqueDeviceMemory stagingBufferMemory;
|
|
|
|
|
|
|
|
createBuffer(
|
|
|
|
bufferSize,
|
|
|
|
vk::BufferUsageFlagBits::eTransferSrc,
|
|
|
|
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
|
|
|
|
stagingBuffer,
|
|
|
|
stagingBufferMemory
|
|
|
|
);
|
|
|
|
|
2024-10-11 20:04:28 -04:00
|
|
|
copyData(stagingBufferMemory.get(), bufferSize, mIndices.data());
|
2024-10-10 18:51:20 -04:00
|
|
|
|
|
|
|
createBuffer(
|
|
|
|
bufferSize,
|
|
|
|
vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst,
|
|
|
|
vk::MemoryPropertyFlagBits::eDeviceLocal,
|
|
|
|
mIndexBuffer,
|
|
|
|
mIndexBufferMemory
|
|
|
|
);
|
|
|
|
|
|
|
|
copyBuffer(stagingBuffer.get(), mIndexBuffer.get(), bufferSize);
|
|
|
|
|
|
|
|
stagingBuffer.reset();
|
|
|
|
stagingBufferMemory.reset();
|
|
|
|
}
|
|
|
|
|
2024-10-10 20:39:04 -04:00
|
|
|
fn createUniformBuffers() -> void {
|
|
|
|
vk::DeviceSize bufferSize = sizeof(UniformBufferObject);
|
|
|
|
|
|
|
|
mUniformBuffers.resize(MAX_FRAMES_IN_FLIGHT);
|
|
|
|
mUniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT);
|
|
|
|
mUniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT);
|
|
|
|
|
|
|
|
for (usize i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
|
|
|
createBuffer(
|
|
|
|
bufferSize,
|
|
|
|
vk::BufferUsageFlagBits::eUniformBuffer,
|
|
|
|
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
|
|
|
|
mUniformBuffers[i],
|
|
|
|
mUniformBuffersMemory[i]
|
|
|
|
);
|
|
|
|
|
|
|
|
mUniformBuffersMapped[i] = mDevice->mapMemory(mUniformBuffersMemory[i].get(), 0, bufferSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn createDescriptorPool() -> void {
|
2024-10-11 00:42:58 -04:00
|
|
|
std::array<vk::DescriptorPoolSize, 2> poolSizes = {
|
|
|
|
{ { .type = vk::DescriptorType::eUniformBuffer, .descriptorCount = MAX_FRAMES_IN_FLIGHT },
|
|
|
|
{ .type = vk::DescriptorType::eCombinedImageSampler, .descriptorCount = MAX_FRAMES_IN_FLIGHT } },
|
2024-10-10 20:39:04 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
vk::DescriptorPoolCreateInfo poolInfo {
|
2024-10-11 00:42:58 -04:00
|
|
|
.maxSets = MAX_FRAMES_IN_FLIGHT,
|
|
|
|
.poolSizeCount = static_cast<u32>(poolSizes.size()),
|
|
|
|
.pPoolSizes = poolSizes.data(),
|
2024-10-10 20:39:04 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
mDescriptorPool = mDevice->createDescriptorPoolUnique(poolInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn createDescriptorSets() -> void {
|
|
|
|
std::vector<vk::DescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, mDescriptorSetLayout.get());
|
|
|
|
|
|
|
|
vk::DescriptorSetAllocateInfo allocInfo {
|
|
|
|
.descriptorPool = mDescriptorPool.get(),
|
|
|
|
.descriptorSetCount = static_cast<u32>(MAX_FRAMES_IN_FLIGHT),
|
|
|
|
.pSetLayouts = layouts.data(),
|
|
|
|
};
|
|
|
|
|
|
|
|
mDescriptorSets = mDevice->allocateDescriptorSets(allocInfo);
|
|
|
|
|
|
|
|
for (usize i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
|
|
|
vk::DescriptorBufferInfo bufferInfo {
|
|
|
|
.buffer = mUniformBuffers[i].get(),
|
|
|
|
.offset = 0,
|
|
|
|
.range = sizeof(UniformBufferObject),
|
|
|
|
};
|
|
|
|
|
2024-10-11 00:42:58 -04:00
|
|
|
vk::DescriptorImageInfo imageInfo {
|
|
|
|
.sampler = mTextureSampler.get(),
|
|
|
|
.imageView = mTextureImageView.get(),
|
|
|
|
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
|
|
|
};
|
|
|
|
|
|
|
|
std::array<vk::WriteDescriptorSet, 2> descriptorWrites = {
|
|
|
|
{ {
|
|
|
|
.dstSet = mDescriptorSets[i],
|
|
|
|
.dstBinding = 0,
|
|
|
|
.dstArrayElement = 0,
|
|
|
|
.descriptorCount = 1,
|
|
|
|
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
|
|
|
.pBufferInfo = &bufferInfo,
|
|
|
|
}, {
|
|
|
|
.dstSet = mDescriptorSets[i],
|
|
|
|
.dstBinding = 1,
|
|
|
|
.dstArrayElement = 0,
|
|
|
|
.descriptorCount = 1,
|
|
|
|
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
|
|
|
.pImageInfo = &imageInfo,
|
|
|
|
} }
|
2024-10-10 20:39:04 -04:00
|
|
|
};
|
|
|
|
|
2024-10-11 00:42:58 -04:00
|
|
|
mDevice->updateDescriptorSets(descriptorWrites, {});
|
2024-10-10 20:39:04 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-10 16:04:46 -04:00
|
|
|
fn createBuffer(
|
|
|
|
vk::DeviceSize deviceSize,
|
|
|
|
vk::BufferUsageFlags bufferUsageFlags,
|
|
|
|
vk::MemoryPropertyFlags memoryPropertyFlags,
|
|
|
|
vk::UniqueBuffer& buffer,
|
|
|
|
vk::UniqueDeviceMemory& bufferMemory
|
|
|
|
) -> void {
|
2024-10-09 23:00:45 -04:00
|
|
|
vk::BufferCreateInfo bufferInfo {
|
2024-10-10 16:04:46 -04:00
|
|
|
.size = deviceSize,
|
|
|
|
.usage = bufferUsageFlags,
|
2024-10-09 23:00:45 -04:00
|
|
|
.sharingMode = vk::SharingMode::eExclusive,
|
|
|
|
};
|
|
|
|
|
2024-10-10 16:04:46 -04:00
|
|
|
buffer = mDevice->createBufferUnique(bufferInfo);
|
2024-10-09 23:00:45 -04:00
|
|
|
|
2024-10-10 16:04:46 -04:00
|
|
|
vk::MemoryRequirements memRequirements = mDevice->getBufferMemoryRequirements(buffer.get());
|
2024-10-09 23:00:45 -04:00
|
|
|
|
|
|
|
vk::MemoryAllocateInfo allocInfo {
|
|
|
|
.allocationSize = memRequirements.size,
|
2024-10-10 16:04:46 -04:00
|
|
|
.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, memoryPropertyFlags),
|
|
|
|
};
|
|
|
|
|
|
|
|
bufferMemory = mDevice->allocateMemoryUnique(allocInfo);
|
|
|
|
|
|
|
|
mDevice->bindBufferMemory(buffer.get(), bufferMemory.get(), 0);
|
|
|
|
}
|
|
|
|
|
2024-10-11 00:42:58 -04:00
|
|
|
fn beginSingleTimeCommands() -> vk::UniqueCommandBuffer {
|
2024-10-10 16:04:46 -04:00
|
|
|
vk::CommandBufferAllocateInfo allocInfo {
|
|
|
|
.commandPool = mCommandPool.get(),
|
|
|
|
.level = vk::CommandBufferLevel::ePrimary,
|
|
|
|
.commandBufferCount = 1,
|
2024-10-09 23:00:45 -04:00
|
|
|
};
|
|
|
|
|
2024-10-10 16:04:46 -04:00
|
|
|
vk::UniqueCommandBuffer commandBuffer = std::move(mDevice->allocateCommandBuffersUnique(allocInfo)[0]);
|
|
|
|
|
|
|
|
vk::CommandBufferBeginInfo beginInfo { .flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit };
|
|
|
|
|
|
|
|
commandBuffer->begin(beginInfo);
|
|
|
|
|
2024-10-11 00:42:58 -04:00
|
|
|
return commandBuffer;
|
|
|
|
}
|
2024-10-10 16:04:46 -04:00
|
|
|
|
2024-10-11 00:42:58 -04:00
|
|
|
fn endSingleTimeCommands(vk::UniqueCommandBuffer commandBuffer) -> void {
|
2024-10-10 16:04:46 -04:00
|
|
|
commandBuffer->end();
|
2024-10-09 23:00:45 -04:00
|
|
|
|
2024-10-10 16:04:46 -04:00
|
|
|
vk::SubmitInfo submitInfo { .commandBufferCount = 1, .pCommandBuffers = &commandBuffer.get() };
|
2024-10-09 23:00:45 -04:00
|
|
|
|
2024-10-10 16:04:46 -04:00
|
|
|
mGraphicsQueue.submit(submitInfo, nullptr);
|
|
|
|
mGraphicsQueue.waitIdle();
|
2024-10-09 23:00:45 -04:00
|
|
|
}
|
|
|
|
|
2024-10-11 00:42:58 -04:00
|
|
|
fn copyData(vk::DeviceMemory stagingBufferMemory, vk::DeviceSize bufferSize, const void* src) -> void {
|
|
|
|
void* data = mDevice->mapMemory(stagingBufferMemory, 0, bufferSize);
|
|
|
|
memcpy(data, src, static_cast<usize>(bufferSize));
|
|
|
|
mDevice->unmapMemory(stagingBufferMemory);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn copyBuffer(vk::Buffer srcBuffer, vk::Buffer dstBuffer, vk::DeviceSize deviceSize) -> void {
|
|
|
|
vk::UniqueCommandBuffer commandBuffer = beginSingleTimeCommands();
|
|
|
|
|
|
|
|
vk::BufferCopy copyRegion { .size = deviceSize };
|
|
|
|
|
|
|
|
commandBuffer->copyBuffer(srcBuffer, dstBuffer, 1, ©Region);
|
|
|
|
|
|
|
|
endSingleTimeCommands(std::move(commandBuffer));
|
|
|
|
}
|
|
|
|
|
2024-10-09 23:00:45 -04:00
|
|
|
fn findMemoryType(u32 typeFilter, vk::MemoryPropertyFlags properties) -> u32 {
|
|
|
|
vk::PhysicalDeviceMemoryProperties memProperties = mPhysicalDevice.getMemoryProperties();
|
|
|
|
|
|
|
|
for (u32 i = 0; i < memProperties.memoryTypeCount; i++)
|
|
|
|
if ((typeFilter & (1 << i)) &&
|
|
|
|
(memProperties.memoryTypes.at(i).propertyFlags & properties) == properties)
|
|
|
|
return i;
|
|
|
|
|
|
|
|
throw std::runtime_error("Failed to find a suitable memory type!");
|
|
|
|
}
|
|
|
|
|
2024-10-05 23:08:12 -04:00
|
|
|
fn createCommandBuffers() -> void {
|
|
|
|
mCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
|
|
|
|
|
|
|
|
vk::CommandBufferAllocateInfo allocInfo { .commandPool = mCommandPool.get(),
|
|
|
|
.level = vk::CommandBufferLevel::ePrimary,
|
|
|
|
.commandBufferCount =
|
|
|
|
static_cast<u32>(mCommandBuffers.size()) };
|
2024-10-01 18:54:41 -04:00
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
mCommandBuffers = mDevice->allocateCommandBuffersUnique(allocInfo);
|
2024-10-01 18:54:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn recordCommandBuffer(vk::CommandBuffer commandBuffer, u32 imageIndex) -> void {
|
|
|
|
vk::CommandBufferBeginInfo beginInfo {};
|
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
commandBuffer.begin(beginInfo);
|
2024-10-01 18:54:41 -04:00
|
|
|
|
2024-10-11 19:09:37 -04:00
|
|
|
std::array<vk::ClearValue, 2> clearValues {
|
|
|
|
{ { .color = { .float32 = std::array<float, 4> { 0.0F, 0.0F, 0.0F, 1.0F } } },
|
|
|
|
{ .depthStencil = { .depth = 1.0F, .stencil = 0 } } }
|
|
|
|
};
|
2024-10-01 18:54:41 -04:00
|
|
|
|
|
|
|
vk::RenderPassBeginInfo renderPassInfo {
|
|
|
|
.renderPass = mRenderPass.get(),
|
|
|
|
.framebuffer = mSwapChainFramebuffers[imageIndex].get(),
|
2024-10-01 19:00:31 -04:00
|
|
|
.renderArea = { .offset = { .x = 0, .y = 0 }, .extent = mSwapChainExtent },
|
2024-10-11 19:09:37 -04:00
|
|
|
.clearValueCount = static_cast<u32>(clearValues.size()),
|
|
|
|
.pClearValues = clearValues.data()
|
2024-10-01 18:54:41 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
commandBuffer.beginRenderPass(renderPassInfo, vk::SubpassContents::eInline);
|
|
|
|
commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, mGraphicsPipeline.get());
|
|
|
|
|
|
|
|
vk::Viewport viewport {
|
|
|
|
.x = 0.0F,
|
|
|
|
.y = 0.0F,
|
|
|
|
.width = static_cast<f32>(mSwapChainExtent.width),
|
|
|
|
.height = static_cast<f32>(mSwapChainExtent.height),
|
|
|
|
.minDepth = 0.0F,
|
|
|
|
.maxDepth = 1.0F,
|
|
|
|
};
|
2024-10-09 23:00:45 -04:00
|
|
|
|
2024-10-01 18:54:41 -04:00
|
|
|
vk::Rect2D scissor {
|
|
|
|
.offset = { 0, 0 },
|
|
|
|
.extent = mSwapChainExtent,
|
|
|
|
};
|
|
|
|
|
2024-10-01 19:00:31 -04:00
|
|
|
commandBuffer.setViewport(0, viewport);
|
2024-10-01 18:54:41 -04:00
|
|
|
commandBuffer.setScissor(0, scissor);
|
|
|
|
|
2024-10-09 23:00:45 -04:00
|
|
|
commandBuffer.bindVertexBuffers(0, mVertexBuffer.get(), { 0 });
|
|
|
|
|
2024-10-11 20:04:28 -04:00
|
|
|
commandBuffer.bindIndexBuffer(mIndexBuffer.get(), 0, vk::IndexType::eUint32);
|
2024-10-10 18:51:20 -04:00
|
|
|
|
2024-10-10 20:39:04 -04:00
|
|
|
commandBuffer.bindDescriptorSets(
|
|
|
|
vk::PipelineBindPoint::eGraphics,
|
|
|
|
mPipelineLayout.get(),
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
&mDescriptorSets[mCurrentFrame],
|
|
|
|
0,
|
|
|
|
nullptr
|
|
|
|
);
|
|
|
|
|
2024-10-11 20:04:28 -04:00
|
|
|
commandBuffer.drawIndexed(static_cast<u32>(mIndices.size()), 1, 0, 0, 0);
|
2024-10-01 18:54:41 -04:00
|
|
|
|
|
|
|
commandBuffer.endRenderPass();
|
2024-10-06 00:37:42 -04:00
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
commandBuffer.end();
|
2024-10-01 18:54:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn createSyncObjects() -> void {
|
2024-10-05 23:08:12 -04:00
|
|
|
mImageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
|
|
|
|
mRenderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
|
|
|
|
mInFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
|
|
|
|
|
2024-10-01 18:54:41 -04:00
|
|
|
vk::SemaphoreCreateInfo semaphoreInfo {};
|
|
|
|
vk::FenceCreateInfo fenceInfo { .flags = vk::FenceCreateFlagBits::eSignaled };
|
|
|
|
|
2024-10-05 23:08:12 -04:00
|
|
|
for (usize idx = 0; idx < MAX_FRAMES_IN_FLIGHT; idx++) {
|
2024-10-06 18:14:15 -04:00
|
|
|
mImageAvailableSemaphores[idx] = mDevice->createSemaphoreUnique(semaphoreInfo);
|
|
|
|
mRenderFinishedSemaphores[idx] = mDevice->createSemaphoreUnique(semaphoreInfo);
|
|
|
|
mInFlightFences[idx] = mDevice->createFenceUnique(fenceInfo);
|
2024-10-05 23:08:12 -04:00
|
|
|
}
|
2024-10-01 18:54:41 -04:00
|
|
|
}
|
|
|
|
|
2024-10-10 20:39:04 -04:00
|
|
|
fn updateUniformBuffer(u32 currentImage) -> void {
|
|
|
|
static auto StartTime = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
auto currentTime = std::chrono::high_resolution_clock::now();
|
|
|
|
f32 time = std::chrono::duration<f32, std::chrono::seconds::period>(currentTime - StartTime).count();
|
|
|
|
|
|
|
|
UniformBufferObject ubo {
|
|
|
|
.model = glm::rotate(glm::mat4(1.0F), time * glm::radians(90.0F), glm::vec3(0.0F, 0.0F, 1.0F)),
|
|
|
|
.view =
|
|
|
|
glm::lookAt(glm::vec3(2.0F, 2.0F, 2.0F), glm::vec3(0.0F, 0.0F, 0.0F), glm::vec3(0.0F, 0.0F, 1.0F)),
|
|
|
|
.proj = glm::perspective(
|
|
|
|
glm::radians(45.0F),
|
|
|
|
static_cast<f32>(mSwapChainExtent.width) / static_cast<f32>(mSwapChainExtent.height),
|
|
|
|
0.1F,
|
|
|
|
10.0F
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
// Flip the Y axis, because glm was designed for OpenGL
|
|
|
|
ubo.proj[1][1] *= -1;
|
|
|
|
|
|
|
|
memcpy(mUniformBuffersMapped[currentImage], &ubo, sizeof(ubo));
|
|
|
|
}
|
|
|
|
|
2024-10-01 18:54:41 -04:00
|
|
|
fn drawFrame() -> void {
|
2024-10-06 18:34:42 -04:00
|
|
|
try {
|
|
|
|
vk::Result result =
|
|
|
|
mDevice->waitForFences(mInFlightFences[mCurrentFrame].get(), vk::Bool32(vk::True), UINT64_MAX);
|
2024-10-01 18:54:41 -04:00
|
|
|
|
2024-10-06 18:34:42 -04:00
|
|
|
if (result != vk::Result::eSuccess)
|
|
|
|
throw std::runtime_error("Failed to wait for fences!");
|
2024-10-01 18:54:41 -04:00
|
|
|
|
2024-10-06 18:34:42 -04:00
|
|
|
vk::Result imageIndexResult = vk::Result::eSuccess;
|
|
|
|
u32 imageIndexValue = 0;
|
2024-10-06 17:34:25 -04:00
|
|
|
|
2024-10-06 18:34:42 -04:00
|
|
|
std::tie(imageIndexResult, imageIndexValue) = mDevice->acquireNextImageKHR(
|
|
|
|
mSwapChain.get(), UINT64_MAX, mImageAvailableSemaphores[mCurrentFrame].get(), nullptr
|
|
|
|
);
|
2024-10-05 23:08:12 -04:00
|
|
|
|
2024-10-06 18:34:42 -04:00
|
|
|
if (imageIndexResult == vk::Result::eErrorOutOfDateKHR) {
|
|
|
|
recreateSwapChain();
|
|
|
|
return;
|
|
|
|
}
|
2024-10-05 23:08:12 -04:00
|
|
|
|
2024-10-06 18:34:42 -04:00
|
|
|
if (imageIndexResult != vk::Result::eSuccess && imageIndexResult != vk::Result::eSuboptimalKHR)
|
|
|
|
throw std::runtime_error("Failed to acquire swap chain image!");
|
2024-10-01 18:54:41 -04:00
|
|
|
|
2024-10-10 20:39:04 -04:00
|
|
|
updateUniformBuffer(mCurrentFrame);
|
|
|
|
|
2024-10-06 18:34:42 -04:00
|
|
|
mDevice->resetFences(mInFlightFences[mCurrentFrame].get());
|
2024-10-01 18:54:41 -04:00
|
|
|
|
2024-10-06 18:34:42 -04:00
|
|
|
mCommandBuffers[mCurrentFrame]->reset(vk::CommandBufferResetFlagBits::eReleaseResources);
|
|
|
|
recordCommandBuffer(mCommandBuffers[mCurrentFrame].get(), imageIndexValue);
|
2024-10-01 18:54:41 -04:00
|
|
|
|
2024-10-06 18:34:42 -04:00
|
|
|
std::array<vk::PipelineStageFlags, 1> waitStages = {
|
|
|
|
vk::PipelineStageFlagBits::eColorAttachmentOutput
|
|
|
|
};
|
2024-10-01 18:54:41 -04:00
|
|
|
|
2024-10-06 18:34:42 -04:00
|
|
|
vk::SubmitInfo submitInfo {
|
|
|
|
.waitSemaphoreCount = 1,
|
|
|
|
.pWaitSemaphores = &mImageAvailableSemaphores[mCurrentFrame].get(),
|
|
|
|
.pWaitDstStageMask = waitStages.data(),
|
|
|
|
.commandBufferCount = 1,
|
|
|
|
.pCommandBuffers = &mCommandBuffers[mCurrentFrame].get(),
|
|
|
|
.signalSemaphoreCount = 1,
|
|
|
|
.pSignalSemaphores = &mRenderFinishedSemaphores[mCurrentFrame].get(),
|
|
|
|
};
|
2024-10-01 18:54:41 -04:00
|
|
|
|
2024-10-06 18:34:42 -04:00
|
|
|
mGraphicsQueue.submit(submitInfo, mInFlightFences[mCurrentFrame].get());
|
2024-10-01 18:54:41 -04:00
|
|
|
|
2024-10-06 18:34:42 -04:00
|
|
|
vk::PresentInfoKHR presentInfo {
|
|
|
|
.waitSemaphoreCount = 1,
|
|
|
|
.pWaitSemaphores = &mRenderFinishedSemaphores[mCurrentFrame].get(),
|
|
|
|
.swapchainCount = 1,
|
|
|
|
.pSwapchains = &mSwapChain.get(),
|
|
|
|
.pImageIndices = &imageIndexValue,
|
|
|
|
};
|
2024-10-01 18:54:41 -04:00
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
vk::Result presentResult = mPresentQueue.presentKHR(presentInfo);
|
|
|
|
|
|
|
|
if (presentResult == vk::Result::eErrorOutOfDateKHR || presentResult == vk::Result::eSuboptimalKHR ||
|
|
|
|
mFramebufferResized) {
|
|
|
|
mFramebufferResized = false;
|
|
|
|
recreateSwapChain();
|
2024-10-07 16:04:49 -04:00
|
|
|
} else if (presentResult != vk::Result::eSuccess)
|
2024-10-06 18:14:15 -04:00
|
|
|
throw std::runtime_error("Failed to present swap chain image!");
|
|
|
|
|
|
|
|
mCurrentFrame = (mCurrentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
|
2024-10-07 16:04:49 -04:00
|
|
|
} catch (vk::OutOfDateKHRError& /*err*/) {
|
2024-10-05 23:08:12 -04:00
|
|
|
mFramebufferResized = false;
|
|
|
|
recreateSwapChain();
|
2024-10-06 18:14:15 -04:00
|
|
|
return;
|
2024-10-05 23:08:12 -04:00
|
|
|
}
|
2024-10-01 18:54:41 -04:00
|
|
|
}
|
|
|
|
|
2024-10-01 18:30:31 -04:00
|
|
|
fn createShaderModule(const std::vector<char>& code) -> vk::UniqueShaderModule {
|
|
|
|
vk::ShaderModuleCreateInfo createInfo { .codeSize = code.size(),
|
|
|
|
.pCode = std::bit_cast<const u32*>(code.data()) };
|
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
return mDevice->createShaderModuleUnique(createInfo);
|
2024-10-01 18:30:31 -04:00
|
|
|
}
|
|
|
|
|
2024-10-05 23:08:12 -04:00
|
|
|
static fn chooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& availableFormats
|
|
|
|
) -> vk::SurfaceFormatKHR {
|
2024-10-01 16:57:40 -04:00
|
|
|
for (const auto& availableFormat : availableFormats)
|
|
|
|
if (availableFormat.format == vk::Format::eB8G8R8A8Srgb &&
|
|
|
|
availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear)
|
|
|
|
return availableFormat;
|
|
|
|
|
|
|
|
return availableFormats[0];
|
|
|
|
}
|
|
|
|
|
2024-10-05 23:08:12 -04:00
|
|
|
static fn chooseSwapPresentMode(const std::vector<vk::PresentModeKHR>& availablePresentModes
|
|
|
|
) -> vk::PresentModeKHR {
|
2024-10-01 16:57:40 -04:00
|
|
|
for (const auto& availablePresentMode : availablePresentModes)
|
|
|
|
if (availablePresentMode == vk::PresentModeKHR::eMailbox)
|
|
|
|
return availablePresentMode;
|
|
|
|
|
|
|
|
return vk::PresentModeKHR::eFifo;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn chooseSwapExtent(const vk::SurfaceCapabilitiesKHR capabilities) -> vk::Extent2D {
|
|
|
|
if (capabilities.currentExtent.width != UINT32_MAX)
|
|
|
|
return capabilities.currentExtent;
|
|
|
|
|
|
|
|
u32 width = 0, height = 0;
|
|
|
|
std::tie(width, height) = mWindow->getFramebufferSize();
|
|
|
|
|
2024-10-06 16:19:29 -04:00
|
|
|
vk::Extent2D actualExtent = { width, height };
|
|
|
|
|
|
|
|
actualExtent.width =
|
|
|
|
std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
|
|
|
|
actualExtent.height =
|
|
|
|
std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
|
2024-10-01 16:57:40 -04:00
|
|
|
|
|
|
|
return actualExtent;
|
|
|
|
}
|
|
|
|
|
2024-10-01 17:06:14 -04:00
|
|
|
fn querySwapChainSupport(vk::PhysicalDevice device) -> SwapChainSupportDetails {
|
|
|
|
SwapChainSupportDetails details;
|
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
details.capabilities = device.getSurfaceCapabilitiesKHR(mSurface.get());
|
|
|
|
details.formats = device.getSurfaceFormatsKHR(mSurface.get());
|
|
|
|
details.present_modes = device.getSurfacePresentModesKHR(mSurface.get());
|
2024-10-01 17:06:14 -04:00
|
|
|
|
|
|
|
return details;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn isDeviceSuitable(vk::PhysicalDevice device) -> bool {
|
2024-10-10 18:51:20 -04:00
|
|
|
QueueFamilyIndices qfIndices = findQueueFamilies(device);
|
2024-10-01 17:06:14 -04:00
|
|
|
|
|
|
|
bool extensionsSupported = checkDeviceExtensionSupport(device);
|
|
|
|
|
|
|
|
bool swapChainAdequate = false;
|
|
|
|
|
|
|
|
if (extensionsSupported) {
|
|
|
|
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);
|
|
|
|
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.present_modes.empty();
|
|
|
|
}
|
|
|
|
|
2024-10-11 00:42:58 -04:00
|
|
|
vk::PhysicalDeviceFeatures supportedFeatures = device.getFeatures();
|
|
|
|
|
|
|
|
return qfIndices.isComplete() && extensionsSupported && swapChainAdequate &&
|
|
|
|
supportedFeatures.samplerAnisotropy;
|
2024-10-01 17:06:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static fn checkDeviceExtensionSupport(vk::PhysicalDevice device) -> bool {
|
2024-10-06 18:14:15 -04:00
|
|
|
std::vector<vk::ExtensionProperties> availableExtensions = device.enumerateDeviceExtensionProperties();
|
2024-10-01 17:06:14 -04:00
|
|
|
|
|
|
|
std::set<string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
|
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
for (const vk::ExtensionProperties& extension : availableExtensions)
|
2024-10-06 17:34:25 -04:00
|
|
|
requiredExtensions.erase(extension.extensionName);
|
2024-10-01 17:06:14 -04:00
|
|
|
|
|
|
|
return requiredExtensions.empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn findQueueFamilies(vk::PhysicalDevice device) -> QueueFamilyIndices {
|
2024-10-10 18:51:20 -04:00
|
|
|
QueueFamilyIndices qfIndices;
|
2024-10-01 17:06:14 -04:00
|
|
|
|
|
|
|
std::vector<vk::QueueFamilyProperties> queueFamilies = device.getQueueFamilyProperties();
|
|
|
|
|
|
|
|
for (u32 i = 0; i < queueFamilies.size(); i++) {
|
|
|
|
if (queueFamilies[i].queueFlags & vk::QueueFlagBits::eGraphics)
|
2024-10-10 18:51:20 -04:00
|
|
|
qfIndices.graphics_family = i;
|
2024-10-01 17:06:14 -04:00
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
vk::Bool32 queuePresentSupport = device.getSurfaceSupportKHR(i, mSurface.get());
|
2024-10-01 17:06:14 -04:00
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
if (queuePresentSupport)
|
2024-10-10 18:51:20 -04:00
|
|
|
qfIndices.present_family = i;
|
2024-10-01 17:06:14 -04:00
|
|
|
|
2024-10-10 18:51:20 -04:00
|
|
|
if (qfIndices.isComplete())
|
2024-10-01 17:06:14 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-10-10 18:51:20 -04:00
|
|
|
return qfIndices;
|
2024-10-01 17:06:14 -04:00
|
|
|
}
|
|
|
|
|
2024-09-28 18:13:24 -04:00
|
|
|
static fn getRequiredExtensions() -> std::vector<const char*> {
|
2024-09-30 16:46:17 -04:00
|
|
|
std::span<const char*> extensionsSpan = vkfw::getRequiredInstanceExtensions();
|
2024-09-28 18:13:24 -04:00
|
|
|
|
2024-09-30 16:46:17 -04:00
|
|
|
std::vector extensions(extensionsSpan.begin(), extensionsSpan.end());
|
2024-09-28 18:13:24 -04:00
|
|
|
|
2024-09-28 14:54:39 -04:00
|
|
|
if (enableValidationLayers)
|
2024-10-01 14:15:39 -04:00
|
|
|
extensions.emplace_back(vk::EXTDebugUtilsExtensionName);
|
2024-09-28 14:54:39 -04:00
|
|
|
|
2024-09-28 18:13:24 -04:00
|
|
|
return extensions;
|
|
|
|
}
|
|
|
|
|
2024-09-30 00:31:08 -04:00
|
|
|
static fn checkValidationLayerSupport() -> bool {
|
2024-10-06 18:14:15 -04:00
|
|
|
std::vector<vk::LayerProperties> availableLayers = vk::enumerateInstanceLayerProperties();
|
2024-09-30 00:31:08 -04:00
|
|
|
|
|
|
|
for (const char* layerName : validationLayers) {
|
|
|
|
bool layerFound = false;
|
|
|
|
|
2024-10-06 18:14:15 -04:00
|
|
|
for (const vk::LayerProperties& layerProperties : availableLayers)
|
2024-09-30 00:31:08 -04:00
|
|
|
if (strcmp(layerName, layerProperties.layerName) == 0) {
|
|
|
|
layerFound = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!layerFound)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-09-28 18:13:24 -04:00
|
|
|
static VKAPI_ATTR fn VKAPI_CALL debugCallback(
|
|
|
|
VkDebugUtilsMessageSeverityFlagBitsEXT /*messageSeverity*/,
|
|
|
|
VkDebugUtilsMessageTypeFlagsEXT /*messageType*/,
|
|
|
|
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
|
|
|
void* /*pUserData*/
|
2024-09-30 00:57:13 -04:00
|
|
|
) -> vk::Bool32 {
|
2024-09-29 23:02:04 -04:00
|
|
|
fmt::println("Validation layer: {}", pCallbackData->pMessage);
|
2024-09-28 18:13:24 -04:00
|
|
|
|
2024-09-30 21:34:25 -04:00
|
|
|
return vk::False;
|
2024-09-25 23:03:56 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-09-28 21:55:26 -04:00
|
|
|
fn main() -> i32 {
|
2024-10-06 12:26:44 -04:00
|
|
|
vk::DynamicLoader dynamicLoader;
|
|
|
|
auto vkGetInstanceProcAddr =
|
|
|
|
dynamicLoader.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
|
|
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
|
|
|
|
2024-09-29 23:11:12 -04:00
|
|
|
VulkanApp app;
|
2024-09-25 23:03:56 -04:00
|
|
|
|
|
|
|
try {
|
|
|
|
app.run();
|
|
|
|
} catch (const std::exception& e) {
|
2024-10-05 23:08:12 -04:00
|
|
|
fmt::println("{}", e.what());
|
2024-09-25 23:03:56 -04:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|