very crude first triangle pre version
This commit is contained in:
parent
649fdb795f
commit
8d5f6e8a3e
@ -1,7 +1,24 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
project(stories)
|
||||
project(stories VERSION 0.0.1 LANGUAGES CXX)
|
||||
cmake_policy(SET CMP0076 NEW)
|
||||
cmake_policy(SET CMP0079 NEW)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(Vulkan REQUIRED)
|
||||
|
||||
add_executable(stories main.cpp)
|
||||
target_include_directories(stories PRIVATE ${CMAKE_SOURCE_DIR})
|
||||
|
||||
target_link_libraries(stories
|
||||
engine
|
||||
SDL2::SDL2
|
||||
Vulkan::Vulkan
|
||||
)
|
||||
|
||||
add_subdirectory(engine)
|
||||
add_subdirectory(shaders)
|
||||
|
||||
install(TARGETS stories RUNTIME DESTINATION bin)
|
||||
|
||||
|
25
engine/CMakeLists.txt
Normal file
25
engine/CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
||||
set(SOURCES
|
||||
instance.cpp
|
||||
engine.cpp
|
||||
utils.cpp
|
||||
window.cpp
|
||||
surface.cpp
|
||||
physicaldevice.cpp
|
||||
logicaldevice.cpp
|
||||
swapchain.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
instance.h
|
||||
engine.h
|
||||
utils.h
|
||||
window.h
|
||||
surface.h
|
||||
physicaldevice.h
|
||||
logicaldevice.h
|
||||
swapchain.h
|
||||
)
|
||||
|
||||
add_library(engine STATIC ${SOURCES})
|
||||
|
||||
target_include_directories(engine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
138
engine/engine.cpp
Normal file
138
engine/engine.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
#include "engine.h"
|
||||
#include "instance.h"
|
||||
|
||||
constexpr const char* validationLayerName("VK_LAYER_KHRONOS_validation");
|
||||
|
||||
Engine::Engine::Engine() :
|
||||
initialized(false),
|
||||
window(new Window()),
|
||||
instance(nullptr),
|
||||
surface(nullptr),
|
||||
physicalDevice(nullptr),
|
||||
logicalDevice(nullptr),
|
||||
layerNames(),
|
||||
instanceExtensionNames(),
|
||||
deviceExtensionNames(),
|
||||
layers(),
|
||||
instanceExtensions(),
|
||||
deviceExtensions()
|
||||
{
|
||||
addDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
Engine::Engine::~Engine() {
|
||||
delete window;
|
||||
}
|
||||
|
||||
bool Engine::Engine::enableValidationLayers() const {
|
||||
return instanceExtensionNames.count(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) > 0 &&
|
||||
instanceExtensionNames.count(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) > 0 &&
|
||||
layerNames.count(validationLayerName) > 0;
|
||||
}
|
||||
|
||||
void Engine::Engine::addLayer(const std::string& layerName) {
|
||||
if (initialized)
|
||||
throw std::runtime_error("Adding layers to an initialized engine is not supported yet");
|
||||
|
||||
layerNames.insert(layerName);
|
||||
}
|
||||
|
||||
void Engine::Engine::addInstanceExtension(const std::string& extensionName) {
|
||||
if (initialized)
|
||||
throw std::runtime_error("Adding extension to an initialized engine is not supported yet");
|
||||
|
||||
std::pair<std::set<std::string>::const_iterator, bool> pair = instanceExtensionNames.insert(extensionName);
|
||||
if (pair.second)
|
||||
instanceExtensions.push_back(pair.first->c_str());
|
||||
}
|
||||
|
||||
void Engine::Engine::addDeviceExtension(const std::string& extensionName) {
|
||||
if (initialized)
|
||||
throw std::runtime_error("Adding extension to an initialized engine is not supported yet");
|
||||
|
||||
std::pair<std::set<std::string>::const_iterator, bool> pair = deviceExtensionNames.insert(extensionName);
|
||||
if (pair.second)
|
||||
deviceExtensions.push_back(pair.first->c_str());
|
||||
}
|
||||
|
||||
void Engine::Engine::enableDebug() {
|
||||
addLayer(validationLayerName);
|
||||
addInstanceExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
|
||||
addInstanceExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void Engine::Engine::initVulkan() {
|
||||
instance = new Instance(this);
|
||||
surface = new Surface(instance, window);
|
||||
|
||||
pickPhysicalDevice();
|
||||
logicalDevice = new LogicalDevice(physicalDevice, surface, deviceExtensions, layers);
|
||||
|
||||
logicalDevice->createGraphicsPipeline("shaders/shader.vert.spv", "shaders/shader.frag.spv");
|
||||
logicalDevice->createSwapChain();
|
||||
}
|
||||
|
||||
void Engine::Engine::cleanup() {
|
||||
delete logicalDevice;
|
||||
logicalDevice = nullptr;
|
||||
|
||||
delete physicalDevice;
|
||||
physicalDevice = nullptr;
|
||||
|
||||
delete surface;
|
||||
surface = nullptr;
|
||||
|
||||
delete instance;
|
||||
instance = nullptr;
|
||||
}
|
||||
|
||||
void Engine::Engine::pickPhysicalDevice() {
|
||||
std::vector<VkPhysicalDevice> devices = instance->enumeratePhysicalDevices();
|
||||
if (devices.size() == 0)
|
||||
throw std::runtime_error("failed to find GPUs with Vulkan support!");
|
||||
|
||||
for (const auto& device : devices) {
|
||||
if (PhysicalDevice::checkDeviceExtensionSupport(device, deviceExtensionNames)) {
|
||||
QueueFamilyIndices indices = surface->findQueueFamilies(device);
|
||||
if (indices.isComplete()) {
|
||||
SwapChainSupportDetails scsd = surface->querySwapChainSupport(device);
|
||||
if (scsd.adequate()) {
|
||||
physicalDevice = new PhysicalDevice(device, indices, scsd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (physicalDevice == nullptr)
|
||||
throw std::runtime_error("failed to find a suitable GPU!");
|
||||
}
|
||||
|
||||
std::vector<const char *> Engine::Engine::getRequiredVulkanExtensions() const {
|
||||
return window->getRequiredVulkanExtensions();
|
||||
}
|
||||
|
||||
void Engine::Engine::run() {
|
||||
initVulkan();
|
||||
mainLoop();
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void Engine::Engine::mainLoop() {
|
||||
SDL_Event e;
|
||||
bool bQuit = false;
|
||||
|
||||
//main loop
|
||||
while (!bQuit) {
|
||||
//Handle events on queue
|
||||
while (SDL_PollEvent(&e) != 0) {
|
||||
//close the window when user clicks the X button or alt-f4s
|
||||
if (e.type == SDL_QUIT)
|
||||
bQuit = true;
|
||||
}
|
||||
|
||||
logicalDevice->drawFrame();
|
||||
}
|
||||
|
||||
logicalDevice->waitIdle();
|
||||
}
|
70
engine/engine.h
Normal file
70
engine/engine.h
Normal file
@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
|
||||
#include "window.h"
|
||||
#include "surface.h"
|
||||
#include "physicaldevice.h"
|
||||
#include "logicaldevice.h"
|
||||
|
||||
namespace Engine {
|
||||
|
||||
const int MAX_FRAMES_IN_FLIGHT = 2;
|
||||
|
||||
class Instance;
|
||||
|
||||
class Engine {
|
||||
friend class Instance;
|
||||
public:
|
||||
Engine();
|
||||
~Engine();
|
||||
|
||||
void run();
|
||||
bool enableValidationLayers() const;
|
||||
void addLayer(const std::string& layerName);
|
||||
void addInstanceExtension(const std::string& extensionName);
|
||||
void addDeviceExtension(const std::string& extensionName);
|
||||
void enableDebug();
|
||||
std::vector<const char *> getRequiredVulkanExtensions() const;
|
||||
|
||||
private:
|
||||
bool initialized;
|
||||
Window* window;
|
||||
Instance* instance;
|
||||
Surface* surface;
|
||||
PhysicalDevice* physicalDevice;
|
||||
LogicalDevice* logicalDevice;
|
||||
std::set<std::string> layerNames;
|
||||
std::set<std::string> instanceExtensionNames;
|
||||
std::set<std::string> deviceExtensionNames;
|
||||
std::vector<const char*> layers;
|
||||
std::vector<const char*> instanceExtensions;
|
||||
std::vector<const char*> deviceExtensions;
|
||||
|
||||
void initVulkan();
|
||||
void mainLoop();
|
||||
void cleanup();
|
||||
void pickPhysicalDevice();
|
||||
};
|
||||
|
||||
}
|
||||
|
110
engine/instance.cpp
Normal file
110
engine/instance.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
#include "instance.h"
|
||||
|
||||
#include "engine.h"
|
||||
|
||||
Engine::Instance::Instance(Engine* eng) :
|
||||
validationLayersEnabledAndSupported(false),
|
||||
engine(eng),
|
||||
vk(),
|
||||
debugMessenger()
|
||||
{
|
||||
VkApplicationInfo appInfo {};
|
||||
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
appInfo.pApplicationName = "Hello Triangle";
|
||||
appInfo.applicationVersion = VK_MAKE_VERSION(0, 0, 1);
|
||||
appInfo.pEngineName = "Stories";
|
||||
appInfo.engineVersion = VK_MAKE_VERSION(0, 0, 1);
|
||||
appInfo.apiVersion = VK_API_VERSION_1_0;
|
||||
appInfo.pNext = nullptr;
|
||||
|
||||
//TODO handle exception
|
||||
Support exts = getVulkanExtensions(engine->instanceExtensionNames, engine->getRequiredVulkanExtensions());
|
||||
if (exts.unsupportedCritical.size() > 0) {
|
||||
std::string error("Unable to create Vulkan instance, following critical extensions are unsupported:\n");
|
||||
for (const char* ext : exts.unsupportedCritical) {
|
||||
error += "\t" + std::string(ext) + "\n";
|
||||
}
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
//TODO log unsupported;
|
||||
exts.logSupported("Applying extensions:");
|
||||
|
||||
//TODO handle exception
|
||||
Support layers = getVulkanLayers(engine->layerNames);
|
||||
//TODO log unsupported
|
||||
layers.logSupported("Applying layers:");
|
||||
|
||||
VkInstanceCreateInfo createInfo {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
createInfo.pApplicationInfo = &appInfo;
|
||||
createInfo.flags = 0;
|
||||
createInfo.enabledExtensionCount = static_cast<uint32_t>(exts.supported.size());
|
||||
createInfo.ppEnabledExtensionNames = exts.supported.data();
|
||||
createInfo.enabledLayerCount = static_cast<uint32_t>(layers.supported.size());
|
||||
createInfo.ppEnabledLayerNames = layers.supported.data();
|
||||
|
||||
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo {};
|
||||
if (engine->enableValidationLayers()) {
|
||||
validationLayersEnabledAndSupported = true; //TODO make sure it's actually supported, this shows that they are just enabled;
|
||||
populateDebugMessengerCreateInfo(debugCreateInfo, debugCallback);
|
||||
createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo;
|
||||
}
|
||||
|
||||
VkResult res = vkCreateInstance(&createInfo, nullptr, &vk);
|
||||
switch (res) {
|
||||
case VK_SUCCESS:
|
||||
break;
|
||||
case VK_ERROR_INCOMPATIBLE_DRIVER:
|
||||
throw std::runtime_error("unable to create vulkan instance, cannot find a compatible Vulkan ICD");
|
||||
case VK_ERROR_OUT_OF_HOST_MEMORY:
|
||||
throw std::runtime_error("unable to create vulkan instance, out of host memory");
|
||||
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
|
||||
throw std::runtime_error("unable to create vulkan instance, out of device memory");
|
||||
case VK_ERROR_INITIALIZATION_FAILED:
|
||||
throw std::runtime_error("unable to create vulkan instance, initialization failed");
|
||||
case VK_ERROR_LAYER_NOT_PRESENT:
|
||||
throw std::runtime_error("unable to create vulkan instance, layer not present");
|
||||
case VK_ERROR_EXTENSION_NOT_PRESENT:
|
||||
throw std::runtime_error("unable to create vulkan instance, extension not present");
|
||||
default:
|
||||
throw std::runtime_error("unable to create Vulkan instance: unknown error");
|
||||
}
|
||||
|
||||
if (validationLayersEnabledAndSupported)
|
||||
setupDebugMessenger();
|
||||
}
|
||||
|
||||
Engine::Instance::~Instance() {
|
||||
if (validationLayersEnabledAndSupported)
|
||||
destroyDebugUtilsMessengerEXT(vk, debugMessenger, nullptr);
|
||||
|
||||
vkDestroyInstance(vk, nullptr);
|
||||
}
|
||||
|
||||
void Engine::Instance::setupDebugMessenger() {
|
||||
VkDebugUtilsMessengerCreateInfoEXT createInfo{};
|
||||
populateDebugMessengerCreateInfo(createInfo, debugCallback);
|
||||
|
||||
VkResult res = createDebugUtilsMessengerEXT(vk, &createInfo, nullptr, &debugMessenger);
|
||||
switch (res) {
|
||||
case VK_SUCCESS:
|
||||
break;
|
||||
case VK_ERROR_EXTENSION_NOT_PRESENT:
|
||||
throw std::runtime_error("failed to set up debug messenger: extension not present");
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("failed to set up debug messenger");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<VkPhysicalDevice> Engine::Instance::enumeratePhysicalDevices() const {
|
||||
uint32_t deviceCount = 0;
|
||||
vkEnumeratePhysicalDevices(vk, &deviceCount, nullptr);
|
||||
|
||||
std::vector<VkPhysicalDevice> devices(deviceCount);
|
||||
if (deviceCount > 0)
|
||||
vkEnumeratePhysicalDevices(vk, &deviceCount, devices.data());
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
33
engine/instance.h
Normal file
33
engine/instance.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vector>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Engine;
|
||||
class Window;
|
||||
class Surface;
|
||||
|
||||
class Instance {
|
||||
friend class Engine;
|
||||
friend class Surface;
|
||||
public:
|
||||
Instance(Engine* engine);
|
||||
~Instance();
|
||||
|
||||
std::vector<VkPhysicalDevice> enumeratePhysicalDevices() const;
|
||||
|
||||
private:
|
||||
void setupDebugMessenger();
|
||||
|
||||
private:
|
||||
bool validationLayersEnabledAndSupported;
|
||||
Engine* engine;
|
||||
VkInstance vk;
|
||||
VkDebugUtilsMessengerEXT debugMessenger;
|
||||
};
|
||||
|
||||
}
|
568
engine/logicaldevice.cpp
Normal file
568
engine/logicaldevice.cpp
Normal file
@ -0,0 +1,568 @@
|
||||
#include "logicaldevice.h"
|
||||
|
||||
#include "swapchain.h"
|
||||
|
||||
constexpr int MAX_FRAMES_IN_FLIGHT = 2;
|
||||
constexpr float queuePriority = 1.0f;
|
||||
|
||||
Engine::LogicalDevice::LogicalDevice(
|
||||
PhysicalDevice* physicalDevice,
|
||||
Surface* surface,
|
||||
const std::vector<const char*>& extensions,
|
||||
const std::vector<const char*> layers
|
||||
):
|
||||
phys(physicalDevice),
|
||||
vk(),
|
||||
graphicsQueue(),
|
||||
presentQueue(),
|
||||
renderPass(),
|
||||
pipelineLayout(),
|
||||
graphicsPipeline(),
|
||||
commandPool(),
|
||||
commandBuffers(),
|
||||
imageAvailableSemaphores(),
|
||||
renderFinishedSemaphores(),
|
||||
inFlightFences(),
|
||||
currentFrame(0),
|
||||
framebufferResized(false),
|
||||
hasPipeline(false),
|
||||
surface(surface),
|
||||
surfaceFormat(surface->chooseSwapSurfaceFormat(phys->swapChainSupport)),
|
||||
swapChain(nullptr)
|
||||
{
|
||||
const QueueFamilyIndices& indices = phys->indices;
|
||||
|
||||
createDevice(phys->vk, indices, extensions, layers);
|
||||
createQueues(indices.graphicsFamily.value(), indices.presentFamily.value());
|
||||
|
||||
createCommandPool(phys->indices.graphicsFamily.value());
|
||||
createCommandBuffers();
|
||||
createSyncObjects();
|
||||
createRenderPass(surfaceFormat.format);
|
||||
}
|
||||
|
||||
Engine::LogicalDevice::~LogicalDevice() {
|
||||
clearSwapChain();
|
||||
|
||||
if (hasPipeline) {
|
||||
vkDestroyPipeline(vk, graphicsPipeline, nullptr);
|
||||
vkDestroyPipelineLayout(vk, pipelineLayout, nullptr);
|
||||
}
|
||||
|
||||
vkDestroyRenderPass(vk, renderPass, nullptr);
|
||||
|
||||
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||||
vkDestroySemaphore(vk, renderFinishedSemaphores[i], nullptr);
|
||||
vkDestroySemaphore(vk, imageAvailableSemaphores[i], nullptr);
|
||||
vkDestroyFence(vk, inFlightFences[i], nullptr);
|
||||
}
|
||||
|
||||
vkFreeCommandBuffers(vk, commandPool, MAX_FRAMES_IN_FLIGHT, commandBuffers.data());
|
||||
vkDestroyCommandPool(vk, commandPool, nullptr);
|
||||
|
||||
vkDestroyDevice(vk, nullptr);
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::createSwapChain() {
|
||||
clearSwapChain();
|
||||
swapChain = new SwapChain(this);
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::clearSwapChain() {
|
||||
if (swapChain != nullptr)
|
||||
delete swapChain;
|
||||
|
||||
swapChain = nullptr;
|
||||
}
|
||||
|
||||
VkResult Engine::LogicalDevice::waitIdle() {
|
||||
return vkDeviceWaitIdle(vk);
|
||||
}
|
||||
|
||||
VkResult Engine::LogicalDevice::waitForFence(const VkFence& fence) {
|
||||
return vkWaitForFences(vk, 1, &fence, VK_TRUE, UINT64_MAX);
|
||||
}
|
||||
|
||||
VkResult Engine::LogicalDevice::resetFence(const VkFence& fence) {
|
||||
return vkResetFences(vk, 1, &fence);
|
||||
}
|
||||
|
||||
VkResult Engine::LogicalDevice::queueSubmitGraphics(
|
||||
const VkSemaphore& wait,
|
||||
const VkCommandBuffer& buffer,
|
||||
const VkSemaphore& signal,
|
||||
const VkFence& fence
|
||||
) {
|
||||
VkSubmitInfo submitInfo{};
|
||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
|
||||
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
||||
submitInfo.waitSemaphoreCount = 1;
|
||||
submitInfo.pWaitSemaphores = &wait;
|
||||
submitInfo.pWaitDstStageMask = waitStages;
|
||||
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = &buffer;
|
||||
|
||||
submitInfo.signalSemaphoreCount = 1;
|
||||
submitInfo.pSignalSemaphores = &signal;
|
||||
|
||||
return vkQueueSubmit(graphicsQueue, 1, &submitInfo, fence);
|
||||
}
|
||||
|
||||
VkResult Engine::LogicalDevice::queuePresent(const VkSemaphore& signal, uint32_t index) {
|
||||
VkPresentInfoKHR presentInfo{};
|
||||
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
|
||||
presentInfo.waitSemaphoreCount = 1;
|
||||
presentInfo.pWaitSemaphores = &signal;
|
||||
|
||||
presentInfo.swapchainCount = 1;
|
||||
presentInfo.pSwapchains = &swapChain->vk;
|
||||
|
||||
presentInfo.pImageIndices = &index;
|
||||
|
||||
return vkQueuePresentKHR(presentQueue, &presentInfo);
|
||||
}
|
||||
|
||||
VkShaderModule Engine::LogicalDevice::createShaderModule(const std::string& path) {
|
||||
std::vector<char> code = readFile(path);
|
||||
|
||||
VkShaderModuleCreateInfo createInfo{};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
createInfo.codeSize = code.size();
|
||||
createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
|
||||
|
||||
VkShaderModule shaderModule;
|
||||
if (vkCreateShaderModule(vk, &createInfo, nullptr, &shaderModule) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create shader module!");
|
||||
|
||||
return shaderModule;
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::destroyShaderModule(VkShaderModule shaderModule) {
|
||||
vkDestroyShaderModule(vk, shaderModule, nullptr);
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::createDevice(
|
||||
VkPhysicalDevice physicalDevice,
|
||||
const QueueFamilyIndices& indices,
|
||||
const std::vector<const char*>& extensions,
|
||||
const std::vector<const char*> layers
|
||||
) {
|
||||
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
|
||||
std::set<uint32_t> uniqueQueueFamilies = {indices.graphicsFamily.value(), indices.presentFamily.value()};
|
||||
|
||||
|
||||
for (uint32_t queueFamily : uniqueQueueFamilies) {
|
||||
VkDeviceQueueCreateInfo queueCreateInfo{};
|
||||
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||
queueCreateInfo.queueFamilyIndex = queueFamily;
|
||||
queueCreateInfo.queueCount = 1;
|
||||
queueCreateInfo.pQueuePriorities = &queuePriority;
|
||||
queueCreateInfos.push_back(queueCreateInfo);
|
||||
}
|
||||
|
||||
VkPhysicalDeviceFeatures deviceFeatures{};
|
||||
|
||||
VkDeviceCreateInfo createInfo{};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||
|
||||
createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
|
||||
createInfo.pQueueCreateInfos = queueCreateInfos.data();
|
||||
|
||||
createInfo.pEnabledFeatures = &deviceFeatures;
|
||||
|
||||
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
|
||||
createInfo.ppEnabledExtensionNames = extensions.data();
|
||||
|
||||
createInfo.enabledLayerCount = static_cast<uint32_t>(layers.size());
|
||||
createInfo.ppEnabledLayerNames = layers.data();
|
||||
|
||||
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &vk) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create logical device!");
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::createQueues(uint32_t graphicsFamilyIndex, uint32_t presenFamilyIndex) {
|
||||
vkGetDeviceQueue(vk, graphicsFamilyIndex, 0, &graphicsQueue);
|
||||
vkGetDeviceQueue(vk, presenFamilyIndex, 0, &presentQueue);
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::createSyncObjects() {
|
||||
imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
|
||||
VkSemaphoreCreateInfo semaphoreInfo{};
|
||||
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
|
||||
VkFenceCreateInfo fenceInfo{};
|
||||
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||||
|
||||
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||||
if (vkCreateSemaphore(vk, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
|
||||
vkCreateSemaphore(vk, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
|
||||
vkCreateFence(vk, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create synchronization objects for a frame!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::createCommandPool(uint32_t queueFamilyIndex) {
|
||||
VkCommandPoolCreateInfo poolInfo{};
|
||||
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||
poolInfo.queueFamilyIndex = queueFamilyIndex;
|
||||
|
||||
if (vkCreateCommandPool(vk, &poolInfo, nullptr, &commandPool) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create command pool!");
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::createCommandBuffers() {
|
||||
commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
|
||||
VkCommandBufferAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
allocInfo.commandPool = commandPool;
|
||||
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
allocInfo.commandBufferCount = (uint32_t) commandBuffers.size();
|
||||
|
||||
if (vkAllocateCommandBuffers(vk, &allocInfo, commandBuffers.data()) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to allocate command buffers!");
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::drawFrame() {
|
||||
waitForFence(inFlightFences[currentFrame]);
|
||||
|
||||
uint32_t imageIndex;
|
||||
VkResult result = vkAcquireNextImageKHR(vk, swapChain->vk, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
|
||||
|
||||
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||
recreateSwapChain();
|
||||
return;
|
||||
} else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
|
||||
throw std::runtime_error("failed to acquire swap chain image!");
|
||||
}
|
||||
|
||||
resetFence(inFlightFences[currentFrame]);
|
||||
|
||||
vkResetCommandBuffer(commandBuffers[currentFrame], /*VkCommandBufferResetFlagBits*/ 0);
|
||||
recordCommandBuffer(commandBuffers[currentFrame], imageIndex);
|
||||
|
||||
if (queueSubmitGraphics(
|
||||
imageAvailableSemaphores[currentFrame],
|
||||
commandBuffers[currentFrame],
|
||||
renderFinishedSemaphores[currentFrame],
|
||||
inFlightFences[currentFrame]) != VK_SUCCESS
|
||||
)
|
||||
throw std::runtime_error("failed to submit draw command buffer!");
|
||||
|
||||
result = queuePresent(renderFinishedSemaphores[currentFrame], imageIndex);
|
||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
|
||||
framebufferResized = false;
|
||||
recreateSwapChain();
|
||||
} else if (result != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to present swap chain image!");
|
||||
}
|
||||
|
||||
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||
}
|
||||
|
||||
VkExtent2D Engine::LogicalDevice::chooseSwapExtent() const {
|
||||
return surface->chooseSwapExtent(phys->swapChainSupport.capabilities);
|
||||
}
|
||||
|
||||
VkResult Engine::LogicalDevice::createVkSwapChain(const VkExtent2D& extent, VkSwapchainKHR& out) {
|
||||
const SwapChainSupportDetails& swapChainSupport = phys->swapChainSupport;
|
||||
|
||||
uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
|
||||
if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount)
|
||||
imageCount = swapChainSupport.capabilities.maxImageCount;
|
||||
|
||||
VkSwapchainCreateInfoKHR createInfo{};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||
createInfo.surface = surface->vk;
|
||||
|
||||
createInfo.imageFormat = surfaceFormat.format;
|
||||
createInfo.imageColorSpace = surfaceFormat.colorSpace;
|
||||
createInfo.imageArrayLayers = 1;
|
||||
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
|
||||
createInfo.imageExtent = extent;
|
||||
createInfo.minImageCount = imageCount;
|
||||
|
||||
const QueueFamilyIndices& indices = phys->indices;
|
||||
uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()};
|
||||
|
||||
if (indices.graphicsFamily != indices.presentFamily) {
|
||||
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||
createInfo.queueFamilyIndexCount = 2;
|
||||
createInfo.pQueueFamilyIndices = queueFamilyIndices;
|
||||
} else {
|
||||
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
}
|
||||
|
||||
createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
|
||||
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
createInfo.presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
|
||||
createInfo.clipped = VK_TRUE;
|
||||
|
||||
return vkCreateSwapchainKHR(vk, &createInfo, nullptr, &out);
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::destroyVkSwapChain(VkSwapchainKHR& swapChain) {
|
||||
vkDestroySwapchainKHR(vk, swapChain, nullptr);
|
||||
}
|
||||
|
||||
VkResult Engine::LogicalDevice::getVkSwapChainImages(VkSwapchainKHR& swapChain, std::vector<VkImage>& out) {
|
||||
uint32_t imageCount;
|
||||
VkResult result = vkGetSwapchainImagesKHR(vk, swapChain, &imageCount, nullptr);
|
||||
if (result != VK_SUCCESS)
|
||||
return result;
|
||||
|
||||
out.resize(imageCount);
|
||||
return vkGetSwapchainImagesKHR(vk, swapChain, &imageCount, out.data());
|
||||
}
|
||||
|
||||
VkResult Engine::LogicalDevice::createVkImageView(const VkImage& image, VkImageView& out) {
|
||||
VkImageViewCreateInfo createInfo{};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
createInfo.image = image;
|
||||
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
createInfo.format = surfaceFormat.format;
|
||||
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
createInfo.subresourceRange.baseMipLevel = 0;
|
||||
createInfo.subresourceRange.levelCount = 1;
|
||||
createInfo.subresourceRange.baseArrayLayer = 0;
|
||||
createInfo.subresourceRange.layerCount = 1;
|
||||
|
||||
return vkCreateImageView(vk, &createInfo, nullptr, &out);
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::destroyVkImageView(VkImageView& view) {
|
||||
vkDestroyImageView(vk, view, nullptr);
|
||||
}
|
||||
|
||||
VkResult Engine::LogicalDevice::createVkFrameBuffer(const VkImageView& imageView, const VkExtent2D& extent, VkFramebuffer& out) {
|
||||
VkImageView attachments[] = {imageView};
|
||||
|
||||
VkFramebufferCreateInfo framebufferInfo{};
|
||||
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||
framebufferInfo.renderPass = renderPass;
|
||||
framebufferInfo.attachmentCount = 1;
|
||||
framebufferInfo.pAttachments = attachments;
|
||||
framebufferInfo.width = extent.width;
|
||||
framebufferInfo.height = extent.height;
|
||||
framebufferInfo.layers = 1;
|
||||
|
||||
return vkCreateFramebuffer(vk, &framebufferInfo, nullptr, &out);
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::destroyVkFrameBuffer(VkFramebuffer& buffer) {
|
||||
vkDestroyFramebuffer(vk, buffer, nullptr);
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::createRenderPass(VkFormat format) {
|
||||
VkAttachmentDescription colorAttachment{};
|
||||
colorAttachment.format = format;
|
||||
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
VkAttachmentReference colorAttachmentRef{};
|
||||
colorAttachmentRef.attachment = 0;
|
||||
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkSubpassDescription subpass{};
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &colorAttachmentRef;
|
||||
|
||||
VkSubpassDependency dependency{};
|
||||
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dependency.dstSubpass = 0;
|
||||
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.srcAccessMask = 0;
|
||||
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
VkRenderPassCreateInfo renderPassInfo{};
|
||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
renderPassInfo.attachmentCount = 1;
|
||||
renderPassInfo.pAttachments = &colorAttachment;
|
||||
renderPassInfo.subpassCount = 1;
|
||||
renderPassInfo.pSubpasses = &subpass;
|
||||
renderPassInfo.dependencyCount = 1;
|
||||
renderPassInfo.pDependencies = &dependency;
|
||||
|
||||
if (vkCreateRenderPass(vk, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create render pass!");
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::createGraphicsPipeline(const std::string& vertexShaderPath, const std::string& fragmentShaderPath) {
|
||||
if (hasPipeline)
|
||||
throw std::runtime_error("an attempt to create graphics pipeline for the second time");
|
||||
|
||||
VkShaderModule vertShaderModule = createShaderModule(vertexShaderPath);
|
||||
VkShaderModule fragShaderModule = createShaderModule(fragmentShaderPath);
|
||||
|
||||
VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
|
||||
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vertShaderStageInfo.module = vertShaderModule;
|
||||
vertShaderStageInfo.pName = "main";
|
||||
|
||||
VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
|
||||
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fragShaderStageInfo.module = fragShaderModule;
|
||||
fragShaderStageInfo.pName = "main";
|
||||
|
||||
VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
|
||||
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
vertexInputInfo.vertexBindingDescriptionCount = 0;
|
||||
vertexInputInfo.vertexAttributeDescriptionCount = 0;
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
|
||||
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
inputAssembly.primitiveRestartEnable = VK_FALSE;
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewportState{};
|
||||
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
viewportState.viewportCount = 1;
|
||||
viewportState.scissorCount = 1;
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterizer{};
|
||||
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
rasterizer.depthClampEnable = VK_FALSE;
|
||||
rasterizer.rasterizerDiscardEnable = VK_FALSE;
|
||||
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
rasterizer.lineWidth = 1.0f;
|
||||
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
|
||||
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
||||
rasterizer.depthBiasEnable = VK_FALSE;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisampling{};
|
||||
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
multisampling.sampleShadingEnable = VK_FALSE;
|
||||
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
|
||||
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
colorBlendAttachment.blendEnable = VK_FALSE;
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo colorBlending{};
|
||||
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
colorBlending.logicOpEnable = VK_FALSE;
|
||||
colorBlending.logicOp = VK_LOGIC_OP_COPY;
|
||||
colorBlending.attachmentCount = 1;
|
||||
colorBlending.pAttachments = &colorBlendAttachment;
|
||||
colorBlending.blendConstants[0] = 0.0f;
|
||||
colorBlending.blendConstants[1] = 0.0f;
|
||||
colorBlending.blendConstants[2] = 0.0f;
|
||||
colorBlending.blendConstants[3] = 0.0f;
|
||||
|
||||
std::vector<VkDynamicState> dynamicStates = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR
|
||||
};
|
||||
VkPipelineDynamicStateCreateInfo dynamicState{};
|
||||
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
|
||||
dynamicState.pDynamicStates = dynamicStates.data();
|
||||
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
|
||||
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
pipelineLayoutInfo.setLayoutCount = 0;
|
||||
pipelineLayoutInfo.pushConstantRangeCount = 0;
|
||||
|
||||
if (vkCreatePipelineLayout(vk, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create pipeline layout!");
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineInfo{};
|
||||
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipelineInfo.stageCount = 2;
|
||||
pipelineInfo.pStages = shaderStages;
|
||||
pipelineInfo.pVertexInputState = &vertexInputInfo;
|
||||
pipelineInfo.pInputAssemblyState = &inputAssembly;
|
||||
pipelineInfo.pViewportState = &viewportState;
|
||||
pipelineInfo.pRasterizationState = &rasterizer;
|
||||
pipelineInfo.pMultisampleState = &multisampling;
|
||||
pipelineInfo.pColorBlendState = &colorBlending;
|
||||
pipelineInfo.pDynamicState = &dynamicState;
|
||||
pipelineInfo.layout = pipelineLayout;
|
||||
pipelineInfo.renderPass = renderPass;
|
||||
pipelineInfo.subpass = 0;
|
||||
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
|
||||
|
||||
if (vkCreateGraphicsPipelines(vk, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create graphics pipeline!");
|
||||
|
||||
destroyShaderModule(fragShaderModule);
|
||||
destroyShaderModule(vertShaderModule);
|
||||
|
||||
hasPipeline = true;
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) {
|
||||
VkExtent2D extent = swapChain->getExtent();
|
||||
VkCommandBufferBeginInfo beginInfo{};
|
||||
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
|
||||
if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to begin recording command buffer!");
|
||||
|
||||
VkRenderPassBeginInfo renderPassInfo{};
|
||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
renderPassInfo.renderPass = renderPass;
|
||||
renderPassInfo.framebuffer = swapChain->getFrameBuffer(imageIndex);
|
||||
renderPassInfo.renderArea.offset = {0, 0};
|
||||
renderPassInfo.renderArea.extent = extent;
|
||||
|
||||
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
|
||||
renderPassInfo.clearValueCount = 1;
|
||||
renderPassInfo.pClearValues = &clearColor;
|
||||
|
||||
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
|
||||
|
||||
VkViewport viewport{};
|
||||
viewport.x = 0.0f;
|
||||
viewport.y = 0.0f;
|
||||
viewport.width = (float) extent.width;
|
||||
viewport.height = (float) extent.height;
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
|
||||
|
||||
VkRect2D scissor{};
|
||||
scissor.offset = {0, 0};
|
||||
scissor.extent = extent;
|
||||
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
|
||||
|
||||
vkCmdDraw(commandBuffer, 3, 1, 0, 0);
|
||||
|
||||
vkCmdEndRenderPass(commandBuffer);
|
||||
|
||||
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to record command buffer!");
|
||||
}
|
||||
|
||||
void Engine::LogicalDevice::recreateSwapChain() {
|
||||
surface->waitForResize();
|
||||
waitIdle();
|
||||
createSwapChain();
|
||||
}
|
92
engine/logicaldevice.h
Normal file
92
engine/logicaldevice.h
Normal file
@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "physicaldevice.h"
|
||||
#include "surface.h"
|
||||
|
||||
namespace Engine {
|
||||
class Engine;
|
||||
class SwapChain;
|
||||
|
||||
class LogicalDevice {
|
||||
friend class Engine;
|
||||
public:
|
||||
LogicalDevice(
|
||||
PhysicalDevice* physicalDevice,
|
||||
Surface* surface,
|
||||
const std::vector<const char*>& extensions,
|
||||
const std::vector<const char*> layers
|
||||
);
|
||||
~LogicalDevice();
|
||||
|
||||
void createSwapChain();
|
||||
void clearSwapChain();
|
||||
void createGraphicsPipeline(const std::string& vertexShaderPath, const std::string& fragmentShaderPath);
|
||||
void recreateSwapChain();
|
||||
|
||||
void drawFrame();
|
||||
|
||||
VkResult waitIdle();
|
||||
VkResult waitForFence(const VkFence& fence);
|
||||
VkResult resetFence(const VkFence& fence);
|
||||
|
||||
VkExtent2D chooseSwapExtent() const;
|
||||
VkResult createVkSwapChain(const VkExtent2D& extent, VkSwapchainKHR& out);
|
||||
void destroyVkSwapChain(VkSwapchainKHR& swapChain);
|
||||
VkResult getVkSwapChainImages(VkSwapchainKHR& swapChain, std::vector<VkImage>& out);
|
||||
VkResult createVkImageView(const VkImage& image, VkImageView& out);
|
||||
void destroyVkImageView(VkImageView& view);
|
||||
VkResult createVkFrameBuffer(const VkImageView& imageView, const VkExtent2D& extent, VkFramebuffer& out);
|
||||
void destroyVkFrameBuffer(VkFramebuffer& buffer);
|
||||
|
||||
private:
|
||||
VkShaderModule createShaderModule(const std::string& path);
|
||||
void destroyShaderModule(VkShaderModule shaderModule);
|
||||
void createDevice(
|
||||
VkPhysicalDevice physicalDevice,
|
||||
const QueueFamilyIndices& indices,
|
||||
const std::vector<const char*>& extensions,
|
||||
const std::vector<const char*> layers
|
||||
);
|
||||
void createQueues(uint32_t graphicsFamilyIndex, uint32_t presenFamilyIndex);
|
||||
void createCommandPool(uint32_t queueFamilyIndex);
|
||||
void createCommandBuffers();
|
||||
void createSyncObjects();
|
||||
void createRenderPass(VkFormat format);
|
||||
|
||||
VkResult queueSubmitGraphics(
|
||||
const VkSemaphore& wait,
|
||||
const VkCommandBuffer& buffer,
|
||||
const VkSemaphore& signal,
|
||||
const VkFence& fence
|
||||
);
|
||||
VkResult queuePresent(const VkSemaphore& signal, uint32_t index);
|
||||
void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex);
|
||||
|
||||
private:
|
||||
PhysicalDevice* phys;
|
||||
VkDevice vk;
|
||||
VkQueue graphicsQueue;
|
||||
VkQueue presentQueue;
|
||||
VkRenderPass renderPass;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
VkPipeline graphicsPipeline;
|
||||
VkCommandPool commandPool;
|
||||
std::vector<VkCommandBuffer> commandBuffers;
|
||||
std::vector<VkSemaphore> imageAvailableSemaphores;
|
||||
std::vector<VkSemaphore> renderFinishedSemaphores;
|
||||
std::vector<VkFence> inFlightFences;
|
||||
uint32_t currentFrame;
|
||||
bool framebufferResized;
|
||||
bool hasPipeline;
|
||||
Surface* surface;
|
||||
VkSurfaceFormatKHR surfaceFormat;
|
||||
SwapChain* swapChain;
|
||||
};
|
||||
|
||||
}
|
34
engine/physicaldevice.cpp
Normal file
34
engine/physicaldevice.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include "physicaldevice.h"
|
||||
|
||||
Engine::PhysicalDevice::PhysicalDevice(
|
||||
VkPhysicalDevice raw,
|
||||
const QueueFamilyIndices& indices,
|
||||
const SwapChainSupportDetails& swapChainSupport):
|
||||
indices(indices),
|
||||
swapChainSupport(swapChainSupport),
|
||||
vk(raw)
|
||||
{}
|
||||
|
||||
bool Engine::PhysicalDevice::checkDeviceExtensionSupport(VkPhysicalDevice device, std::set<std::string> requiredExtensions) {
|
||||
uint32_t extensionCount;
|
||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
|
||||
|
||||
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
|
||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
|
||||
|
||||
for (const auto& extension : availableExtensions)
|
||||
requiredExtensions.erase(extension.extensionName);
|
||||
|
||||
if (!requiredExtensions.empty()) {
|
||||
// std::cout << "Available Extensions: " << std::endl;
|
||||
// for (const auto& ext : availableExtensions)
|
||||
// std::cout << ext.extensionName << std::endl;
|
||||
|
||||
|
||||
std::cout << "Missing Extensions: " << std::endl;
|
||||
for (const auto& ext : requiredExtensions)
|
||||
std::cout << ext << std::endl;
|
||||
}
|
||||
|
||||
return requiredExtensions.empty();
|
||||
}
|
30
engine/physicaldevice.h
Normal file
30
engine/physicaldevice.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace Engine {
|
||||
class Engine;
|
||||
class LogicalDevice;
|
||||
|
||||
class PhysicalDevice {
|
||||
friend class Engine;
|
||||
friend class LogicalDevice;
|
||||
public:
|
||||
PhysicalDevice(VkPhysicalDevice raw, const QueueFamilyIndices& indices, const SwapChainSupportDetails& swapChainSupport);
|
||||
|
||||
static bool checkDeviceExtensionSupport(VkPhysicalDevice device, std::set<std::string> requiredExtensions);
|
||||
|
||||
const QueueFamilyIndices indices;
|
||||
const SwapChainSupportDetails swapChainSupport;
|
||||
|
||||
private:
|
||||
VkPhysicalDevice vk;
|
||||
};
|
||||
|
||||
}
|
88
engine/surface.cpp
Normal file
88
engine/surface.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
#include "surface.h"
|
||||
|
||||
#include "window.h"
|
||||
#include "instance.h"
|
||||
|
||||
Engine::Surface::Surface(const Instance* instance, const Window* window):
|
||||
vk(),
|
||||
instance(instance),
|
||||
window(window)
|
||||
{
|
||||
window->createSurface(instance->vk, &vk);
|
||||
}
|
||||
|
||||
Engine::Surface::~Surface() {
|
||||
vkDestroySurfaceKHR(instance->vk, vk, nullptr);
|
||||
}
|
||||
|
||||
bool Engine::Surface::isDeviceSutable(VkPhysicalDevice device) const {
|
||||
QueueFamilyIndices indices = findQueueFamilies(device);
|
||||
|
||||
if (!indices.isComplete())
|
||||
return false;
|
||||
|
||||
return querySwapChainSupport(device).adequate();
|
||||
}
|
||||
|
||||
Engine::SwapChainSupportDetails Engine::Surface::querySwapChainSupport(VkPhysicalDevice device) const {
|
||||
SwapChainSupportDetails details;
|
||||
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, vk, &details.capabilities);
|
||||
uint32_t formatCount;
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(device, vk, &formatCount, nullptr);
|
||||
|
||||
if (formatCount != 0) {
|
||||
details.formats.resize(formatCount);
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(device, vk, &formatCount, details.formats.data());
|
||||
}
|
||||
|
||||
uint32_t presentModeCount;
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(device, vk, &presentModeCount, nullptr);
|
||||
|
||||
if (presentModeCount != 0) {
|
||||
details.presentModes.resize(presentModeCount);
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(device, vk, &presentModeCount, details.presentModes.data());
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
Engine::QueueFamilyIndices Engine::Surface::findQueueFamilies(VkPhysicalDevice device) const {
|
||||
QueueFamilyIndices indices;
|
||||
|
||||
uint32_t queueFamilyCount = 0;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
|
||||
|
||||
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
|
||||
|
||||
for (std::vector<VkQueueFamilyProperties>::size_type i = 0; i < queueFamilyCount; ++i) {
|
||||
const VkQueueFamilyProperties& queueFamily = queueFamilies[i];
|
||||
if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
|
||||
indices.graphicsFamily = i;
|
||||
|
||||
VkBool32 presentSupport = false;
|
||||
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, vk, &presentSupport);
|
||||
|
||||
if (presentSupport)
|
||||
indices.presentFamily = i;
|
||||
|
||||
if (indices.isComplete())
|
||||
break;
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
VkExtent2D Engine::Surface::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) const {
|
||||
return window->chooseSwapExtent(capabilities);
|
||||
}
|
||||
|
||||
VkSurfaceFormatKHR Engine::Surface::chooseSwapSurfaceFormat(const SwapChainSupportDetails& swapChainSupport) const {
|
||||
return ::Engine::chooseSwapSurfaceFormat(swapChainSupport.formats);
|
||||
}
|
||||
|
||||
VkExtent2D Engine::Surface::waitForResize() const {
|
||||
return window->waitForResize();
|
||||
}
|
||||
|
35
engine/surface.h
Normal file
35
engine/surface.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Window;
|
||||
class Instance;
|
||||
class Engine;
|
||||
class LogicalDevice;
|
||||
|
||||
class Surface {
|
||||
friend class Engine;
|
||||
friend class LogicalDevice;
|
||||
public:
|
||||
Surface(const Instance* instance, const Window* window);
|
||||
~Surface();
|
||||
|
||||
VkExtent2D waitForResize() const;
|
||||
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) const;
|
||||
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const SwapChainSupportDetails& swapChainSupport) const;
|
||||
bool isDeviceSutable(VkPhysicalDevice device) const;
|
||||
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) const;
|
||||
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) const;
|
||||
|
||||
private:
|
||||
VkSurfaceKHR vk;
|
||||
const Instance* instance;
|
||||
const Window* window;
|
||||
};
|
||||
|
||||
}
|
||||
|
51
engine/swapchain.cpp
Normal file
51
engine/swapchain.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include "swapchain.h"
|
||||
|
||||
#include "logicaldevice.h"
|
||||
|
||||
Engine::SwapChain::SwapChain(LogicalDevice* logical):
|
||||
logical(logical),
|
||||
vk(),
|
||||
images(),
|
||||
extent(logical->chooseSwapExtent()),
|
||||
imageViews(),
|
||||
frameBuffers()
|
||||
{
|
||||
VkResult result = logical->createVkSwapChain(extent, vk);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create swap chain!");
|
||||
|
||||
logical->getVkSwapChainImages(vk, images);
|
||||
|
||||
imageViews.resize(images.size());
|
||||
frameBuffers.resize(images.size());
|
||||
|
||||
for (size_t i = 0; i < images.size(); i++) {
|
||||
if (logical->createVkImageView(images[i], imageViews[i]) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create image views!");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < images.size(); i++) {
|
||||
if (logical->createVkFrameBuffer(imageViews[i], extent, frameBuffers[i]) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create framebuffer!");
|
||||
}
|
||||
}
|
||||
|
||||
Engine::SwapChain::~SwapChain() {
|
||||
for (VkFramebuffer& buffer : frameBuffers)
|
||||
logical->destroyVkFrameBuffer(buffer);
|
||||
|
||||
for (VkImageView& imageView : imageViews)
|
||||
logical->destroyVkImageView(imageView);
|
||||
|
||||
//images are also indirectly freed by this operation
|
||||
logical->destroyVkSwapChain(vk);
|
||||
}
|
||||
|
||||
VkExtent2D Engine::SwapChain::getExtent() const {
|
||||
return extent;
|
||||
}
|
||||
|
||||
VkFramebuffer Engine::SwapChain::getFrameBuffer(uint32_t index) {
|
||||
return frameBuffers[index];
|
||||
}
|
29
engine/swapchain.h
Normal file
29
engine/swapchain.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
namespace Engine {
|
||||
class LogicalDevice;
|
||||
|
||||
class SwapChain {
|
||||
friend class LogicalDevice;
|
||||
public:
|
||||
SwapChain(LogicalDevice* logical);
|
||||
~SwapChain();
|
||||
|
||||
VkExtent2D getExtent() const;
|
||||
VkFramebuffer getFrameBuffer(uint32_t index);
|
||||
|
||||
private:
|
||||
LogicalDevice* logical;
|
||||
VkSwapchainKHR vk;
|
||||
std::vector<VkImage> images;
|
||||
VkExtent2D extent;
|
||||
std::vector<VkImageView> imageViews;
|
||||
std::vector<VkFramebuffer> frameBuffers;
|
||||
};
|
||||
|
||||
}
|
||||
|
194
engine/utils.cpp
Normal file
194
engine/utils.cpp
Normal file
@ -0,0 +1,194 @@
|
||||
#include "utils.h"
|
||||
|
||||
bool discoveredLayers = false;
|
||||
bool discoveredExtensions = false;
|
||||
std::map<std::string, VkLayerProperties> availableLayers;
|
||||
std::map<std::string, VkExtensionProperties> availableExtensions;
|
||||
|
||||
Engine::Support Engine::getVulkanLayers(const std::set<std::string>& requestedLayers)
|
||||
{
|
||||
Engine::Support result;
|
||||
if (!discoveredLayers) {
|
||||
std::vector<VkLayerProperties> props = getAvailableVulkanLayers();
|
||||
for (const VkLayerProperties& prop : props) {
|
||||
availableLayers.insert(std::make_pair(prop.layerName, prop));
|
||||
}
|
||||
discoveredLayers = true;
|
||||
}
|
||||
|
||||
for (const std::string& ext : requestedLayers) {
|
||||
if (availableLayers.count(ext) > 0) {
|
||||
result.supported.push_back(ext.c_str());
|
||||
} else {
|
||||
result.unsupported.push_back(ext.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Engine::Support Engine::getVulkanExtensions(
|
||||
const std::set<std::string>& requestedExtensions,
|
||||
const std::vector<const char*>& criticalExtensions
|
||||
) {
|
||||
Support result;
|
||||
if (!discoveredExtensions) {
|
||||
std::vector<VkExtensionProperties> props = getAvailableVulkanExtensions();
|
||||
for (const VkExtensionProperties& prop : props) {
|
||||
availableExtensions.insert(std::make_pair(prop.extensionName, prop));
|
||||
}
|
||||
discoveredExtensions = true;
|
||||
}
|
||||
|
||||
for (const char* ext : criticalExtensions) {
|
||||
if (availableExtensions.count(ext) > 0) {
|
||||
result.supported.push_back(ext);
|
||||
} else {
|
||||
result.unsupportedCritical.push_back(ext);
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string& ext : requestedExtensions) {
|
||||
if (availableExtensions.count(ext) > 0) {
|
||||
result.supported.push_back(ext.c_str());
|
||||
} else {
|
||||
result.unsupported.push_back(ext.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<VkExtensionProperties> Engine::getAvailableVulkanExtensions() {
|
||||
unsigned int extCount = 0;
|
||||
VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extCount, nullptr);
|
||||
if (res != VK_SUCCESS) {
|
||||
throw std::runtime_error("unable to query vulkan extension property count");
|
||||
}
|
||||
|
||||
std::vector<VkExtensionProperties> extensions(extCount);
|
||||
res = vkEnumerateInstanceExtensionProperties(nullptr, &extCount, extensions.data());
|
||||
if (res != VK_SUCCESS) {
|
||||
throw std::runtime_error("unable to retrieve vulkan instance layer names");
|
||||
}
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
void Engine::populateDebugMessengerCreateInfo(
|
||||
VkDebugUtilsMessengerCreateInfoEXT& createInfo,
|
||||
PFN_vkDebugUtilsMessengerCallbackEXT debugCallback)
|
||||
{
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||
|
||||
createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||
createInfo.pfnUserCallback = debugCallback;
|
||||
createInfo.pUserData = nullptr; // Optional
|
||||
}
|
||||
|
||||
std::vector<VkLayerProperties> Engine::getAvailableVulkanLayers() {
|
||||
unsigned int layerCount = 0;
|
||||
VkResult res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
|
||||
if (res != VK_SUCCESS) {
|
||||
throw std::runtime_error("unable to query vulkan instance layer property count");
|
||||
}
|
||||
|
||||
std::vector<VkLayerProperties> layers(layerCount);
|
||||
res = vkEnumerateInstanceLayerProperties(&layerCount, layers.data());
|
||||
if (res != VK_SUCCESS) {
|
||||
throw std::runtime_error("unable to retrieve vulkan instance layer names");
|
||||
}
|
||||
|
||||
return layers;
|
||||
}
|
||||
|
||||
VkResult Engine::createDebugUtilsMessengerEXT(
|
||||
VkInstance instance,
|
||||
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkDebugUtilsMessengerEXT* pDebugMessenger)
|
||||
{
|
||||
PFN_vkVoidFunction vdfunc = vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
|
||||
PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT)(vdfunc);
|
||||
if (func != nullptr)
|
||||
return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
|
||||
else
|
||||
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||
}
|
||||
|
||||
void Engine::destroyDebugUtilsMessengerEXT(
|
||||
VkInstance instance,
|
||||
VkDebugUtilsMessengerEXT debugMessenger,
|
||||
const VkAllocationCallbacks* pAllocator)
|
||||
{
|
||||
PFN_vkVoidFunction vdfunc = vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
|
||||
PFN_vkDestroyDebugUtilsMessengerEXT func = (PFN_vkDestroyDebugUtilsMessengerEXT)(vdfunc);
|
||||
if (func != nullptr)
|
||||
func(instance, debugMessenger, pAllocator);
|
||||
}
|
||||
|
||||
Engine::Support::Support():
|
||||
supported(),
|
||||
unsupported(),
|
||||
unsupportedCritical()
|
||||
{}
|
||||
|
||||
void Engine::Support::logSupported(const std::string& header) {
|
||||
if (!supported.empty()) {
|
||||
std::cout << header << std::endl;
|
||||
|
||||
for (const char* element : supported)
|
||||
std::cout << "\t" << element << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool Engine::SwapChainSupportDetails::adequate() const {
|
||||
return !formats.empty() && !presentModes.empty();
|
||||
}
|
||||
|
||||
bool Engine::QueueFamilyIndices::isComplete() const {
|
||||
return graphicsFamily.has_value() && presentFamily.has_value();
|
||||
}
|
||||
|
||||
std::vector<char> Engine::readFile(const std::string& filename) {
|
||||
std::ifstream file(filename, std::ios::ate | std::ios::binary);
|
||||
|
||||
if (!file.is_open())
|
||||
throw std::runtime_error("failed to open file!");
|
||||
|
||||
size_t fileSize = (size_t) file.tellg();
|
||||
std::vector<char> buffer(fileSize);
|
||||
|
||||
file.seekg(0);
|
||||
file.read(buffer.data(), fileSize);
|
||||
|
||||
file.close();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
VkSurfaceFormatKHR Engine::chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {
|
||||
for (const VkSurfaceFormatKHR& availableFormat : availableFormats) {
|
||||
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||
return availableFormat;
|
||||
}
|
||||
}
|
||||
|
||||
return availableFormats[0];
|
||||
}
|
||||
|
||||
VkPresentModeKHR Engine::chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) {
|
||||
for (const auto& availablePresentMode : availablePresentModes) {
|
||||
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
|
||||
return availablePresentMode;
|
||||
}
|
||||
}
|
||||
|
||||
return VK_PRESENT_MODE_FIFO_KHR;
|
||||
}
|
81
engine/utils.h
Normal file
81
engine/utils.h
Normal file
@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <fstream>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
struct Support {
|
||||
Support();
|
||||
|
||||
void logSupported(const std::string& header);
|
||||
|
||||
std::vector<const char*> supported;
|
||||
std::vector<const char*> unsupported;
|
||||
std::vector<const char*> unsupportedCritical;
|
||||
|
||||
};
|
||||
|
||||
struct SwapChainSupportDetails {
|
||||
VkSurfaceCapabilitiesKHR capabilities;
|
||||
std::vector<VkSurfaceFormatKHR> formats;
|
||||
std::vector<VkPresentModeKHR> presentModes;
|
||||
|
||||
bool adequate() const;
|
||||
};
|
||||
|
||||
struct QueueFamilyIndices {
|
||||
std::optional<uint32_t> graphicsFamily;
|
||||
std::optional<uint32_t> presentFamily;
|
||||
|
||||
bool isComplete() const;
|
||||
};
|
||||
|
||||
Support getVulkanLayers(const std::set<std::string>& requestedLayers);
|
||||
Support getVulkanExtensions(const std::set<std::string>& requestedExtensions, const std::vector<const char*>& criticalExtensions);
|
||||
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
|
||||
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes);
|
||||
|
||||
std::vector<VkExtensionProperties> getAvailableVulkanExtensions();
|
||||
std::vector<VkLayerProperties> getAvailableVulkanLayers();
|
||||
|
||||
void populateDebugMessengerCreateInfo(
|
||||
VkDebugUtilsMessengerCreateInfoEXT& createInfo,
|
||||
PFN_vkDebugUtilsMessengerCallbackEXT debugCallback
|
||||
);
|
||||
|
||||
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* callbackData,
|
||||
void* userData
|
||||
)
|
||||
{
|
||||
std::cerr << "validation layer: " << callbackData->pMessage << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
VkResult createDebugUtilsMessengerEXT(
|
||||
VkInstance instance,
|
||||
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkDebugUtilsMessengerEXT* pDebugMessenger
|
||||
);
|
||||
|
||||
void destroyDebugUtilsMessengerEXT(
|
||||
VkInstance instance,
|
||||
VkDebugUtilsMessengerEXT debugMessenger,
|
||||
const VkAllocationCallbacks* pAllocator
|
||||
);
|
||||
|
||||
std::vector<char> readFile(const std::string& filename);
|
||||
}
|
90
engine/window.cpp
Normal file
90
engine/window.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
#include "window.h"
|
||||
|
||||
Engine::Window::Window(uint32_t width, uint32_t height) :
|
||||
sdl(createWindow(width, height)),
|
||||
extent({width, height})
|
||||
{
|
||||
|
||||
//glfwSetWindowUserPointer(window, this);
|
||||
//glfwSetFramebufferSizeCallback(window, framebufferResizeCallback);
|
||||
}
|
||||
|
||||
// static void framebufferResizeCallback(SDL_Window* window, int width, int height) {
|
||||
// auto app = reinterpret_cast<HelloTriangleApplication*>(glfwGetWindowUserPointer(window));
|
||||
// app->framebufferResized = true;
|
||||
// }
|
||||
|
||||
Engine::Window::~Window() {
|
||||
SDL_DestroyWindow(sdl);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
SDL_Window* Engine::Window::createWindow(uint32_t width, uint32_t height) {
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
SDL_Window* wnd = SDL_CreateWindow(
|
||||
"Stories", //TODO parametrize window title
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
width,
|
||||
height,
|
||||
SDL_WINDOW_VULKAN
|
||||
);
|
||||
|
||||
return wnd;
|
||||
}
|
||||
|
||||
std::vector<const char *> Engine::Window::getRequiredVulkanExtensions() const {
|
||||
unsigned int extCount = 0;
|
||||
if (!SDL_Vulkan_GetInstanceExtensions(sdl, &extCount, nullptr)) {
|
||||
throw std::runtime_error("Unable to query the number of Vulkan instance extensions");
|
||||
}
|
||||
|
||||
std::vector<const char*> extract(extCount);
|
||||
if (!SDL_Vulkan_GetInstanceExtensions(sdl, &extCount, extract.data())) {
|
||||
throw std::runtime_error("Unable to query the number of Vulkan instance extension names");
|
||||
}
|
||||
|
||||
return extract;
|
||||
}
|
||||
|
||||
VkExtent2D Engine::Window::getDrawableSize() const {
|
||||
int width, height;
|
||||
SDL_Vulkan_GetDrawableSize(sdl, &width, &height);
|
||||
|
||||
VkExtent2D actualExtent = {
|
||||
static_cast<uint32_t>(width),
|
||||
static_cast<uint32_t>(height)
|
||||
};
|
||||
|
||||
return actualExtent;
|
||||
}
|
||||
|
||||
void Engine::Window::createSurface(VkInstance instance, VkSurfaceKHR* out) const {
|
||||
if (SDL_Vulkan_CreateSurface(sdl, instance, out) != SDL_TRUE) {
|
||||
throw std::runtime_error("failed to create window surface!");
|
||||
}
|
||||
}
|
||||
|
||||
VkExtent2D Engine::Window::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) const {
|
||||
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
|
||||
return capabilities.currentExtent;
|
||||
} else {
|
||||
VkExtent2D actualExtent = getDrawableSize();
|
||||
|
||||
actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
|
||||
actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
|
||||
|
||||
return actualExtent;
|
||||
}
|
||||
}
|
||||
|
||||
VkExtent2D Engine::Window::waitForResize() const {
|
||||
VkExtent2D extent = getDrawableSize();
|
||||
while (extent.width == 0 || extent.height == 0) {
|
||||
extent = getDrawableSize();
|
||||
SDL_WaitEvent(nullptr);
|
||||
}
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
36
engine/window.h
Normal file
36
engine/window.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
class Engine;
|
||||
|
||||
class Window {
|
||||
friend class Engine;
|
||||
public:
|
||||
Window(uint32_t width = 800, uint32_t height = 600);
|
||||
~Window();
|
||||
|
||||
std::vector<const char *> getRequiredVulkanExtensions() const;
|
||||
VkExtent2D waitForResize() const;
|
||||
VkExtent2D getDrawableSize() const;
|
||||
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) const;
|
||||
void createSurface(VkInstance instance, VkSurfaceKHR* out) const;
|
||||
|
||||
private:
|
||||
static SDL_Window* createWindow(uint32_t width = 800, uint32_t height = 600);
|
||||
|
||||
private:
|
||||
SDL_Window* sdl;
|
||||
VkExtent2D extent;
|
||||
};
|
||||
|
||||
}
|
18
main.cpp
18
main.cpp
@ -1,6 +1,16 @@
|
||||
#include <iostream>
|
||||
#include "engine/engine.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
std::cout << "Hello, world!" << std::endl;
|
||||
return 0;
|
||||
int main() {
|
||||
Engine::Engine app;
|
||||
app.enableDebug();
|
||||
|
||||
try {
|
||||
app.run();
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
24
shaders/CMakeLists.txt
Normal file
24
shaders/CMakeLists.txt
Normal file
@ -0,0 +1,24 @@
|
||||
set(GLSL_VALIDATOR "glslangValidator")
|
||||
|
||||
file(GLOB_RECURSE GLSL_SOURCE_FILES
|
||||
"*.frag"
|
||||
"*.vert"
|
||||
)
|
||||
|
||||
foreach(GLSL ${GLSL_SOURCE_FILES})
|
||||
get_filename_component(FILE_NAME ${GLSL} NAME)
|
||||
set(SPIRV "${PROJECT_BINARY_DIR}/shaders/${FILE_NAME}.spv")
|
||||
add_custom_command(
|
||||
OUTPUT ${SPIRV}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_BINARY_DIR}/shaders/"
|
||||
COMMAND ${GLSL_VALIDATOR} -V ${GLSL} -o ${SPIRV}
|
||||
DEPENDS ${GLSL})
|
||||
list(APPEND SPIRV_BINARY_FILES ${SPIRV})
|
||||
endforeach(GLSL)
|
||||
|
||||
add_custom_target(
|
||||
Shaders
|
||||
DEPENDS ${SPIRV_BINARY_FILES}
|
||||
)
|
||||
|
||||
add_dependencies(stories Shaders)
|
9
shaders/shader.frag
Normal file
9
shaders/shader.frag
Normal file
@ -0,0 +1,9 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(fragColor, 1.0);
|
||||
}
|
20
shaders/shader.vert
Normal file
20
shaders/shader.vert
Normal file
@ -0,0 +1,20 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
|
||||
vec2 positions[3] = vec2[](
|
||||
vec2(0.0, -0.5),
|
||||
vec2(0.5, 0.5),
|
||||
vec2(-0.5, 0.5)
|
||||
);
|
||||
|
||||
vec3 colors[3] = vec3[](
|
||||
vec3(1.0, 0.0, 0.0),
|
||||
vec3(0.0, 1.0, 0.0),
|
||||
vec3(0.0, 0.0, 1.0)
|
||||
);
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
||||
fragColor = colors[gl_VertexIndex];
|
||||
}
|
Loading…
Reference in New Issue
Block a user