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)
|
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)
|
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)
|
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) {
|
int main() {
|
||||||
std::cout << "Hello, world!" << std::endl;
|
Engine::Engine app;
|
||||||
return 0;
|
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