refactor
This commit is contained in:
parent
d2c174ba38
commit
ea4428f1af
25 changed files with 1374 additions and 1157 deletions
|
|
@ -3,8 +3,7 @@ project(App)
|
|||
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
|
||||
add_executable(${PROJECT_NAME} src/main.cpp src/utils.cpp src/shader.cpp)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ./inc)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC vulkan glfw)
|
||||
|
||||
file(COPY shaders DESTINATION ${PROJECT_BINARY_DIR}/)
|
||||
add_subdirectory(src/Common)
|
||||
add_subdirectory(src/GPU)
|
||||
add_subdirectory(src/PlatformWindow)
|
||||
add_subdirectory(src/App)
|
||||
|
|
|
|||
16
src/App/CMakeLists.txt
Normal file
16
src/App/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
cmake_minimum_required(VERSION 3.30)
|
||||
|
||||
project(App)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
private/main.cpp
|
||||
private/shader.cpp
|
||||
private/swapchain.cpp
|
||||
private/renderer.cpp
|
||||
private/vulkan_utils.cpp
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC public)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC Common GPU PlatformWindow)
|
||||
|
||||
file(COPY private/shaders DESTINATION ${PROJECT_BINARY_DIR}/)
|
||||
436
src/App/private/main.cpp
Normal file
436
src/App/private/main.cpp
Normal file
|
|
@ -0,0 +1,436 @@
|
|||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include "renderer.hpp"
|
||||
#include "swapchain.hpp"
|
||||
#include "vulkan_utils.hpp"
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
class Application {
|
||||
public:
|
||||
void run() {
|
||||
initWindow();
|
||||
initVulkan();
|
||||
mainLoop();
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void scheduleWindowResize(int sizeX, int sizeY) {
|
||||
mWindowSizeDirtyFlagTime = std::chrono::system_clock::now();
|
||||
mWindowSizeDirtyFlag = true;
|
||||
mWindowFramebufferSize = std::make_pair(sizeX, sizeY);
|
||||
assert(!(sizeX <= 0 || sizeY <= 0));
|
||||
}
|
||||
|
||||
private:
|
||||
void initWindow() {
|
||||
// glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11);
|
||||
glfwInit();
|
||||
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||
// glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||
mWindow = glfwCreateWindow(800, 600, "App", nullptr, nullptr);
|
||||
scheduleWindowResize(800, 600);
|
||||
|
||||
glfwSetWindowUserPointer(mWindow, this);
|
||||
glfwSetFramebufferSizeCallback(mWindow, [](GLFWwindow* window, int sizeX, int sizeY){
|
||||
((Application*) glfwGetWindowUserPointer(window))->scheduleWindowResize(sizeX, sizeY);
|
||||
});
|
||||
}
|
||||
|
||||
void initVulkan() {
|
||||
const std::vector<const char *> deviceExtensions = {
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||
};
|
||||
|
||||
uint32_t glfwExtensionCount = 0;
|
||||
const char **glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
||||
|
||||
mInstance = createInstance(glfwExtensions, glfwExtensionCount);
|
||||
|
||||
createWindowSurface();
|
||||
pickPhysicalDevice(deviceExtensions);
|
||||
findPhysicalDeviceQueueFamilies();
|
||||
createLogicalDevice(deviceExtensions);
|
||||
getQueues();
|
||||
|
||||
int width, height;
|
||||
glfwGetFramebufferSize(mWindow, &width, &height);
|
||||
|
||||
SwapChain::CreateInfo swapChainCreateInfo {
|
||||
.device = mDevice,
|
||||
.physicalDevice = mPhysicalDevice,
|
||||
.surface = mSurface,
|
||||
.sizeX = width,
|
||||
.sizeY = height,
|
||||
.graphicsQueueFamilyIndex = mGraphicsQueueFamilyIndex,
|
||||
.presentationQueueFamilyIndex = mPresentationQueueFamilyIndex,
|
||||
};
|
||||
|
||||
mSwapChain.createSwapChain(swapChainCreateInfo);
|
||||
mSwapChain.createSwapChainImageViews(mDevice);
|
||||
|
||||
createCommandPool();
|
||||
createCommandBuffer();
|
||||
|
||||
Renderer::CreateInfo rendererCreateInfo {
|
||||
mDevice, mPhysicalDevice,
|
||||
mSwapChain.mSwapChainExtent, mSwapChain.mSwapChainFormat,
|
||||
mCommandPool, mGraphicsQueue,
|
||||
};
|
||||
|
||||
mRenderer.create(rendererCreateInfo);
|
||||
|
||||
mSwapChain.createSwapChainFramebuffers(mDevice, mRenderer.mGraphicsRenderPass);
|
||||
|
||||
createSynchronizationObjects();
|
||||
}
|
||||
|
||||
void mainLoop() {
|
||||
while (!glfwWindowShouldClose(mWindow)) {
|
||||
glfwPollEvents();
|
||||
|
||||
if (mWindowSizeDirtyFlag) {
|
||||
auto timeDelayMs = timeDeltaMs(mWindowSizeDirtyFlagTime);
|
||||
if (timeDelayMs > mWindowSizeApplyMinDelay) {
|
||||
recreateSwapChain(mWindowFramebufferSize.first, mWindowFramebufferSize.second);
|
||||
mWindowSizeDirtyFlag = false;
|
||||
}
|
||||
}
|
||||
|
||||
drawFrame();
|
||||
}
|
||||
|
||||
vkDeviceWaitIdle(mDevice);
|
||||
}
|
||||
|
||||
void pickPhysicalDevice(const std::vector<const char *>& deviceExtensions) {
|
||||
|
||||
uint32_t deviceCount = 0;
|
||||
vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr);
|
||||
|
||||
if (deviceCount == 0) {
|
||||
throw std::runtime_error("no gpu with vulkan support");
|
||||
}
|
||||
|
||||
std::vector<VkPhysicalDevice> devices(deviceCount);
|
||||
vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data());
|
||||
|
||||
for (const auto &device: devices) {
|
||||
if (isDeviceSuitable(device, deviceExtensions)) {
|
||||
mPhysicalDevice = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mPhysicalDevice) throw std::runtime_error("no suitable gpu");
|
||||
}
|
||||
|
||||
bool isDeviceSuitable(VkPhysicalDevice device, const std::vector<const char *>& deviceExtensions) {
|
||||
VkPhysicalDeviceProperties properties;
|
||||
VkPhysicalDeviceFeatures features;
|
||||
|
||||
vkGetPhysicalDeviceProperties(device, &properties);
|
||||
vkGetPhysicalDeviceFeatures(device, &features);
|
||||
|
||||
// if (properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) return false;
|
||||
if (!features.geometryShader) return false;
|
||||
if (!checkDeviceExtensions(device, deviceExtensions)) return false;
|
||||
if (!SwapChain::checkDeviceSwapChain(device, mSurface)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void findPhysicalDeviceQueueFamilies() {
|
||||
uint32_t queuesCount = 0;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queuesCount, nullptr);
|
||||
|
||||
std::vector<VkQueueFamilyProperties> queueFamilyProperties(queuesCount);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queuesCount, queueFamilyProperties.data());
|
||||
|
||||
int index = 0;
|
||||
for (const auto &familyProperty: queueFamilyProperties) {
|
||||
if (familyProperty.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||||
mGraphicsQueueFamilyIndex = index;
|
||||
}
|
||||
|
||||
VkBool32 presentationQueueSupport = false;
|
||||
vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice, index, mSurface, &presentationQueueSupport);
|
||||
|
||||
if (presentationQueueSupport) {
|
||||
mPresentationQueueFamilyIndex = index;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if (mPresentationQueueFamilyIndex == -1 || mGraphicsQueueFamilyIndex == -1) {
|
||||
throw std::runtime_error("nu require queue families found");
|
||||
}
|
||||
}
|
||||
|
||||
void createLogicalDevice(const std::vector<const char *>& deviceExtensions) {
|
||||
float queuePriority = 1.f;
|
||||
|
||||
VkDeviceQueueCreateInfo graphicsQueueCreateInfos{
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.queueFamilyIndex = mGraphicsQueueFamilyIndex,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &queuePriority,
|
||||
};
|
||||
|
||||
VkDeviceQueueCreateInfo presentationQueueCreateInfos{
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.queueFamilyIndex = mPresentationQueueFamilyIndex,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &queuePriority,
|
||||
};
|
||||
|
||||
std::vector<VkDeviceQueueCreateInfo> queues = {graphicsQueueCreateInfos};
|
||||
|
||||
if (mPresentationQueueFamilyIndex != mGraphicsQueueFamilyIndex) {
|
||||
queues.push_back(presentationQueueCreateInfos);
|
||||
}
|
||||
|
||||
VkPhysicalDeviceFeatures features{};
|
||||
|
||||
VkDeviceCreateInfo deviceCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
.queueCreateInfoCount = (uint32_t) queues.size(),
|
||||
.pQueueCreateInfos = queues.data(),
|
||||
.enabledExtensionCount = (uint32_t) deviceExtensions.size(),
|
||||
.ppEnabledExtensionNames = deviceExtensions.data(),
|
||||
.pEnabledFeatures = &features,
|
||||
};
|
||||
|
||||
vkCreateDevice(mPhysicalDevice, &deviceCreateInfo, nullptr, &mDevice);
|
||||
|
||||
if (mDevice == VK_NULL_HANDLE) throw std::runtime_error("failed to create vulkan logical device");
|
||||
}
|
||||
|
||||
void getQueues() {
|
||||
vkGetDeviceQueue(mDevice, mGraphicsQueueFamilyIndex, 0, &mGraphicsQueue);
|
||||
vkGetDeviceQueue(mDevice, mPresentationQueueFamilyIndex, 0, &mPresentQueue);
|
||||
}
|
||||
|
||||
void createWindowSurface() {
|
||||
if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create vulkan window surface");
|
||||
}
|
||||
}
|
||||
|
||||
void recreateSwapChain(int sizeX, int sizeY) {
|
||||
vkDeviceWaitIdle(mDevice);
|
||||
|
||||
mSwapChain.destroySwapChain(mDevice);
|
||||
|
||||
SwapChain::CreateInfo createInfo {
|
||||
.device = mDevice,
|
||||
.physicalDevice = mPhysicalDevice,
|
||||
.surface = mSurface,
|
||||
.sizeX = sizeX,
|
||||
.sizeY = sizeY,
|
||||
.graphicsQueueFamilyIndex = mGraphicsQueueFamilyIndex,
|
||||
.presentationQueueFamilyIndex = mPresentationQueueFamilyIndex,
|
||||
};
|
||||
|
||||
mSwapChain.createSwapChain(createInfo);
|
||||
mSwapChain.createSwapChainImageViews(mDevice);
|
||||
mSwapChain.createSwapChainFramebuffers(mDevice, mRenderer.mGraphicsRenderPass);
|
||||
}
|
||||
|
||||
|
||||
void createSynchronizationObjects() {
|
||||
VkSemaphoreCreateInfo semaphoreCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
};
|
||||
|
||||
VkFenceCreateInfo fenceCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
|
||||
};
|
||||
|
||||
bool failure = false;
|
||||
|
||||
failure |= vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, &mSemaphoreImageAcquired) != VK_SUCCESS;
|
||||
failure |= vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, &mSemaphoreFramebufferDrawn) != VK_SUCCESS;
|
||||
failure |= vkCreateFence(mDevice, &fenceCreateInfo, nullptr, &mFenceCanStartNewFrame) != VK_SUCCESS;
|
||||
|
||||
if (failure) throw std::runtime_error("failed to create synchronization objects");
|
||||
}
|
||||
|
||||
void drawFrame() {
|
||||
vkWaitForFences(mDevice, 1, &mFenceCanStartNewFrame, VK_TRUE, UINT64_MAX);
|
||||
vkResetFences(mDevice, 1, &mFenceCanStartNewFrame);
|
||||
|
||||
uint32_t imageIndex = 0;
|
||||
vkAcquireNextImageKHR(mDevice, mSwapChain.mSwapChain, UINT64_MAX, mSemaphoreImageAcquired, VK_NULL_HANDLE, &imageIndex);
|
||||
|
||||
vkResetCommandBuffer(mCommandBuffer, 0);
|
||||
mRenderer.populateGraphicsCommandBuffer(mCommandBuffer, mSwapChain.mSwapChainFrameBuffers[imageIndex],
|
||||
mSwapChain.mSwapChainExtent);
|
||||
|
||||
VkSemaphore waitSemaphores[] = { mSemaphoreImageAcquired };
|
||||
VkSemaphore signalSemaphores[] = { mSemaphoreFramebufferDrawn };
|
||||
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
|
||||
|
||||
VkSubmitInfo submitInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = waitSemaphores,
|
||||
.pWaitDstStageMask = waitStages,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &mCommandBuffer,
|
||||
.signalSemaphoreCount = 1,
|
||||
.pSignalSemaphores = signalSemaphores,
|
||||
};
|
||||
|
||||
updateUniformBuffer();
|
||||
|
||||
if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mFenceCanStartNewFrame) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to submit to graphics queue");
|
||||
}
|
||||
|
||||
VkSwapchainKHR swapchains[] = { mSwapChain.mSwapChain };
|
||||
|
||||
VkPresentInfoKHR presentInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = signalSemaphores,
|
||||
.swapchainCount = 1,
|
||||
.pSwapchains = swapchains,
|
||||
.pImageIndices = &imageIndex,
|
||||
};
|
||||
|
||||
vkQueuePresentKHR(mPresentQueue, &presentInfo);
|
||||
/* Somehow res is always VK_SUCCESS
|
||||
if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) {
|
||||
recreateSwapChain();
|
||||
} else if (res != VK_SUCCESS) {
|
||||
throw std::runtime_error("cannot acquire new khr image");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void updateUniformBuffer() const {
|
||||
static float time = 0;
|
||||
time += 0.001;
|
||||
|
||||
glm::mat4 model = glm::rotate(glm::mat4(1.0f), (time * glm::radians(90.f)), glm::vec3(0.f, 0.f, 1.f));
|
||||
glm::mat4 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});
|
||||
glm::mat4 perspective = glm::perspective(glm::radians(45.f),
|
||||
(float) mWindowFramebufferSize.first /
|
||||
(float) mWindowFramebufferSize.second, 0.1f, 10.f);
|
||||
|
||||
auto transforms = perspective * view * model;
|
||||
|
||||
CustomShader::UniformBuffer ubo {
|
||||
.transforms = transforms,
|
||||
.origin = glm::vec4(0, 0, 0, 0),
|
||||
};
|
||||
|
||||
memcpy(mRenderer.mUniformBufferMemoryMapped, &ubo, sizeof(ubo));
|
||||
// ubo.transforms =
|
||||
}
|
||||
|
||||
void destroySynchronizationObjects() {
|
||||
vkDestroySemaphore(mDevice, mSemaphoreImageAcquired, nullptr);
|
||||
vkDestroySemaphore(mDevice, mSemaphoreFramebufferDrawn, nullptr);
|
||||
vkDestroyFence(mDevice, mFenceCanStartNewFrame, nullptr);
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
mRenderer.destroy(mDevice);
|
||||
|
||||
destroyCommandPool();
|
||||
destroySynchronizationObjects();
|
||||
|
||||
mSwapChain.destroySwapChain(mDevice);
|
||||
|
||||
vkDestroyDevice(mDevice, nullptr);
|
||||
vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
|
||||
vkDestroyInstance(mInstance, nullptr);
|
||||
glfwDestroyWindow(mWindow);
|
||||
glfwTerminate();
|
||||
}
|
||||
|
||||
void createCommandPool() {
|
||||
VkCommandPoolCreateInfo createInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||
.queueFamilyIndex = mGraphicsQueueFamilyIndex,
|
||||
};
|
||||
|
||||
if (vkCreateCommandPool(mDevice, &createInfo, nullptr, &mCommandPool) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create command pool");
|
||||
}
|
||||
}
|
||||
|
||||
void createCommandBuffer() {
|
||||
VkCommandBufferAllocateInfo allocateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = mCommandPool,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = 1,
|
||||
};
|
||||
|
||||
if (vkAllocateCommandBuffers(mDevice, &allocateInfo, &mCommandBuffer) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create command buffer");
|
||||
}
|
||||
}
|
||||
|
||||
void destroyCommandPool() {
|
||||
vkDestroyCommandPool(mDevice, mCommandPool, nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
GLFWwindow *mWindow = nullptr;
|
||||
|
||||
bool mWindowSizeDirtyFlag = true;
|
||||
TimePoint mWindowSizeDirtyFlagTime = std::chrono::system_clock::now();
|
||||
TimeMs mWindowSizeApplyMinDelay = 200;
|
||||
std::pair<int, int> mWindowFramebufferSize = { 0, 0 };
|
||||
|
||||
uint32_t mGraphicsQueueFamilyIndex = -1;
|
||||
uint32_t mPresentationQueueFamilyIndex = -1;
|
||||
|
||||
VkCommandPool mCommandPool = VK_NULL_HANDLE;
|
||||
VkCommandBuffer mCommandBuffer = VK_NULL_HANDLE;
|
||||
|
||||
VkInstance mInstance = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
|
||||
VkDevice mDevice = VK_NULL_HANDLE;
|
||||
|
||||
VkQueue mGraphicsQueue = VK_NULL_HANDLE;
|
||||
VkQueue mPresentQueue = VK_NULL_HANDLE;
|
||||
|
||||
VkSurfaceKHR mSurface = VK_NULL_HANDLE;
|
||||
SwapChain mSwapChain;
|
||||
|
||||
Renderer mRenderer;
|
||||
|
||||
VkSemaphore mSemaphoreImageAcquired = VK_NULL_HANDLE;
|
||||
VkSemaphore mSemaphoreFramebufferDrawn = VK_NULL_HANDLE;
|
||||
VkFence mFenceCanStartNewFrame = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
int main() {
|
||||
Application app;
|
||||
|
||||
try {
|
||||
app.run();
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
486
src/App/private/renderer.cpp
Normal file
486
src/App/private/renderer.cpp
Normal file
|
|
@ -0,0 +1,486 @@
|
|||
#include "renderer.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
|
||||
void Renderer::create(const CreateInfo &info) {
|
||||
mShader.create(info.device);
|
||||
|
||||
createRenderPass(info.device, info.format);
|
||||
createGraphicsPipeline(info.device, info.extent2D);
|
||||
|
||||
createVertexBuffer(info.physicalDevice, info.device, info.queue, info.commandPool);
|
||||
createIndexBuffer(info.physicalDevice, info.device, info.queue, info.commandPool);
|
||||
createUniformBuffer(info.physicalDevice, info.device);
|
||||
|
||||
createDescriptorPool(info.device);
|
||||
createDescriptorSets(info.device);
|
||||
}
|
||||
|
||||
void Renderer::destroy(VkDevice device) const {
|
||||
// vkDestroyDescriptorSetLayout(mDevice, mDescriptorSet, nullptr);
|
||||
vkDestroyDescriptorPool(device, mDescriptorPool, nullptr);
|
||||
|
||||
vkUnmapMemory(device, mUniformBufferMemory);
|
||||
destroyBuffer(device, mUniformBuffer, mUniformBufferMemory);
|
||||
|
||||
destroyBuffer(device, mVertexBuffer, mVertexBufferMemory);
|
||||
destroyBuffer(device, mIndexBuffer, mIndexBufferMemory);
|
||||
|
||||
vkDestroyRenderPass(device, mGraphicsRenderPass, nullptr);
|
||||
|
||||
destroyGraphicsPipeline(device);
|
||||
}
|
||||
|
||||
void Renderer::createDescriptorPool(VkDevice device) {
|
||||
VkDescriptorPoolSize poolSize{
|
||||
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
};
|
||||
|
||||
VkDescriptorPoolCreateInfo createInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.maxSets = 1,
|
||||
.poolSizeCount = 1,
|
||||
.pPoolSizes = &poolSize,
|
||||
};
|
||||
|
||||
if (vkCreateDescriptorPool(device, &createInfo, nullptr, &mDescriptorPool) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create descriptor pool");
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::createDescriptorSets(VkDevice device) {
|
||||
VkDescriptorSetAllocateInfo allocateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.descriptorPool = mDescriptorPool,
|
||||
.descriptorSetCount = 1,
|
||||
.pSetLayouts = &mShader.mDescriptorSetLayout,
|
||||
};
|
||||
|
||||
if (vkAllocateDescriptorSets(device, &allocateInfo, &mDescriptorSet) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to allocate descriptor set");
|
||||
}
|
||||
|
||||
VkDescriptorBufferInfo bufferInfo{
|
||||
.buffer = mUniformBuffer,
|
||||
.offset = 0,
|
||||
.range = sizeof(CustomShader::UniformBuffer),
|
||||
};
|
||||
|
||||
VkWriteDescriptorSet writeDescriptorSet{
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = mDescriptorSet,
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.pBufferInfo = &bufferInfo,
|
||||
};
|
||||
|
||||
vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr);
|
||||
}
|
||||
|
||||
void Renderer::createGraphicsPipeline(VkDevice device, VkExtent2D extent2D) {
|
||||
|
||||
std::vector<VkDynamicState> dynamicStates = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR,
|
||||
};
|
||||
|
||||
VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.dynamicStateCount = (uint32_t) dynamicStates.size(),
|
||||
.pDynamicStates = dynamicStates.data(),
|
||||
};
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = 1,
|
||||
.pVertexBindingDescriptions = &mShader.mVertexInputDescription,
|
||||
.vertexAttributeDescriptionCount = (uint32_t) mShader.mVertexAttributes.size(),
|
||||
.pVertexAttributeDescriptions = mShader.mVertexAttributes.data(),
|
||||
};
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||
.primitiveRestartEnable = VK_FALSE,
|
||||
};
|
||||
|
||||
VkViewport viewport{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = (float) extent2D.width,
|
||||
.height = (float) extent2D.height,
|
||||
.minDepth = 0.f,
|
||||
.maxDepth = 1.f,
|
||||
};
|
||||
|
||||
VkRect2D scissor{
|
||||
.offset = {0, 0},
|
||||
.extent = extent2D,
|
||||
};
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewportState{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.viewportCount = 1,
|
||||
.pViewports = &viewport,
|
||||
.scissorCount = 1,
|
||||
.pScissors = &scissor,
|
||||
};
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.depthClampEnable = VK_FALSE,
|
||||
.rasterizerDiscardEnable = VK_FALSE,
|
||||
.polygonMode = VK_POLYGON_MODE_FILL,
|
||||
.cullMode = VK_CULL_MODE_BACK_BIT,
|
||||
.frontFace = VK_FRONT_FACE_CLOCKWISE,
|
||||
|
||||
.depthBiasEnable = VK_FALSE,
|
||||
.depthBiasConstantFactor = 0.f,
|
||||
.depthBiasClamp = 0.f,
|
||||
.depthBiasSlopeFactor = 0.f,
|
||||
|
||||
.lineWidth = 1,
|
||||
};
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.sampleShadingEnable = VK_FALSE,
|
||||
};
|
||||
|
||||
VkPipelineColorBlendAttachmentState colorBlendAttachmentState{
|
||||
.blendEnable = VK_FALSE,
|
||||
.colorWriteMask = (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT |
|
||||
VK_COLOR_COMPONENT_A_BIT),
|
||||
};
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.logicOpEnable = VK_FALSE,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &colorBlendAttachmentState,
|
||||
};
|
||||
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = &mShader.mDescriptorSetLayout,
|
||||
};
|
||||
|
||||
if (vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &mGraphicsPipelineLayout) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create pipeline layout");
|
||||
}
|
||||
|
||||
VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.stageCount = 2,
|
||||
.pStages = mShader.mStageCreateInfos.data(),
|
||||
.pVertexInputState = &vertexInputStateCreateInfo,
|
||||
.pInputAssemblyState = &inputAssemblyStateCreateInfo,
|
||||
.pViewportState = &viewportState,
|
||||
.pRasterizationState = &rasterizationStateCreateInfo,
|
||||
.pMultisampleState = &multisampleStateCreateInfo,
|
||||
.pColorBlendState = &colorBlendStateCreateInfo,
|
||||
.pDynamicState = &dynamicStateCreateInfo,
|
||||
.layout = mGraphicsPipelineLayout,
|
||||
.renderPass = mGraphicsRenderPass,
|
||||
.subpass = 0,
|
||||
};
|
||||
|
||||
if (vkCreateGraphicsPipelines(device, nullptr, 1, &graphicsPipelineCreateInfo, nullptr, &mGraphicsPipeline) !=
|
||||
VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create graphics pipeline");
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::createRenderPass(VkDevice device, VkFormat format) {
|
||||
VkAttachmentDescription colorAttachment{
|
||||
.format = format,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
};
|
||||
|
||||
VkAttachmentReference colorAttachmentReference{
|
||||
.attachment = 0,
|
||||
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
};
|
||||
|
||||
VkSubpassDescription subpassDescription{
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachments = &colorAttachmentReference,
|
||||
};
|
||||
|
||||
VkSubpassDependency dependency{
|
||||
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
||||
.dstSubpass = 0,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
};
|
||||
|
||||
VkRenderPassCreateInfo renderPassCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &colorAttachment,
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpassDescription,
|
||||
.dependencyCount = 1,
|
||||
.pDependencies = &dependency
|
||||
};
|
||||
|
||||
if (vkCreateRenderPass(device, &renderPassCreateInfo, nullptr, &mGraphicsRenderPass) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create render pass");
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::destroyGraphicsPipeline(VkDevice device) const {
|
||||
mShader.destroy(device);
|
||||
vkDestroyPipelineLayout(device, mGraphicsPipelineLayout, nullptr);
|
||||
vkDestroyPipeline(device, mGraphicsPipeline, nullptr);
|
||||
}
|
||||
|
||||
void Renderer::createIndexBuffer(VkPhysicalDevice physicalDevice, VkDevice device, VkQueue queue,
|
||||
VkCommandPool commandPool) {
|
||||
VkDeviceSize size = sizeof(indices[0]) * indices.size();
|
||||
|
||||
VkBuffer stagingBuffer;
|
||||
VkDeviceMemory stagingBufferMemory;
|
||||
|
||||
BufferCreateInfo stagingBufferInfo{
|
||||
&stagingBuffer, &stagingBufferMemory,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, size,
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT
|
||||
};
|
||||
|
||||
createBuffer(physicalDevice, device, stagingBufferInfo);
|
||||
|
||||
{
|
||||
void *memory;
|
||||
vkMapMemory(device, stagingBufferMemory, 0, size, 0, &memory);
|
||||
memcpy(memory, indices.data(), size);
|
||||
vkUnmapMemory(device, stagingBufferMemory);
|
||||
}
|
||||
|
||||
BufferCreateInfo indexBufferInfo{
|
||||
&mIndexBuffer, &mIndexBufferMemory,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, size,
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT
|
||||
};
|
||||
|
||||
createBuffer(physicalDevice, device, indexBufferInfo);
|
||||
|
||||
copyBuffer(device, commandPool, queue, stagingBuffer, mIndexBuffer, size);
|
||||
|
||||
destroyBuffer(device, stagingBuffer, stagingBufferMemory);
|
||||
}
|
||||
|
||||
void Renderer::createUniformBuffer(VkPhysicalDevice physicalDevice, VkDevice device) {
|
||||
auto size = sizeof(CustomShader::UniformBuffer);
|
||||
|
||||
BufferCreateInfo uniformCreateInfo{
|
||||
&mUniformBuffer, &mUniformBufferMemory,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, size,
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
|
||||
};
|
||||
|
||||
createBuffer(physicalDevice, device, uniformCreateInfo);
|
||||
|
||||
vkMapMemory(device, mUniformBufferMemory, 0, size, 0, &mUniformBufferMemoryMapped);
|
||||
}
|
||||
|
||||
void Renderer::createVertexBuffer(VkPhysicalDevice physicalDevice, VkDevice device, VkQueue queue,
|
||||
VkCommandPool commandPool) {
|
||||
size_t size = vertices.size() * sizeof(vertices[0]);
|
||||
|
||||
VkBuffer stagingBuffer;
|
||||
VkDeviceMemory stagingBufferMemory;
|
||||
|
||||
BufferCreateInfo stagingInfo{
|
||||
&stagingBuffer, &stagingBufferMemory,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, size,
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT
|
||||
};
|
||||
|
||||
createBuffer(physicalDevice, device, stagingInfo);
|
||||
|
||||
{
|
||||
void *memory;
|
||||
vkMapMemory(device, stagingBufferMemory, 0, size, 0, &memory);
|
||||
memcpy(memory, vertices.data(), size);
|
||||
vkUnmapMemory(device, stagingBufferMemory);
|
||||
}
|
||||
|
||||
BufferCreateInfo vertexInfo{
|
||||
&mVertexBuffer, &mVertexBufferMemory,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, size,
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT
|
||||
};
|
||||
|
||||
createBuffer(physicalDevice, device, vertexInfo);
|
||||
|
||||
copyBuffer(device, commandPool, queue, stagingBuffer, mVertexBuffer, size);
|
||||
|
||||
destroyBuffer(device, stagingBuffer, stagingBufferMemory);
|
||||
}
|
||||
|
||||
void
|
||||
Renderer::createBuffer(VkPhysicalDevice physicalDevice, VkDevice device, const BufferCreateInfo &bufferCreateInfo) {
|
||||
|
||||
VkBufferCreateInfo createInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = bufferCreateInfo.size,
|
||||
.usage = bufferCreateInfo.usage,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
};
|
||||
|
||||
if (vkCreateBuffer(device, &createInfo, nullptr, bufferCreateInfo.buffer) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create vertex buffer");
|
||||
}
|
||||
|
||||
VkMemoryRequirements memoryRequirements;
|
||||
vkGetBufferMemoryRequirements(device, *bufferCreateInfo.buffer, &memoryRequirements);
|
||||
|
||||
VkMemoryAllocateInfo allocateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = memoryRequirements.size,
|
||||
.memoryTypeIndex = findMemoryType(physicalDevice, memoryRequirements.memoryTypeBits, bufferCreateInfo.properties),
|
||||
};
|
||||
|
||||
if (vkAllocateMemory(device, &allocateInfo, nullptr, bufferCreateInfo.memory) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to allocate vertex buffer memory");
|
||||
}
|
||||
|
||||
vkBindBufferMemory(device, *bufferCreateInfo.buffer, *bufferCreateInfo.memory, 0);
|
||||
}
|
||||
|
||||
void Renderer::destroyBuffer(VkDevice device, VkBuffer buffer, VkDeviceMemory memory) {
|
||||
vkDestroyBuffer(device, buffer, nullptr);
|
||||
vkFreeMemory(device, memory, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
Renderer::populateGraphicsCommandBuffer(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend) {
|
||||
VkCommandBufferBeginInfo commandBufferBeginInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
};
|
||||
|
||||
if (vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to begin command buffer =");
|
||||
}
|
||||
|
||||
VkClearValue clearColor{.color = {.float32 = {0.f, 0.f, 0.f, 1.f}}};
|
||||
|
||||
VkRenderPassBeginInfo renderPassBeginInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.renderPass = mGraphicsRenderPass,
|
||||
.framebuffer = framebuffer,
|
||||
.renderArea = {
|
||||
.offset = {0, 0},
|
||||
.extent = extend,
|
||||
},
|
||||
.clearValueCount = 1,
|
||||
.pClearValues = &clearColor,
|
||||
};
|
||||
|
||||
vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mGraphicsPipeline);
|
||||
|
||||
VkViewport viewport{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = (float) extend.width,
|
||||
.height = (float) extend.height,
|
||||
.minDepth = 0.f,
|
||||
.maxDepth = 1.f,
|
||||
};
|
||||
|
||||
VkRect2D scissor{
|
||||
.offset = {0, 0},
|
||||
.extent = extend,
|
||||
};
|
||||
|
||||
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
|
||||
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
|
||||
|
||||
VkBuffer vertexBuffers[] = {mVertexBuffer};
|
||||
VkDeviceSize offsets[] = {0};
|
||||
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
|
||||
vkCmdBindIndexBuffer(commandBuffer, mIndexBuffer, 0, VK_INDEX_TYPE_UINT16);
|
||||
|
||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mGraphicsPipelineLayout, 0, 1,
|
||||
&mDescriptorSet, 0,
|
||||
nullptr);
|
||||
|
||||
vkCmdDrawIndexed(commandBuffer, indices.size(), 1, 0, 0, 0);
|
||||
|
||||
vkCmdEndRenderPass(commandBuffer);
|
||||
|
||||
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to end command buffer");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Renderer::findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags flags) {
|
||||
VkPhysicalDeviceMemoryProperties memoryProperties;
|
||||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
|
||||
|
||||
for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) {
|
||||
if (typeFilter & (1 << i) && (memoryProperties.memoryTypes[i].propertyFlags & flags) == flags) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("no suitable memory for vertex buffer found");
|
||||
}
|
||||
|
||||
void Renderer::copyBuffer(VkDevice device, VkCommandPool commandPool, VkQueue queue, VkBuffer src, VkBuffer dst,
|
||||
VkDeviceSize size) {
|
||||
|
||||
VkCommandBufferAllocateInfo allocateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = commandPool,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = 1
|
||||
};
|
||||
|
||||
VkCommandBuffer commandBuffer;
|
||||
vkAllocateCommandBuffers(device, &allocateInfo, &commandBuffer);
|
||||
|
||||
VkCommandBufferBeginInfo beginInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
};
|
||||
|
||||
vkBeginCommandBuffer(commandBuffer, &beginInfo);
|
||||
|
||||
VkBufferCopy copyRegion{
|
||||
.srcOffset = 0,
|
||||
.dstOffset = 0,
|
||||
.size = size,
|
||||
};
|
||||
|
||||
vkCmdCopyBuffer(commandBuffer, src, dst, 1, ©Region);
|
||||
vkEndCommandBuffer(commandBuffer);
|
||||
|
||||
VkSubmitInfo submitInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &commandBuffer,
|
||||
};
|
||||
|
||||
vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
|
||||
vkQueueWaitIdle(queue);
|
||||
|
||||
vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
|
||||
}
|
||||
190
src/App/private/swapchain.cpp
Normal file
190
src/App/private/swapchain.cpp
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
#include "swapchain.hpp"
|
||||
#include <stdexcept>
|
||||
#include <numeric>
|
||||
#include <algorithm>
|
||||
|
||||
void SwapChain::destroySwapChain(VkDevice device) {
|
||||
for (const auto &buffer: mSwapChainFrameBuffers) {
|
||||
vkDestroyFramebuffer(device, buffer, nullptr);
|
||||
}
|
||||
|
||||
for (const auto &imageView: mSwapChainImageViews) {
|
||||
vkDestroyImageView(device, imageView, nullptr);
|
||||
}
|
||||
|
||||
vkDestroySwapchainKHR(device, mSwapChain, nullptr);
|
||||
}
|
||||
|
||||
void SwapChain::createSwapChain(const CreateInfo &info) {
|
||||
SwapChainSupportDetails details = querySwapChainSupportDetails(info.physicalDevice, info.surface);
|
||||
|
||||
VkSurfaceFormatKHR surfaceFormat = pickSwapChainSurfaceFormat(details);
|
||||
VkPresentModeKHR presentMode = pickSwapChainPresentMode(details);
|
||||
VkExtent2D extent2D = pickSwapChainExtent(details.capabilities, info.sizeX, info.sizeY);
|
||||
|
||||
// +1 so will not have to wait for device to finish frame to query an image to render to
|
||||
uint32_t imageCount = details.capabilities.minImageCount + 1;
|
||||
|
||||
if (details.capabilities.maxImageCount > 0 && imageCount > details.capabilities.maxImageCount) {
|
||||
imageCount = details.capabilities.maxImageCount;
|
||||
}
|
||||
|
||||
VkSwapchainCreateInfoKHR createInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||
.surface = info.surface,
|
||||
.minImageCount = imageCount,
|
||||
.imageFormat = surfaceFormat.format,
|
||||
.imageExtent = extent2D,
|
||||
.imageArrayLayers = 1,
|
||||
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
||||
.preTransform = details.capabilities.currentTransform,
|
||||
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
.presentMode = presentMode,
|
||||
.clipped = VK_TRUE,
|
||||
.oldSwapchain = VK_NULL_HANDLE,
|
||||
};
|
||||
|
||||
uint32_t queueIndices[] = {info.graphicsQueueFamilyIndex, info.presentationQueueFamilyIndex};
|
||||
|
||||
if (info.graphicsQueueFamilyIndex != info.presentationQueueFamilyIndex) {
|
||||
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||
createInfo.queueFamilyIndexCount = 2;
|
||||
createInfo.pQueueFamilyIndices = queueIndices;
|
||||
} else {
|
||||
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
}
|
||||
|
||||
if (vkCreateSwapchainKHR(info.device, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create swap chain");
|
||||
}
|
||||
|
||||
mSwapChainExtent = extent2D;
|
||||
mSwapChainFormat = surfaceFormat.format;
|
||||
|
||||
uint32_t createdImageCount = 0;
|
||||
vkGetSwapchainImagesKHR(info.device, mSwapChain, &createdImageCount, nullptr);
|
||||
mSwapChainImages.resize(createdImageCount);
|
||||
vkGetSwapchainImagesKHR(info.device, mSwapChain, &createdImageCount, mSwapChainImages.data());
|
||||
}
|
||||
|
||||
|
||||
void SwapChain::createSwapChainImageViews(VkDevice device) {
|
||||
mSwapChainImageViews.resize(mSwapChainImages.size());
|
||||
|
||||
int i = 0;
|
||||
for (const auto &image: mSwapChainImages) {
|
||||
|
||||
VkImageViewCreateInfo createInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = mSwapChainFormat,
|
||||
.components = {
|
||||
.r = VK_COMPONENT_SWIZZLE_R,
|
||||
.g = VK_COMPONENT_SWIZZLE_G,
|
||||
.b = VK_COMPONENT_SWIZZLE_B,
|
||||
.a = VK_COMPONENT_SWIZZLE_A,
|
||||
},
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
}
|
||||
};
|
||||
|
||||
if (vkCreateImageView(device, &createInfo, nullptr, &mSwapChainImageViews[i]) != VK_SUCCESS) {
|
||||
throw std::runtime_error("cannot create image view for the swap chain");
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void SwapChain::createSwapChainFramebuffers(VkDevice device, VkRenderPass renderPass) {
|
||||
mSwapChainFrameBuffers.resize(mSwapChainImageViews.size());
|
||||
|
||||
for (size_t i = 0; i < mSwapChainImageViews.size(); i++) {
|
||||
|
||||
VkImageView attachments[] = {
|
||||
mSwapChainImageViews[i],
|
||||
};
|
||||
|
||||
VkFramebufferCreateInfo framebufferCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.renderPass = renderPass,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = attachments,
|
||||
.width = mSwapChainExtent.width,
|
||||
.height = mSwapChainExtent.height,
|
||||
.layers = 1,
|
||||
};
|
||||
|
||||
if (vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &mSwapChainFrameBuffers[i]) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create swapchain framebuffers");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VkSurfaceFormatKHR SwapChain::pickSwapChainSurfaceFormat(const SwapChainSupportDetails &details) {
|
||||
for (const auto &format: details.formats) {
|
||||
if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR) {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
return details.formats.front();
|
||||
}
|
||||
|
||||
VkPresentModeKHR SwapChain::pickSwapChainPresentMode(const SwapChainSupportDetails &details) {
|
||||
for (const auto &mode: details.presentModes) {
|
||||
if (mode == VK_PRESENT_MODE_MAILBOX_KHR) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
|
||||
return VK_PRESENT_MODE_FIFO_KHR; // guaranteed exists
|
||||
}
|
||||
|
||||
VkExtent2D SwapChain::pickSwapChainExtent(const VkSurfaceCapabilitiesKHR &capabilities, int sizeX, int sizeY) {
|
||||
// if set by vulkan just keep it
|
||||
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
|
||||
return capabilities.currentExtent;
|
||||
}
|
||||
|
||||
VkExtent2D out = {(uint32_t) sizeX, (uint32_t) sizeY};
|
||||
|
||||
out.width = std::clamp(out.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
|
||||
out.height = std::clamp(out.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
SwapChain::SwapChainSupportDetails
|
||||
SwapChain::querySwapChainSupportDetails(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||
SwapChainSupportDetails details;
|
||||
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
|
||||
|
||||
uint32_t formatCount = 0;
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
|
||||
if (formatCount != 0) {
|
||||
details.formats.resize(formatCount);
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
|
||||
}
|
||||
|
||||
uint32_t modeCount = 0;
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &modeCount, nullptr);
|
||||
if (modeCount != 0) {
|
||||
details.presentModes.resize(modeCount);
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &modeCount, details.presentModes.data());
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
bool SwapChain::checkDeviceSwapChain(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||
SwapChainSupportDetails details = querySwapChainSupportDetails(device, surface);
|
||||
return !(details.presentModes.empty() || details.formats.empty());
|
||||
}
|
||||
86
src/App/private/vulkan_utils.cpp
Normal file
86
src/App/private/vulkan_utils.cpp
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#include "vulkan_utils.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
bool checkValidationLayerSupport(const std::vector<const char *>& validationLayers) {
|
||||
uint32_t layerCount;
|
||||
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
|
||||
|
||||
std::vector<VkLayerProperties> availableLayers(layerCount);
|
||||
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
|
||||
|
||||
for (const auto &requestedLayer: validationLayers) {
|
||||
bool presents = false;
|
||||
for (auto &layer: availableLayers) {
|
||||
if (strcmp(requestedLayer, layer.layerName) == 0) {
|
||||
presents = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!presents) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool checkDeviceExtensions(VkPhysicalDevice device, const std::vector<const char *>& deviceExtensions) {
|
||||
uint32_t extensionsCount = 0;
|
||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionsCount, nullptr);
|
||||
|
||||
std::vector<VkExtensionProperties> extensions(extensionsCount);
|
||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionsCount, extensions.data());
|
||||
|
||||
for (const auto &requiredExtension: deviceExtensions) {
|
||||
bool found = false;
|
||||
for (const auto &extension: extensions) {
|
||||
if (strcmp(requiredExtension, extension.extensionName) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
VkInstance createInstance(const char ** instanceExtensions, uint32_t extensionsCount) {
|
||||
VkInstance out = VK_NULL_HANDLE;
|
||||
|
||||
const std::vector<const char *> validationLayers = {
|
||||
#ifdef NDEBUG
|
||||
#else
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
#endif
|
||||
};
|
||||
|
||||
if (!checkValidationLayerSupport(validationLayers)) {
|
||||
throw std::runtime_error("no required validation layers present");
|
||||
}
|
||||
|
||||
VkApplicationInfo appInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
.pApplicationName = "App",
|
||||
.applicationVersion = VK_MAKE_VERSION(0, 0, 0),
|
||||
.pEngineName = "no",
|
||||
.engineVersion = VK_MAKE_VERSION(0, 0, 0),
|
||||
.apiVersion = VK_API_VERSION_1_0,
|
||||
};
|
||||
|
||||
VkInstanceCreateInfo createInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
.pApplicationInfo = &appInfo,
|
||||
.enabledLayerCount = (uint32_t) validationLayers.size(),
|
||||
.ppEnabledLayerNames = validationLayers.data(),
|
||||
.enabledExtensionCount = extensionsCount,
|
||||
.ppEnabledExtensionNames = instanceExtensions,
|
||||
};
|
||||
|
||||
if (vkCreateInstance(&createInfo, nullptr, &out) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create instance!");
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
#define GLM_FORCE_RADIANS
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct Vertex {
|
||||
77
src/App/public/renderer.hpp
Normal file
77
src/App/public/renderer.hpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
|
||||
#include "shader.hpp"
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
struct Renderer {
|
||||
CustomShader mShader;
|
||||
VkPipeline mGraphicsPipeline = VK_NULL_HANDLE;
|
||||
VkRenderPass mGraphicsRenderPass = VK_NULL_HANDLE;
|
||||
VkPipelineLayout mGraphicsPipelineLayout{}; // no uniforms used in the shader
|
||||
|
||||
// Buffers
|
||||
VkBuffer mVertexBuffer = VK_NULL_HANDLE;
|
||||
VkDeviceMemory mVertexBufferMemory = VK_NULL_HANDLE;
|
||||
|
||||
VkBuffer mIndexBuffer = VK_NULL_HANDLE;
|
||||
VkDeviceMemory mIndexBufferMemory = VK_NULL_HANDLE;
|
||||
|
||||
VkDescriptorPool mDescriptorPool = VK_NULL_HANDLE;
|
||||
VkDescriptorSet mDescriptorSet = VK_NULL_HANDLE;
|
||||
|
||||
VkBuffer mUniformBuffer = VK_NULL_HANDLE;
|
||||
VkDeviceMemory mUniformBufferMemory = VK_NULL_HANDLE;
|
||||
void* mUniformBufferMemoryMapped = nullptr;
|
||||
|
||||
struct CreateInfo {
|
||||
VkDevice device;
|
||||
VkPhysicalDevice physicalDevice;
|
||||
VkExtent2D extent2D;
|
||||
VkFormat format;
|
||||
|
||||
VkCommandPool commandPool;
|
||||
VkQueue queue;
|
||||
};
|
||||
|
||||
void create(const CreateInfo& info);
|
||||
|
||||
void destroy(VkDevice device) const;
|
||||
|
||||
void createDescriptorPool(VkDevice device);
|
||||
|
||||
void createDescriptorSets(VkDevice device);
|
||||
|
||||
void createGraphicsPipeline(VkDevice device, VkExtent2D extent2D);
|
||||
|
||||
void createRenderPass(VkDevice device, VkFormat format);
|
||||
|
||||
void
|
||||
createIndexBuffer(VkPhysicalDevice physicalDevice, VkDevice device, VkQueue queue, VkCommandPool commandPool);
|
||||
|
||||
void
|
||||
createVertexBuffer(VkPhysicalDevice physicalDevice, VkDevice device, VkQueue queue, VkCommandPool commandPool);
|
||||
|
||||
void createUniformBuffer(VkPhysicalDevice physicalDevice, VkDevice device);
|
||||
|
||||
|
||||
void populateGraphicsCommandBuffer(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend);
|
||||
|
||||
struct BufferCreateInfo {
|
||||
VkBuffer *buffer;
|
||||
VkDeviceMemory *memory;
|
||||
VkMemoryPropertyFlags properties;
|
||||
VkDeviceSize size;
|
||||
VkBufferUsageFlags usage;
|
||||
};
|
||||
|
||||
static void createBuffer(VkPhysicalDevice physicalDevice, VkDevice device, const BufferCreateInfo& createInfo);
|
||||
|
||||
static uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags flags);
|
||||
|
||||
static void copyBuffer(VkDevice device, VkCommandPool commandPool, VkQueue queue, VkBuffer src, VkBuffer dst,
|
||||
VkDeviceSize size);
|
||||
|
||||
static void destroyBuffer(VkDevice device, VkBuffer buffer, VkDeviceMemory memory);
|
||||
|
||||
void destroyGraphicsPipeline(VkDevice device) const;
|
||||
};
|
||||
48
src/App/public/swapchain.hpp
Normal file
48
src/App/public/swapchain.hpp
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vector>
|
||||
|
||||
struct SwapChain {
|
||||
|
||||
struct SwapChainSupportDetails {
|
||||
VkSurfaceCapabilitiesKHR capabilities{};
|
||||
std::vector<VkSurfaceFormatKHR> formats;
|
||||
std::vector<VkPresentModeKHR> presentModes;
|
||||
};
|
||||
|
||||
VkSwapchainKHR mSwapChain = VK_NULL_HANDLE;
|
||||
VkFormat mSwapChainFormat{};
|
||||
VkExtent2D mSwapChainExtent{};
|
||||
std::vector<VkImage> mSwapChainImages;
|
||||
std::vector<VkImageView> mSwapChainImageViews;
|
||||
std::vector<VkFramebuffer> mSwapChainFrameBuffers;
|
||||
|
||||
void destroySwapChain(VkDevice device);
|
||||
|
||||
struct CreateInfo {
|
||||
VkDevice device = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
||||
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
||||
int sizeX = 0;
|
||||
int sizeY = 0;
|
||||
uint32_t graphicsQueueFamilyIndex = -1;
|
||||
uint32_t presentationQueueFamilyIndex = -1;
|
||||
};
|
||||
|
||||
void createSwapChain(const CreateInfo &info);
|
||||
|
||||
void createSwapChainImageViews(VkDevice device);
|
||||
|
||||
void createSwapChainFramebuffers(VkDevice device, VkRenderPass renderPass);
|
||||
|
||||
static VkSurfaceFormatKHR pickSwapChainSurfaceFormat(const SwapChainSupportDetails &details);
|
||||
|
||||
static VkPresentModeKHR pickSwapChainPresentMode(const SwapChainSupportDetails &details);
|
||||
|
||||
static VkExtent2D pickSwapChainExtent(const VkSurfaceCapabilitiesKHR &capabilities, int sizeX, int sizeY);
|
||||
|
||||
static SwapChainSupportDetails querySwapChainSupportDetails(VkPhysicalDevice device, VkSurfaceKHR surface);
|
||||
|
||||
static bool checkDeviceSwapChain(VkPhysicalDevice device, VkSurfaceKHR surface);
|
||||
};
|
||||
9
src/App/public/vulkan_utils.hpp
Normal file
9
src/App/public/vulkan_utils.hpp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vector>
|
||||
|
||||
bool checkValidationLayerSupport(const std::vector<const char *>& validationLayers);
|
||||
bool checkDeviceExtensions(VkPhysicalDevice device, const std::vector<const char *>& deviceExtensions);
|
||||
|
||||
VkInstance createInstance(const char ** instanceExtensions, uint32_t extensionsCount);
|
||||
6
src/Common/CMakeLists.txt
Normal file
6
src/Common/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.30)
|
||||
|
||||
project(Common)
|
||||
|
||||
add_library(${PROJECT_NAME} utils.cpp)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC .)
|
||||
7
src/GPU/CMakeLists.txt
Normal file
7
src/GPU/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
cmake_minimum_required(VERSION 3.30)
|
||||
|
||||
project(GPU)
|
||||
|
||||
add_library(${PROJECT_NAME} private/GPU.cpp)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC public)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC vulkan Common)
|
||||
0
src/GPU/private/GPU.cpp
Normal file
0
src/GPU/private/GPU.cpp
Normal file
1
src/GPU/public/GPU.hpp
Normal file
1
src/GPU/public/GPU.hpp
Normal file
|
|
@ -0,0 +1 @@
|
|||
#pragma once
|
||||
7
src/PlatformWindow/CMakeLists.txt
Normal file
7
src/PlatformWindow/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
cmake_minimum_required(VERSION 3.30)
|
||||
|
||||
project(PlatformWindow)
|
||||
|
||||
add_library(${PROJECT_NAME} private/PlatformWindow.cpp)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC public)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC glfw GPU)
|
||||
0
src/PlatformWindow/private/PlatformWindow.cpp
Normal file
0
src/PlatformWindow/private/PlatformWindow.cpp
Normal file
1
src/PlatformWindow/public/PlatformWindow.hpp
Normal file
1
src/PlatformWindow/public/PlatformWindow.hpp
Normal file
|
|
@ -0,0 +1 @@
|
|||
#pragma once
|
||||
1151
src/main.cpp
1151
src/main.cpp
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue