Implement a Vulkan Renderer module (#2)
* Starting work on the Vlkx renderer * Fix renderer implementation * Move GLM to FetchContent
This commit is contained in:
parent
3bf44e8985
commit
a370f28f14
|
@ -19,6 +19,20 @@ FetchContent_Declare(
|
||||||
|
|
||||||
FetchContent_MakeAvailable(Catch2)
|
FetchContent_MakeAvailable(Catch2)
|
||||||
|
|
||||||
|
# Fetch GLM for the renderer
|
||||||
|
FetchContent_Declare(
|
||||||
|
glm
|
||||||
|
GIT_REPOSITORY https://github.com/g-truc/glm.git
|
||||||
|
GIT_TAG 0.9.9.2
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_GetProperties(glm)
|
||||||
|
if(NOT glm_POPULATED)
|
||||||
|
FetchContent_Populate(glm)
|
||||||
|
set(GLM_TEST_ENABLE OFF CACHE BOOL "" FORCE)
|
||||||
|
add_subdirectory(${glm_SOURCE_DIR} ${glm_BINARY_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
# Import some find files
|
# Import some find files
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
||||||
|
|
||||||
|
@ -36,5 +50,8 @@ add_subdirectory(projs/shadow/shadow-reflection)
|
||||||
# Core engine
|
# Core engine
|
||||||
add_subdirectory(projs/shadow/shadow-engine)
|
add_subdirectory(projs/shadow/shadow-engine)
|
||||||
|
|
||||||
|
# Renderer
|
||||||
|
add_subdirectory(projs/shadow/shadow-renderer)
|
||||||
|
|
||||||
# Runtime executable
|
# Runtime executable
|
||||||
add_subdirectory(projs/shadow/shadow-runtime)
|
add_subdirectory(projs/shadow/shadow-runtime)
|
|
@ -9,4 +9,4 @@ FILE(GLOB_RECURSE HEADERS src/*.h)
|
||||||
add_library(shadow-engine ${SOURCES})
|
add_library(shadow-engine ${SOURCES})
|
||||||
|
|
||||||
target_include_directories(shadow-engine PRIVATE ${SDL2_INCLUDE_DIRS} PUBLIC ${HEADERS})
|
target_include_directories(shadow-engine PRIVATE ${SDL2_INCLUDE_DIRS} PUBLIC ${HEADERS})
|
||||||
target_link_libraries(shadow-engine PRIVATE Vulkan::Vulkan PUBLIC SDL2::Core shadow-reflect shadow-asset)
|
target_link_libraries(shadow-engine PRIVATE Vulkan::Vulkan PUBLIC SDL2::Core shadow-reflect shadow-asset PUBLIC renderer)
|
|
@ -1,6 +1,9 @@
|
||||||
#include "ShadowApplication.h"
|
#include "ShadowApplication.h"
|
||||||
|
|
||||||
#include "Time.h"
|
#include "Time.h"
|
||||||
|
#include <vlkx/vulkan/VulkanManager.h>
|
||||||
|
#include <vlkx/render/Camera.h>
|
||||||
|
#include <vlkx/render/geometry/SingleRenderer.h>
|
||||||
|
|
||||||
namespace ShadowEngine {
|
namespace ShadowEngine {
|
||||||
|
|
||||||
|
@ -46,6 +49,8 @@ namespace ShadowEngine {
|
||||||
|
|
||||||
window_ = new ShadowWindow(800,450);
|
window_ = new ShadowWindow(800,450);
|
||||||
|
|
||||||
|
VulkanManager::getInstance()->initVulkan(window_->sdlWindowPtr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
moduleManager.PushModule(new Log());
|
moduleManager.PushModule(new Log());
|
||||||
moduleManager.PushModule(new EventSystem::ShadowEventManager());
|
moduleManager.PushModule(new EventSystem::ShadowEventManager());
|
||||||
|
@ -70,6 +75,15 @@ namespace ShadowEngine {
|
||||||
|
|
||||||
void ShadowApplication::Start()
|
void ShadowApplication::Start()
|
||||||
{
|
{
|
||||||
|
// Create the camera
|
||||||
|
Camera camera {};
|
||||||
|
camera.init(45, 1280, 720, 0.1, 10000);
|
||||||
|
camera.setPosition(glm::vec3(0, 0, 4));
|
||||||
|
|
||||||
|
// Create the renderer
|
||||||
|
SingleRenderer object;
|
||||||
|
object.createSingleRenderer(Geo::MeshType::Cube, glm::vec3(-1, 0, -1), glm::vec3(0.5));
|
||||||
|
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while (running)
|
while (running)
|
||||||
{
|
{
|
||||||
|
@ -78,6 +92,16 @@ namespace ShadowEngine {
|
||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object.setRotation(glm::rotate(object.getRotation(), (float) 0.5, glm::vec3(1, 0, 0)));
|
||||||
|
|
||||||
|
VulkanManager::getInstance()->startDraw();
|
||||||
|
|
||||||
|
object.updateUniforms(camera);
|
||||||
|
object.draw();
|
||||||
|
|
||||||
|
VulkanManager::getInstance()->endDraw();
|
||||||
|
|
||||||
|
|
||||||
Time::UpdateTime();
|
Time::UpdateTime();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
ShadowWindow::ShadowWindow(int W, int H) : Height(H), Width(W)
|
ShadowWindow::ShadowWindow(int W, int H) : Height(H), Width(W)
|
||||||
{
|
{
|
||||||
// Create our window
|
// Create our window
|
||||||
sdlWindowPtr = SDL_CreateWindow( "Example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, Width, Height, SDL_WINDOW_SHOWN );
|
sdlWindowPtr = SDL_CreateWindow( "Candlefire", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, Width, Height, SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN );
|
||||||
|
|
||||||
// Make sure creating the window succeeded
|
// Make sure creating the window succeeded
|
||||||
if ( !sdlWindowPtr ) {
|
if ( !sdlWindowPtr ) {
|
||||||
|
@ -12,24 +12,6 @@ ShadowWindow::ShadowWindow(int W, int H) : Height(H), Width(W)
|
||||||
//std::cout << "Error creating window: " << SDL_GetError() << std::endl;
|
//std::cout << "Error creating window: " << SDL_GetError() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the surface from the window
|
|
||||||
sdlSurface = SDL_GetWindowSurface( sdlWindowPtr );
|
|
||||||
|
|
||||||
// Make sure getting the surface succeeded
|
|
||||||
if ( !sdlSurface ) {
|
|
||||||
//Raise an error in the log
|
|
||||||
//std::cout << "Error getting surface: " << SDL_GetError() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create window
|
|
||||||
/*
|
|
||||||
this->winPtr = SDL_CreateWindow("Hello World!", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W, H,
|
|
||||||
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
|
|
||||||
SH_CORE_ASSERT(winPtr, "Window could not be created! SDL_Error: %s\n", SDL_GetError());
|
|
||||||
|
|
||||||
context = ShadowEngine::Rendering::GraphicsContext::Create(this);
|
|
||||||
context->Init();
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ShadowWindow::~ShadowWindow()
|
ShadowWindow::~ShadowWindow()
|
||||||
|
|
10
projs/shadow/shadow-renderer/CMakeLists.txt
Normal file
10
projs/shadow/shadow-renderer/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
find_package(Vulkan REQUIRED)
|
||||||
|
find_package(SDL2 REQUIRED)
|
||||||
|
|
||||||
|
FILE(GLOB_RECURSE SOURCES src/*.cpp inc/*.h)
|
||||||
|
|
||||||
|
add_library(renderer ${SOURCES})
|
||||||
|
|
||||||
|
target_include_directories(renderer PRIVATE ${SDL2_INCLUDE_DIRS} PUBLIC inc ${glm_SOURCE_DIR})
|
||||||
|
target_link_libraries(renderer PRIVATE SDL2::Main Vulkan::Vulkan)
|
23
projs/shadow/shadow-renderer/inc/vlkx/render/Camera.h
Normal file
23
projs/shadow/shadow-renderer/inc/vlkx/render/Camera.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define GLM_FORCE_RADIAN
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
class Camera {
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Set up the camera.
|
||||||
|
void init(float fov, float width, float height, float near, float far);
|
||||||
|
|
||||||
|
// Move the camera.
|
||||||
|
void setPosition(glm::vec3 positionIn);
|
||||||
|
|
||||||
|
glm::mat4 getViewMatrix() { return viewMatrix; };
|
||||||
|
glm::mat4 getProjectionMatrix() { return projectionMatrix; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
glm::mat4 projectionMatrix;
|
||||||
|
glm::mat4 viewMatrix;
|
||||||
|
glm::vec3 position;
|
||||||
|
};
|
94
projs/shadow/shadow-renderer/inc/vlkx/render/Geometry.h
Normal file
94
projs/shadow/shadow-renderer/inc/vlkx/render/Geometry.h
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define GLM_FORCE_RADIAN
|
||||||
|
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
// Global namespace for all classes to do with geometry in a level.
|
||||||
|
namespace Geo {
|
||||||
|
|
||||||
|
// The core components of a given mesh.
|
||||||
|
enum MeshType {
|
||||||
|
Triangle, // A construction of triangles
|
||||||
|
Quad, // A construction of quads
|
||||||
|
Cube, // A single Cube.
|
||||||
|
Sphere // A single Sphere.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Contains standard uniforms for shader files.
|
||||||
|
struct UniformBufferObject {
|
||||||
|
glm::mat4 model; // Model transform matrix.
|
||||||
|
glm::mat4 view; // View matrix.
|
||||||
|
glm::mat4 proj; // Projection matrix.
|
||||||
|
};
|
||||||
|
|
||||||
|
// All of the metadata involved with a vertex.
|
||||||
|
struct Vertex {
|
||||||
|
glm::vec3 position; // XYZ coordinates of the vertex's position.
|
||||||
|
glm::vec3 normal; // Unit vector pointing away from the outer surface of the vertex.
|
||||||
|
glm::vec3 color; // The color of the vertex.
|
||||||
|
glm::vec2 texture; // The u/v coordinates of this vertex in the bound texture.
|
||||||
|
|
||||||
|
// How fast should vertex data be read from RAM?
|
||||||
|
static VkVertexInputBindingDescription getBindingDesc() {
|
||||||
|
VkVertexInputBindingDescription desc = {};
|
||||||
|
desc.binding = 0;
|
||||||
|
desc.stride = sizeof(Vertex);
|
||||||
|
desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// How should vertexes be handled?
|
||||||
|
static std::array<VkVertexInputAttributeDescription, 4> getAttributeDesc() {
|
||||||
|
std::array<VkVertexInputAttributeDescription, 4> descs = {};
|
||||||
|
|
||||||
|
// Attribute 0; position. Location 0, 3x 32-bit float.
|
||||||
|
descs[0].binding = 0;
|
||||||
|
descs[0].location = 0;
|
||||||
|
descs[0].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||||
|
descs[0].offset = offsetof(Vertex, position);
|
||||||
|
|
||||||
|
// Attribute 1; normal. Location 1, 3x 32-bit float.
|
||||||
|
descs[1].binding = 0;
|
||||||
|
descs[1].location = 1;
|
||||||
|
descs[1].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||||
|
descs[1].offset = offsetof(Vertex, normal);
|
||||||
|
|
||||||
|
// Attribute 2; color. Location 2, 3x 32-bit float.
|
||||||
|
descs[2].binding = 0;
|
||||||
|
descs[2].location = 2;
|
||||||
|
descs[2].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||||
|
descs[2].offset = offsetof(Vertex, color);
|
||||||
|
|
||||||
|
// Attribute 3; texture. Location 3, 2x 32-bit float.
|
||||||
|
descs[3].binding = 0;
|
||||||
|
descs[3].location = 3;
|
||||||
|
descs[3].format = VK_FORMAT_R32G32_SFLOAT;
|
||||||
|
descs[3].offset = offsetof(Vertex, texture);
|
||||||
|
|
||||||
|
return descs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Contains data about a given Mesh.
|
||||||
|
class Mesh {
|
||||||
|
public:
|
||||||
|
// Pre-load the data for a triangle into the given buffers.
|
||||||
|
static void setTriData(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices);
|
||||||
|
// Pre-load the data for a quad into the given buffers.
|
||||||
|
static void setQuadData(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices);
|
||||||
|
// Pre-load the data for a cube into the given buffers.
|
||||||
|
static void setCubeData(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices);
|
||||||
|
// Pre-load the data for a sphere into the given buffers.
|
||||||
|
static void setSphereData(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class RenderPass {
|
||||||
|
public:
|
||||||
|
RenderPass();
|
||||||
|
~RenderPass();
|
||||||
|
|
||||||
|
VkRenderPass pass;
|
||||||
|
|
||||||
|
void createVertexRenderPass(VkFormat format);
|
||||||
|
void createRTRenderPass(VkFormat format);
|
||||||
|
void createRTPhysicsPass(VkFormat format);
|
||||||
|
|
||||||
|
void beginRenderPass(std::vector<VkClearValue> clearValues, VkCommandBuffer commands, VkFramebuffer framebuffer, VkExtent2D extent);
|
||||||
|
void endRenderPass(VkCommandBuffer commands);
|
||||||
|
|
||||||
|
void destroy();
|
||||||
|
};
|
|
@ -0,0 +1,33 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vlkx/render/shader/Pipeline.h>
|
||||||
|
#include <vlkx/render/shader/GeoBuffers.h>
|
||||||
|
#include <vlkx/render/shader/Descriptor.h>
|
||||||
|
#include <vlkx/render/Camera.h>
|
||||||
|
|
||||||
|
// Renders a single object.
|
||||||
|
class SingleRenderer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
void createSingleRenderer(Geo::MeshType type, glm::vec3 posIn, glm::vec3 scaleIn);
|
||||||
|
|
||||||
|
void updateUniforms(Camera camera);
|
||||||
|
|
||||||
|
void draw();
|
||||||
|
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
void setPosition(glm::vec3 newPos) { position = newPos; }
|
||||||
|
void setRotation(glm::mat4 newRot) { rotation = newRot; }
|
||||||
|
|
||||||
|
glm::mat4 getRotation() { return rotation; }
|
||||||
|
private:
|
||||||
|
|
||||||
|
Pipeline pipeline;
|
||||||
|
GeoBuffers buffers;
|
||||||
|
Descriptor descriptor;
|
||||||
|
|
||||||
|
glm::vec3 position;
|
||||||
|
glm::vec3 scale;
|
||||||
|
glm::mat4 rotation;
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Descriptor {
|
||||||
|
public:
|
||||||
|
Descriptor();
|
||||||
|
~Descriptor();
|
||||||
|
|
||||||
|
// Global descriptor bindings
|
||||||
|
VkDescriptorSetLayout layout;
|
||||||
|
VkDescriptorPool pool;
|
||||||
|
VkDescriptorSet set;
|
||||||
|
|
||||||
|
// Allocate and prepare the descriptors for a simple (uniform buffer only) descriptor.
|
||||||
|
void createAndAllocateSimple(uint32_t imageCount);
|
||||||
|
|
||||||
|
// Fill the Descriptors with the given uniforms
|
||||||
|
void populate(uint32_t imageCount, VkBuffer uniforms, size_t bufferSize);
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Set up the layout for a single UBO
|
||||||
|
void createSimpleLayout();
|
||||||
|
// Setup the pool for a single UBO
|
||||||
|
void createSimplePool(uint32_t imageCount);
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,37 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <vlkx/render/Geometry.h>
|
||||||
|
#include <vlkx/vulkan/Tools.h>
|
||||||
|
|
||||||
|
// Contains memory and objects required to store information about geometry.
|
||||||
|
class GeoBuffers {
|
||||||
|
public:
|
||||||
|
|
||||||
|
GeoBuffers();
|
||||||
|
~GeoBuffers();
|
||||||
|
|
||||||
|
// Program and virtual memory for vertex data.
|
||||||
|
std::vector<Geo::Vertex> vertices;
|
||||||
|
VkTools::ManagedBuffer vertexBuffer;
|
||||||
|
|
||||||
|
// Program and virtual memory for indices data.
|
||||||
|
std::vector<uint32_t> indices;
|
||||||
|
VkTools::ManagedBuffer indexBuffer;
|
||||||
|
|
||||||
|
// Virtual memory for uniforms - translation matrices, etc.
|
||||||
|
VkTools::ManagedBuffer uniformBuffer;
|
||||||
|
|
||||||
|
void createBuffers(Geo::MeshType type);
|
||||||
|
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void createVertexBuffer();
|
||||||
|
void createIndexBuffer();
|
||||||
|
void createUniformBuffer();
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
class Pipeline {
|
||||||
|
public:
|
||||||
|
Pipeline();
|
||||||
|
~Pipeline();
|
||||||
|
|
||||||
|
// The active Graphics Pipeline layout.
|
||||||
|
VkPipelineLayout layout;
|
||||||
|
// The active Graphics Pipeline instance.
|
||||||
|
VkPipeline pipeline;
|
||||||
|
|
||||||
|
// Create the layout and pipeline for a vertex renderer
|
||||||
|
void create(VkExtent2D extent, VkDescriptorSetLayout set, VkRenderPass renderPass);
|
||||||
|
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<char> readFile(const std::string& filename);
|
||||||
|
VkShaderModule createShaderModule(const std::vector<char>& code);
|
||||||
|
|
||||||
|
void createPipelineLayout(VkDescriptorSetLayout set);
|
||||||
|
|
||||||
|
// For rendering objects that use traditional vertex-based mesh geometry.
|
||||||
|
// See Geo::Vertex for the uniform bindings.
|
||||||
|
void createVertexPipeline(VkExtent2D extent, VkRenderPass renderPass);
|
||||||
|
};
|
|
@ -0,0 +1,46 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class RenderTexture {
|
||||||
|
public:
|
||||||
|
// Create all image views and all framebuffers from a set of images, a format, a size, and the current rendering pass.
|
||||||
|
virtual void createViewsAndFramebuffer(std::vector<VkImage> images, VkFormat format, VkExtent2D extent, VkRenderPass pass) = 0;
|
||||||
|
// Create views for swapChainImages with the given format.
|
||||||
|
virtual void createViews(VkFormat format) = 0;
|
||||||
|
// Create a framebuffer for the swapChainImages, for the given pass.
|
||||||
|
virtual void createFramebuffer(VkExtent2D extent, VkRenderPass pass) = 0;
|
||||||
|
|
||||||
|
virtual VkFramebuffer getFramebuffer(int ID) = 0;
|
||||||
|
|
||||||
|
virtual void destroy() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SingleRenderTexture : public RenderTexture {
|
||||||
|
public:
|
||||||
|
SingleRenderTexture();
|
||||||
|
~SingleRenderTexture();
|
||||||
|
|
||||||
|
// A list of all images involved with this texture (color, normals, etc)
|
||||||
|
std::vector<VkImage> swapChainImages;
|
||||||
|
// The sizes of all attached images.
|
||||||
|
VkExtent2D swapChainImageExtent;
|
||||||
|
|
||||||
|
// Views - mipmaps, portions, crops, etc of the attached images.
|
||||||
|
std::vector<VkImageView> swapChainImageViews;
|
||||||
|
// Framebuffers containing the images that can be bound and rendered from.
|
||||||
|
std::vector<VkFramebuffer> swapChainFramebuffers;
|
||||||
|
|
||||||
|
VkFramebuffer getFramebuffer(int ID) override { return swapChainFramebuffers.at(ID); }
|
||||||
|
|
||||||
|
// Create all image views and all framebuffers from a set of images, a format, a size, and the current rendering pass.
|
||||||
|
void createViewsAndFramebuffer(std::vector<VkImage> images, VkFormat format, VkExtent2D extent, VkRenderPass pass) override;
|
||||||
|
// Create views for swapChainImages with the given format.
|
||||||
|
void createViews(VkFormat format) override;
|
||||||
|
// Create a framebuffer for the swapChainImages, for the given pass.
|
||||||
|
void createFramebuffer(VkExtent2D extent, VkRenderPass pass) override;
|
||||||
|
|
||||||
|
void destroy() override;
|
||||||
|
};
|
23
projs/shadow/shadow-renderer/inc/vlkx/vulkan/CommandBuffer.h
Normal file
23
projs/shadow/shadow-renderer/inc/vlkx/vulkan/CommandBuffer.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class CommandBuffer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
CommandBuffer();
|
||||||
|
~CommandBuffer();
|
||||||
|
|
||||||
|
VkCommandPool commands;
|
||||||
|
std::vector<VkCommandBuffer> buffers;
|
||||||
|
|
||||||
|
void createCommandPoolAndBuffers(size_t images);
|
||||||
|
void beginCommandBuffer(VkCommandBuffer buffer);
|
||||||
|
void endCommandBuffer(VkCommandBuffer buffer);
|
||||||
|
|
||||||
|
void createCommandPool();
|
||||||
|
void allocateCommandBuffers(size_t size);
|
||||||
|
|
||||||
|
void destroy();
|
||||||
|
};
|
25
projs/shadow/shadow-renderer/inc/vlkx/vulkan/SwapChain.h
Normal file
25
projs/shadow/shadow-renderer/inc/vlkx/vulkan/SwapChain.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
class SwapChain {
|
||||||
|
public:
|
||||||
|
SwapChain();
|
||||||
|
~SwapChain();
|
||||||
|
|
||||||
|
VkSwapchainKHR swapChain;
|
||||||
|
VkFormat format;
|
||||||
|
VkExtent2D extent;
|
||||||
|
|
||||||
|
std::vector<VkImage> images;
|
||||||
|
|
||||||
|
VkSurfaceFormatKHR chooseFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
|
||||||
|
VkPresentModeKHR chooseMode(const std::vector<VkPresentModeKHR>& availableModes);
|
||||||
|
VkExtent2D chooseExtent(const VkSurfaceCapabilitiesKHR& capabilities);
|
||||||
|
|
||||||
|
void create(VkSurfaceKHR surface);
|
||||||
|
void destroy();
|
||||||
|
};
|
221
projs/shadow/shadow-renderer/inc/vlkx/vulkan/Tools.h
Normal file
221
projs/shadow/shadow-renderer/inc/vlkx/vulkan/Tools.h
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <vlkx/vulkan/VulkanManager.h>
|
||||||
|
|
||||||
|
#include <vulkan/vk_mem_alloc.h>
|
||||||
|
|
||||||
|
namespace VkTools {
|
||||||
|
extern VmaAllocator g_allocator;
|
||||||
|
extern VkInstance g_Instance;
|
||||||
|
extern VkPhysicalDevice g_PhysicalDevice;
|
||||||
|
extern VkDevice g_Device;
|
||||||
|
extern uint32_t g_QueueFamily;
|
||||||
|
extern VkQueue g_Queue;
|
||||||
|
extern VkDebugReportCallbackEXT g_DebugReport;
|
||||||
|
|
||||||
|
struct ManagedImage {
|
||||||
|
VkImage image;
|
||||||
|
VmaAllocation allocation;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ManagedBuffer {
|
||||||
|
VkBuffer buffer;
|
||||||
|
VmaAllocation allocation;
|
||||||
|
};
|
||||||
|
|
||||||
|
ManagedImage createImage(VkFormat format, VkImageUsageFlags flags, VkExtent3D extent, VkDevice device);
|
||||||
|
VkSampler createSampler(VkFilter filters, VkSamplerAddressMode mode);
|
||||||
|
VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags, VkDevice device);
|
||||||
|
ManagedBuffer createGPUBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkDevice logicalDevice, VkPhysicalDevice physicalDevice);
|
||||||
|
uint32_t findMemoryIndex(uint32_t type, VkMemoryPropertyFlags properties, VkPhysicalDevice physicalDevice);
|
||||||
|
VkCommandBuffer createTempCommandBuffer(VkCommandPool pool, VkDevice logical);
|
||||||
|
void executeAndDeleteTempBuffer(VkCommandBuffer buffer, VkCommandPool pool, VkQueue queue, VkDevice logicalDevice);
|
||||||
|
void copyGPUBuffer(VkBuffer source, VkBuffer dest, VkDeviceSize length, VkDevice logical, VkQueue graphicsQueue, uint32_t queueIndex);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef VKTOOLS_IMPLEMENTATION
|
||||||
|
ManagedImage createImage(VkFormat format, VkImageUsageFlags flags, VkExtent3D extent, VkDevice device) {
|
||||||
|
// Set up image metadata
|
||||||
|
VkImageCreateInfo info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||||
|
info.pNext = nullptr;
|
||||||
|
info.format = format;
|
||||||
|
info.extent = extent;
|
||||||
|
info.mipLevels = 1;
|
||||||
|
info.arrayLayers = 1;
|
||||||
|
info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||||
|
info.usage = flags;
|
||||||
|
|
||||||
|
// Prepare the managed image
|
||||||
|
ManagedImage image {};
|
||||||
|
|
||||||
|
// Set up image allocation
|
||||||
|
VmaAllocationCreateInfo allocateInfo = {};
|
||||||
|
allocateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
||||||
|
|
||||||
|
// Allocate + create the image
|
||||||
|
vmaCreateImage(g_allocator, &info, &allocateInfo, &image.image, &image.allocation, nullptr);
|
||||||
|
|
||||||
|
return image;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSampler createSampler(VkFilter filters, VkSamplerAddressMode mode) {
|
||||||
|
VkSamplerCreateInfo info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||||
|
info.pNext = nullptr;
|
||||||
|
info.magFilter = filters;
|
||||||
|
info.minFilter = filters;
|
||||||
|
info.addressModeU = mode;
|
||||||
|
info.addressModeV = mode;
|
||||||
|
info.addressModeW = mode;
|
||||||
|
|
||||||
|
VkSampler sampler;
|
||||||
|
vkCreateSampler(g_Device, &info, nullptr, &sampler);
|
||||||
|
|
||||||
|
return sampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags, VkDevice device) {
|
||||||
|
// Raw information about the image
|
||||||
|
VkImageViewCreateInfo viewInfo = {};
|
||||||
|
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
|
viewInfo.image = image;
|
||||||
|
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
viewInfo.format = format;
|
||||||
|
|
||||||
|
// Information about the things we want to create - size, mip levels.
|
||||||
|
viewInfo.subresourceRange.aspectMask = flags;
|
||||||
|
viewInfo.subresourceRange.baseMipLevel = 0;
|
||||||
|
viewInfo.subresourceRange.levelCount = 1;
|
||||||
|
viewInfo.subresourceRange.baseArrayLayer = 0;
|
||||||
|
viewInfo.subresourceRange.layerCount = 1;
|
||||||
|
|
||||||
|
VkImageView imageView;
|
||||||
|
if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Failed to create texture image view.");
|
||||||
|
|
||||||
|
return imageView;
|
||||||
|
}
|
||||||
|
|
||||||
|
ManagedBuffer createGPUBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkDevice logicalDevice, VkPhysicalDevice physicalDevice) {
|
||||||
|
// Prepare for creation of a buffer
|
||||||
|
VkBufferCreateInfo bufferInfo = {};
|
||||||
|
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||||
|
bufferInfo.size = size;
|
||||||
|
bufferInfo.usage = usage;
|
||||||
|
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
|
||||||
|
ManagedBuffer buffer;
|
||||||
|
|
||||||
|
VmaAllocationCreateInfo vmaInfo = {};
|
||||||
|
vmaInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
||||||
|
vmaInfo.requiredFlags = properties;
|
||||||
|
vmaInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||||
|
|
||||||
|
// Create the buffer.
|
||||||
|
if (vmaCreateBuffer(g_allocator, &bufferInfo, &vmaInfo, &buffer.buffer, &buffer.allocation, nullptr) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to create GPU buffer");
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t findMemoryIndex(uint32_t type, VkMemoryPropertyFlags properties, VkPhysicalDevice physicalDevice) {
|
||||||
|
// Get the physical properties of the device.
|
||||||
|
VkPhysicalDeviceMemoryProperties physProperties;
|
||||||
|
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &physProperties);
|
||||||
|
|
||||||
|
// Iterate the device and search for a suitable index
|
||||||
|
for (uint32_t i = 0; i < physProperties.memoryTypeCount; i++)
|
||||||
|
// If the type matches, and the properties are what we desire, then ship it.
|
||||||
|
if ((type & (1 << i)) && ((physProperties.memoryTypes[i].propertyFlags & properties) == properties))
|
||||||
|
return i;
|
||||||
|
|
||||||
|
throw std::runtime_error("Unable to find a suitable memory type on the physical device.");
|
||||||
|
}
|
||||||
|
|
||||||
|
VkCommandBuffer createTempCommandBuffer(VkCommandPool pool, VkDevice logical) {
|
||||||
|
// Prepare to allocate a command buffer
|
||||||
|
VkCommandBufferAllocateInfo allocateInfo = {};
|
||||||
|
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||||
|
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||||
|
allocateInfo.commandPool = pool;
|
||||||
|
allocateInfo.commandBufferCount = 1;
|
||||||
|
|
||||||
|
// Allocate the buffer
|
||||||
|
VkCommandBuffer buffer;
|
||||||
|
vkAllocateCommandBuffers(logical, &allocateInfo, &buffer);
|
||||||
|
|
||||||
|
// Prepare to begin the new buffer.
|
||||||
|
VkCommandBufferBeginInfo beginInfo = {};
|
||||||
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||||
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||||
|
|
||||||
|
// Begin listening on the new buffer.
|
||||||
|
vkBeginCommandBuffer(buffer, &beginInfo);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void executeAndDeleteTempBuffer(VkCommandBuffer buffer, VkCommandPool pool, VkQueue queue, VkDevice logicalDevice) {
|
||||||
|
// Stop listening on the buffer
|
||||||
|
vkEndCommandBuffer(buffer);
|
||||||
|
|
||||||
|
// Prepare to execute the commands in the buffer
|
||||||
|
VkSubmitInfo submitInfo = {};
|
||||||
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||||
|
submitInfo.commandBufferCount = 1;
|
||||||
|
submitInfo.pCommandBuffers = &buffer;
|
||||||
|
|
||||||
|
// Submit the commands to be executed
|
||||||
|
vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
|
||||||
|
|
||||||
|
// Wait for the GPU to finish executing
|
||||||
|
vkQueueWaitIdle(queue);
|
||||||
|
|
||||||
|
// Delete the now unusable buffers
|
||||||
|
vkFreeCommandBuffers(logicalDevice, pool, 1, &buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void copyGPUBuffer(VkBuffer source, VkBuffer dest, VkDeviceSize length, VkDevice logical, VkQueue graphicsQueue, uint32_t queueIndex) {
|
||||||
|
|
||||||
|
// Prepare to create a temporary command pool.
|
||||||
|
VkCommandPool pool;
|
||||||
|
VkCommandPoolCreateInfo poolCreateInfo = {};
|
||||||
|
poolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||||
|
poolCreateInfo.queueFamilyIndex = queueIndex;
|
||||||
|
poolCreateInfo.flags = 0;
|
||||||
|
|
||||||
|
// Create the pool
|
||||||
|
if (vkCreateCommandPool(logical, &poolCreateInfo, nullptr, &pool) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to allocate a temporary command pool");
|
||||||
|
|
||||||
|
// Allocate a buffer
|
||||||
|
VkCommandBuffer commands = createTempCommandBuffer(pool, logical);
|
||||||
|
|
||||||
|
// ------ Commands are saved into the commands field ------ //
|
||||||
|
|
||||||
|
// Prepare to copy the data between buffers
|
||||||
|
VkBufferCopy copyInfo = {};
|
||||||
|
copyInfo.srcOffset = 0;
|
||||||
|
copyInfo.dstOffset = 0;
|
||||||
|
copyInfo.size = length;
|
||||||
|
|
||||||
|
// Copy the data.
|
||||||
|
vkCmdCopyBuffer(commands, source, dest, 1, ©Info);
|
||||||
|
|
||||||
|
// ------ Commands are no longer saved into the commands field ------ //
|
||||||
|
|
||||||
|
executeAndDeleteTempBuffer(commands, pool, graphicsQueue, logical);
|
||||||
|
|
||||||
|
// Cleanup the temporary buffer and pool we created
|
||||||
|
vkDestroyCommandPool(logical, pool, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
class ValidationAndExtension {
|
||||||
|
public:
|
||||||
|
ValidationAndExtension();
|
||||||
|
~ValidationAndExtension();
|
||||||
|
|
||||||
|
const std::vector<const char*> requiredValidations = {
|
||||||
|
"VK_LAYER_KHRONOS_validation"
|
||||||
|
};
|
||||||
|
|
||||||
|
VkDebugReportCallbackEXT callback;
|
||||||
|
|
||||||
|
bool checkValidationSupport();
|
||||||
|
|
||||||
|
std::vector<const char*> getRequiredExtensions(SDL_Window* window, bool validationsRequired);
|
||||||
|
void setupDebugCallback(bool validationsRequired, VkInstance vulkan);
|
||||||
|
void destroy(bool validationsRequired, VkInstance vulkan);
|
||||||
|
|
||||||
|
|
||||||
|
VkResult createDebugReportCallbackEXT(
|
||||||
|
VkInstance vulkan,
|
||||||
|
const VkDebugReportCallbackCreateInfoEXT* info,
|
||||||
|
const VkAllocationCallbacks* allocator,
|
||||||
|
VkDebugReportCallbackEXT* callback) {
|
||||||
|
|
||||||
|
auto func = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(vulkan, "vkCreateDebugReportCallbackEXT");
|
||||||
|
|
||||||
|
if (func != nullptr) {
|
||||||
|
return func(vulkan, info, allocator, callback);
|
||||||
|
} else {
|
||||||
|
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyDebugReportCallbackEXT(
|
||||||
|
VkInstance vulkan,
|
||||||
|
const VkDebugReportCallbackEXT callback,
|
||||||
|
const VkAllocationCallbacks* allocator) {
|
||||||
|
|
||||||
|
auto func = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(vulkan, "vkDestroyDebugReportCallbackEXT");
|
||||||
|
|
||||||
|
if (func != nullptr) {
|
||||||
|
func(vulkan, callback, allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
56
projs/shadow/shadow-renderer/inc/vlkx/vulkan/VulkanDevice.h
Normal file
56
projs/shadow/shadow-renderer/inc/vlkx/vulkan/VulkanDevice.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <vlkx/vulkan/ValidationAndExtension.h>
|
||||||
|
|
||||||
|
struct SwapChainMeta {
|
||||||
|
VkSurfaceCapabilitiesKHR capabilities;
|
||||||
|
std::vector<VkSurfaceFormatKHR> formats;
|
||||||
|
std::vector<VkPresentModeKHR> modes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct QueueFamilies {
|
||||||
|
int graphics = -1;
|
||||||
|
int presentation = -1;
|
||||||
|
|
||||||
|
bool present() {
|
||||||
|
return graphics >= 0 && presentation >= 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VulkanDevice {
|
||||||
|
public:
|
||||||
|
VulkanDevice();
|
||||||
|
~VulkanDevice();
|
||||||
|
|
||||||
|
/** Physical Devices **/
|
||||||
|
VkPhysicalDevice physical;
|
||||||
|
SwapChainMeta swapChain;
|
||||||
|
QueueFamilies queueData;
|
||||||
|
|
||||||
|
std::vector<const char*> deviceExtensions = {
|
||||||
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME
|
||||||
|
};
|
||||||
|
|
||||||
|
void choosePhysicalDevice(VkInstance* vulkan, VkSurfaceKHR surface);
|
||||||
|
bool isSuitable(VkPhysicalDevice device, VkSurfaceKHR surface);
|
||||||
|
bool isSupported(VkPhysicalDevice device);
|
||||||
|
|
||||||
|
SwapChainMeta checkSwapchain(VkPhysicalDevice device, VkSurfaceKHR surface);
|
||||||
|
QueueFamilies checkQueues(VkPhysicalDevice device, VkSurfaceKHR surface);
|
||||||
|
|
||||||
|
QueueFamilies getQueues() { return queueData; }
|
||||||
|
|
||||||
|
/** Logical Devices **/
|
||||||
|
VkDevice logical;
|
||||||
|
VkQueue graphicsQueue;
|
||||||
|
VkQueue presentationQueue;
|
||||||
|
|
||||||
|
void createLogicalDevice(VkSurfaceKHR surface, bool validationRequired, ValidationAndExtension* validators);
|
||||||
|
void destroy();
|
||||||
|
};
|
95
projs/shadow/shadow-renderer/inc/vlkx/vulkan/VulkanManager.h
Normal file
95
projs/shadow/shadow-renderer/inc/vlkx/vulkan/VulkanManager.h
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vlkx/vulkan/ValidationAndExtension.h>
|
||||||
|
#include <vlkx/vulkan/VulkanDevice.h>
|
||||||
|
#include <vlkx/vulkan/SwapChain.h>
|
||||||
|
#include <vlkx/render/framebuffer/RenderPass.h>
|
||||||
|
#include <vlkx/render/texture/RenderTexture.h>
|
||||||
|
#include <vlkx/vulkan/CommandBuffer.h>
|
||||||
|
|
||||||
|
#include <vulkan/vk_mem_alloc.h>
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
#include <vlkx/vulkan/Tools.h>
|
||||||
|
|
||||||
|
#include <SDL_vulkan.h>
|
||||||
|
|
||||||
|
class VulkanManager {
|
||||||
|
public:
|
||||||
|
VulkanManager();
|
||||||
|
~VulkanManager();
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
static const bool validationRequired = true;
|
||||||
|
#else
|
||||||
|
static const bool validationRequired = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// VulkanManager is a singleton class.
|
||||||
|
static VulkanManager* instance;
|
||||||
|
static VulkanManager* getInstance();
|
||||||
|
|
||||||
|
// Initialize all Vulkan context and prepare validations in debug mode.
|
||||||
|
void initVulkan(SDL_Window* window);
|
||||||
|
void createAppAndVulkanInstance(bool enableValidation, ValidationAndExtension* validations);
|
||||||
|
|
||||||
|
// Start and end a frame render.
|
||||||
|
void startDraw();
|
||||||
|
void endDraw();
|
||||||
|
|
||||||
|
// Cleanup after the application has closed.
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
|
void useRayTrace() { rayTraceMode = true; }
|
||||||
|
|
||||||
|
VkInstance getVulkan() { return vulkan; }
|
||||||
|
VulkanDevice* getDevice() { return device; }
|
||||||
|
SwapChain* getSwapchain() { return swapchain; }
|
||||||
|
RenderPass* getRenderPass() { return renderPass; }
|
||||||
|
VkCommandBuffer getCurrentCommandBuffer() { return currentCommandBuffer; }
|
||||||
|
VmaAllocator getAllocator() { return allocator; }
|
||||||
|
RenderTexture* getRenderTarget() { return renderTexture; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// To keep track of the window during... stuff
|
||||||
|
SDL_Window* wnd;
|
||||||
|
|
||||||
|
// To handle the validation of Vulkan API usage (because fuck you, it's your problem now)
|
||||||
|
ValidationAndExtension* validators{};
|
||||||
|
// To manage interaction with the hardware
|
||||||
|
VulkanDevice* device{};
|
||||||
|
// To handle the framebuffers
|
||||||
|
SwapChain* swapchain{};
|
||||||
|
// To handle the timing of rendering
|
||||||
|
RenderPass* renderPass{};
|
||||||
|
|
||||||
|
// To handle automatic management of memory.
|
||||||
|
VmaAllocator allocator{};
|
||||||
|
|
||||||
|
// The default RenderTexture, mirroring the SwapChain to the viewport.
|
||||||
|
RenderTexture* renderTexture;
|
||||||
|
// The command buffers used when telling the firmware to do things for us.
|
||||||
|
CommandBuffer* buffers{};
|
||||||
|
|
||||||
|
// To manage the Vulkan context that was passed to us by the API
|
||||||
|
VkInstance vulkan{};
|
||||||
|
// To manage the canvas that was given to us by GLFW
|
||||||
|
VkSurfaceKHR surface{};
|
||||||
|
|
||||||
|
// The index of the texture that is currently being used by the GPU.
|
||||||
|
uint32_t imageIndex = 0;
|
||||||
|
// The command buffer currently being used by the GPU.
|
||||||
|
VkCommandBuffer currentCommandBuffer{};
|
||||||
|
|
||||||
|
// The maximum number of frames that can be dealt with at a time.
|
||||||
|
const int MAX_FRAMES = 2; // Double-buffering requires two frames in memory
|
||||||
|
// Raised when a new image is available
|
||||||
|
VkSemaphore newImageSem{};
|
||||||
|
// Raised when a render is finished
|
||||||
|
VkSemaphore renderDoneSem{};
|
||||||
|
// Stores fences for frames that are currently "in flight".
|
||||||
|
std::vector<VkFence> inFlight;
|
||||||
|
|
||||||
|
bool rayTraceMode;
|
||||||
|
|
||||||
|
};
|
19564
projs/shadow/shadow-renderer/inc/vulkan/vk_mem_alloc.h
Normal file
19564
projs/shadow/shadow-renderer/inc/vulkan/vk_mem_alloc.h
Normal file
File diff suppressed because it is too large
Load Diff
20
projs/shadow/shadow-renderer/src/render/Camera.cpp
Normal file
20
projs/shadow/shadow-renderer/src/render/Camera.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include <vlkx/render/Camera.h>
|
||||||
|
|
||||||
|
void Camera::init(float fov, float width, float height, float near, float far) {
|
||||||
|
|
||||||
|
// Initialise members
|
||||||
|
position = glm::vec3(0, 0, 4);
|
||||||
|
|
||||||
|
glm::vec3 cameraFront = glm::vec3(0, 0, 0);
|
||||||
|
glm::vec3 cameraUp = glm::vec3(0, 1, 0);
|
||||||
|
|
||||||
|
viewMatrix = glm::mat4(1);
|
||||||
|
projectionMatrix = glm::mat4(1);
|
||||||
|
|
||||||
|
projectionMatrix = glm::perspective(fov, width / height, near, far);
|
||||||
|
viewMatrix = glm::lookAt(position, cameraFront, cameraUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::setPosition(glm::vec3 newPosition) {
|
||||||
|
position = newPosition;
|
||||||
|
}
|
156
projs/shadow/shadow-renderer/src/render/Geometry.cpp
Normal file
156
projs/shadow/shadow-renderer/src/render/Geometry.cpp
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
#include <vlkx/render/Geometry.h>
|
||||||
|
using namespace Geo;
|
||||||
|
|
||||||
|
void Mesh::setTriData(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices) {
|
||||||
|
std::vector<Vertex> Vertices = {
|
||||||
|
{ { 0.0f, -1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 1.0f, 0.0f, 0.0 },{ 0.0, 1.0 } },
|
||||||
|
{ { 1.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0f, 1.0f, 0.0 },{ 0.0, 0.0 } },
|
||||||
|
{ { -1.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0f, 0.0f, 1.0 },{ 1.0, 0.0 } },
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<uint32_t> Indices = {
|
||||||
|
0, 1, 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
vertices.clear(); indices.clear();
|
||||||
|
|
||||||
|
vertices = Vertices;
|
||||||
|
indices = Indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mesh::setQuadData(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices) {
|
||||||
|
|
||||||
|
std::vector<Vertex> Vertices = {
|
||||||
|
{ { -1.0f, -1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 1.0f, 0.0f, 0.0 },{ 0.0, 1.0 } },
|
||||||
|
{ { -1.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0f, 1.0f, 0.0 },{ 0.0, 0.0 } },
|
||||||
|
{ { 1.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0f, 0.0f, 1.0 },{ 1.0, 0.0 } },
|
||||||
|
{ { 1.0f, -1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 1.0f, 0.0f, 1.0 },{ 1.0, 1.0 } }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<uint32_t> Indices = {
|
||||||
|
0, 1, 2,
|
||||||
|
0, 2, 3
|
||||||
|
};
|
||||||
|
|
||||||
|
vertices.clear(); indices.clear();
|
||||||
|
vertices = Vertices;
|
||||||
|
indices = Indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mesh::setCubeData(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices) {
|
||||||
|
std::vector<Vertex> Vertices = {
|
||||||
|
// Front
|
||||||
|
{ { -1.0f, -1.0f, 1.0f },{ 0.0f, 0.0f, 1.0 },{ 1.0f, 0.0f, 0.0 },{ 0.0, 1.0 } }, // 0
|
||||||
|
{ { -1.0f, 1.0f, 1.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0f, 1.0f, 0.0 },{ 0.0, 0.0 } }, // 1
|
||||||
|
{ { 1.0f, 1.0f, 1.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0f, 0.0f, 1.0 },{ 1.0, 0.0 } }, // 2
|
||||||
|
{ { 1.0f, -1.0f, 1.0f },{ 0.0f, 0.0f, 1.0 },{ 1.0f, 0.0f, 1.0 },{ 1.0, 1.0 } }, // 3
|
||||||
|
// Back
|
||||||
|
{ { 1.0, -1.0, -1.0 },{ 0.0f, 0.0f, -1.0 },{ 1.0f, 0.0f, 1.0 },{ 0.0, 1.0 } }, // 4
|
||||||
|
{ { 1.0f, 1.0, -1.0 },{ 0.0f, 0.0f, -1.0 },{ 0.0f, 1.0f, 1.0 },{ 0.0, 0.0 } }, // 5
|
||||||
|
{ { -1.0, 1.0, -1.0 },{ 0.0f, 0.0f, -1.0 },{ 0.0f, 1.0f, 1.0 },{ 1.0, 0.0 } }, // 6
|
||||||
|
{ { -1.0, -1.0, -1.0 },{ 0.0f, 0.0f, -1.0 },{ 1.0f, 0.0f, 1.0 },{ 1.0, 1.0 } }, // 7
|
||||||
|
// Left
|
||||||
|
{ { -1.0, -1.0, -1.0 },{ -1.0f, 0.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 0.0, 1.0 } }, // 8
|
||||||
|
{ { -1.0f, 1.0, -1.0 },{ -1.0f, 0.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 0.0, 0.0 } }, // 9
|
||||||
|
{ { -1.0, 1.0, 1.0 },{ -1.0f, 0.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 1.0, 0.0 } }, // 10
|
||||||
|
{ { -1.0, -1.0, 1.0 },{ -1.0f, 0.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 1.0, 1.0 } }, // 11
|
||||||
|
// Right
|
||||||
|
{ { 1.0, -1.0, 1.0 },{ 1.0f, 0.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 0.0, 1.0 } }, // 12
|
||||||
|
{ { 1.0f, 1.0, 1.0 },{ 1.0f, 0.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 0.0, 0.0 } }, // 13
|
||||||
|
{ { 1.0, 1.0, -1.0 },{ 1.0f, 0.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 1.0, 0.0 } }, // 14
|
||||||
|
{ { 1.0, -1.0, -1.0 },{ 1.0f, 0.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 1.0, 1.0 } }, // 15
|
||||||
|
// Top
|
||||||
|
{ { -1.0f, 1.0f, 1.0f },{ 0.0f, 1.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 0.0, 1.0 } }, // 16
|
||||||
|
{ { -1.0f, 1.0f, -1.0f },{ 0.0f, 1.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 0.0, 0.0 } }, // 17
|
||||||
|
{ { 1.0f, 1.0f, -1.0f },{ 0.0f, 1.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 1.0, 0.0 } }, // 18
|
||||||
|
{ { 1.0f, 1.0f, 1.0f },{ 0.0f, 1.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 1.0, 1.0 } }, // 19
|
||||||
|
// Bottom
|
||||||
|
{ { -1.0f, -1.0, -1.0 },{ 0.0f, -1.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 0.0, 1.0 } }, // 20
|
||||||
|
{ { -1.0, -1.0, 1.0 },{ 0.0f, -1.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 0.0, 0.0 } }, // 21
|
||||||
|
{ { 1.0, -1.0, 1.0 },{ 0.0f, -1.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 1.0, 0.0 } }, // 22
|
||||||
|
{ { 1.0, -1.0, -1.0 },{ 0.0f, -1.0f, 0.0 },{ 0.0f, 0.0f, 1.0 },{ 1.0, 1.0 } }, // 23
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<uint32_t> Indices = {
|
||||||
|
0, 1, 2,
|
||||||
|
2, 3, 0,
|
||||||
|
|
||||||
|
4, 5, 6,
|
||||||
|
4, 6, 7,
|
||||||
|
|
||||||
|
8, 9, 10,
|
||||||
|
8, 10, 11,
|
||||||
|
|
||||||
|
12, 13, 14,
|
||||||
|
12, 14, 15,
|
||||||
|
|
||||||
|
16, 17, 18,
|
||||||
|
16, 18, 19,
|
||||||
|
|
||||||
|
20, 21, 22,
|
||||||
|
20, 22, 23
|
||||||
|
};
|
||||||
|
|
||||||
|
vertices.clear(); indices.clear();
|
||||||
|
vertices = Vertices;
|
||||||
|
indices = Indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Mesh::setSphereData(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices) {
|
||||||
|
std::vector<Vertex> Vertices;
|
||||||
|
std::vector<uint32_t> Indices;
|
||||||
|
|
||||||
|
float latitudeBands = 20.0f;
|
||||||
|
float longitudeBands = 20.0f;
|
||||||
|
float radius = 1.0f;
|
||||||
|
|
||||||
|
for (float latNumber = 0; latNumber <= latitudeBands; latNumber++) {
|
||||||
|
float theta = latNumber * 3.14 / latitudeBands;
|
||||||
|
float sinTheta = sin(theta);
|
||||||
|
float cosTheta = cos(theta);
|
||||||
|
|
||||||
|
for (float longNumber = 0; longNumber <= longitudeBands; longNumber++) {
|
||||||
|
|
||||||
|
float phi = longNumber * 2 * 3.147 / longitudeBands;
|
||||||
|
float sinPhi = sin(phi);
|
||||||
|
float cosPhi = cos(phi);
|
||||||
|
|
||||||
|
Vertex vs;
|
||||||
|
|
||||||
|
vs.texture.x = (longNumber / longitudeBands); // u
|
||||||
|
vs.texture.y = (latNumber / latitudeBands); // v
|
||||||
|
|
||||||
|
vs.normal.x = cosPhi * sinTheta; // normal x
|
||||||
|
vs.normal.y = cosTheta; // normal y
|
||||||
|
vs.normal.z = sinPhi * sinTheta; // normal z
|
||||||
|
|
||||||
|
vs.position.x = radius * vs.normal.x; // x
|
||||||
|
vs.position.y = radius * vs.normal.y; // y
|
||||||
|
vs.position.z = radius * vs.normal.z; // z
|
||||||
|
|
||||||
|
Vertices.push_back(vs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t latNumber = 0; latNumber < latitudeBands; latNumber++) {
|
||||||
|
for (uint32_t longNumber = 0; longNumber < longitudeBands; longNumber++) {
|
||||||
|
uint32_t first = (latNumber * (longitudeBands + 1)) + longNumber;
|
||||||
|
uint32_t second = first + longitudeBands + 1;
|
||||||
|
|
||||||
|
Indices.push_back(first);
|
||||||
|
Indices.push_back(second);
|
||||||
|
Indices.push_back(first + 1);
|
||||||
|
|
||||||
|
Indices.push_back(second);
|
||||||
|
Indices.push_back(second + 1);
|
||||||
|
Indices.push_back(first + 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vertices.clear(); indices.clear();
|
||||||
|
vertices = Vertices;
|
||||||
|
indices = Indices;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include <vlkx/render/framebuffer/RenderPass.h>
|
||||||
|
#include <vlkx/vulkan/VulkanManager.h>
|
||||||
|
|
||||||
|
RenderPass::RenderPass() {}
|
||||||
|
RenderPass::~RenderPass() {}
|
||||||
|
|
||||||
|
|
||||||
|
void RenderPass::createVertexRenderPass(VkFormat format) {
|
||||||
|
// Set up color metadata
|
||||||
|
VkAttachmentDescription color = {};
|
||||||
|
color.format = format;
|
||||||
|
color.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||||
|
color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||||
|
color.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||||
|
color.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||||
|
color.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
color.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||||
|
|
||||||
|
// Set up subattachments
|
||||||
|
VkAttachmentReference colorReference = {};
|
||||||
|
colorReference.attachment = 0;
|
||||||
|
colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||||
|
|
||||||
|
VkSubpassDescription subpass = {};
|
||||||
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||||
|
subpass.colorAttachmentCount = 1;
|
||||||
|
subpass.pColorAttachments = &colorReference;
|
||||||
|
|
||||||
|
// Prepare the Render Pass for creation
|
||||||
|
std::array<VkAttachmentDescription, 1> attachments = { color };
|
||||||
|
VkRenderPassCreateInfo createInfo = {};
|
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||||
|
createInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
|
||||||
|
createInfo.pAttachments = attachments.data();
|
||||||
|
createInfo.subpassCount = 1;
|
||||||
|
createInfo.pSubpasses = &subpass;
|
||||||
|
|
||||||
|
// Create the Render Pass
|
||||||
|
if (vkCreateRenderPass(VulkanManager::getInstance()->getDevice()->logical, &createInfo, nullptr, &pass))
|
||||||
|
throw std::runtime_error("Unable to create Render Pass 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderPass::beginRenderPass(std::vector<VkClearValue> clearValues, VkCommandBuffer commands, VkFramebuffer framebuffer, VkExtent2D extent) {
|
||||||
|
// Prepare the Render Pass for start
|
||||||
|
VkRenderPassBeginInfo info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||||
|
info.renderPass = pass;
|
||||||
|
info.framebuffer = framebuffer;
|
||||||
|
|
||||||
|
info.renderArea.offset = { 0, 0 };
|
||||||
|
info.renderArea.extent = extent;
|
||||||
|
|
||||||
|
info.pClearValues = clearValues.data();
|
||||||
|
info.clearValueCount = static_cast<uint32_t>(clearValues.size());
|
||||||
|
|
||||||
|
// Attempt to begin the pass
|
||||||
|
vkCmdBeginRenderPass(commands, &info, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderPass::endRenderPass(VkCommandBuffer commands) {
|
||||||
|
vkCmdEndRenderPass(commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RenderPass::destroy() {
|
||||||
|
vkDestroyRenderPass(VulkanManager::getInstance()->getDevice()->logical, pass, nullptr);
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
#include <vlkx/render/geometry/SingleRenderer.h>
|
||||||
|
#include <vlkx/vulkan/VulkanManager.h>
|
||||||
|
|
||||||
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
void SingleRenderer::createSingleRenderer(Geo::MeshType type, glm::vec3 posIn, glm::vec3 scaleIn) {
|
||||||
|
|
||||||
|
// Collect metadata about the swap chain
|
||||||
|
uint32_t imageCount = VulkanManager::getInstance()->getSwapchain()->images.size();
|
||||||
|
VkExtent2D imageExtent = VulkanManager::getInstance()->getSwapchain()->extent;
|
||||||
|
|
||||||
|
// Create the buffers
|
||||||
|
buffers.createBuffers(type);
|
||||||
|
|
||||||
|
// Create the descriptor layout
|
||||||
|
descriptor.createAndAllocateSimple(imageCount);
|
||||||
|
descriptor.populate(imageCount, buffers.uniformBuffer.buffer, sizeof(Geo::UniformBufferObject));
|
||||||
|
|
||||||
|
// Create the pipeline
|
||||||
|
pipeline.create(imageExtent, descriptor.layout, VulkanManager::getInstance()->getRenderPass()->pass);
|
||||||
|
|
||||||
|
// Set locals
|
||||||
|
position = posIn;
|
||||||
|
scale = scaleIn;
|
||||||
|
rotation = glm::mat4_cast(glm::quat(0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleRenderer::updateUniforms(Camera camera) {
|
||||||
|
|
||||||
|
// Prepare data to go into the buffers
|
||||||
|
Geo::UniformBufferObject ubo = {};
|
||||||
|
|
||||||
|
glm::mat4 scaleMatrix = glm::mat4(1.0f);
|
||||||
|
glm::mat4 rotationMatrix = getRotation();
|
||||||
|
glm::mat4 translationMatrix = glm::mat4(1.0f);
|
||||||
|
|
||||||
|
scaleMatrix = glm::scale(scaleMatrix, scale);
|
||||||
|
translationMatrix = glm::translate(translationMatrix, position);
|
||||||
|
|
||||||
|
ubo.model = translationMatrix * rotationMatrix * scaleMatrix;
|
||||||
|
ubo.view = camera.getViewMatrix();
|
||||||
|
ubo.proj = camera.getProjectionMatrix();
|
||||||
|
|
||||||
|
ubo.proj[1][1] *= -1;
|
||||||
|
|
||||||
|
// Copy the buffers into the GPU
|
||||||
|
void* data;
|
||||||
|
vmaMapMemory(VulkanManager::getInstance()->getAllocator(), buffers.uniformBuffer.allocation, &data);
|
||||||
|
memcpy(data, &ubo, sizeof(ubo));
|
||||||
|
vmaUnmapMemory(VulkanManager::getInstance()->getAllocator(), buffers.uniformBuffer.allocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleRenderer::draw() {
|
||||||
|
// Fetch the buffer we're going to insert commands to
|
||||||
|
VkCommandBuffer commands = VulkanManager::getInstance()->getCurrentCommandBuffer();
|
||||||
|
|
||||||
|
// Bind our pipeline
|
||||||
|
vkCmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline);
|
||||||
|
|
||||||
|
// Bind the vertex buffer
|
||||||
|
VkBuffer vertexBuffers[] = {
|
||||||
|
buffers.vertexBuffer.buffer
|
||||||
|
};
|
||||||
|
|
||||||
|
VkDeviceSize offsets[] = {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
vkCmdBindVertexBuffers(commands, 0, 1, vertexBuffers, offsets);
|
||||||
|
|
||||||
|
// Bind the index buffer
|
||||||
|
vkCmdBindIndexBuffer(commands, buffers.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);
|
||||||
|
|
||||||
|
// Bind the uniform buffer
|
||||||
|
vkCmdBindDescriptorSets(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, 1, &descriptor.set, 0, nullptr);
|
||||||
|
|
||||||
|
// Draw the buffered geometry
|
||||||
|
vkCmdDrawIndexed(commands, static_cast<uint32_t>(buffers.indices.size()), 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleRenderer::destroy() {
|
||||||
|
pipeline.destroy();
|
||||||
|
descriptor.destroy();
|
||||||
|
buffers.destroy();
|
||||||
|
}
|
137
projs/shadow/shadow-renderer/src/render/shader/Descriptor.cpp
Normal file
137
projs/shadow/shadow-renderer/src/render/shader/Descriptor.cpp
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
#include <vlkx/render/shader/Descriptor.h>
|
||||||
|
#include <vlkx/vulkan/VulkanManager.h>
|
||||||
|
#include <vlkx/render/Geometry.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
Descriptor::Descriptor() {}
|
||||||
|
Descriptor::~Descriptor() {}
|
||||||
|
|
||||||
|
void Descriptor::createAndAllocateSimple(uint32_t images) {
|
||||||
|
createSimpleLayout();
|
||||||
|
createSimplePool(images);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Descriptor::createSimpleLayout() {
|
||||||
|
// Set up a binding
|
||||||
|
VkDescriptorSetLayoutBinding binding = {};
|
||||||
|
binding.binding = 0;
|
||||||
|
binding.descriptorCount = 1;
|
||||||
|
binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
|
binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||||
|
|
||||||
|
// Prepare to create the layout
|
||||||
|
std::array<VkDescriptorSetLayoutBinding, 1> bindings = {
|
||||||
|
binding
|
||||||
|
};
|
||||||
|
|
||||||
|
VkDescriptorSetLayoutCreateInfo info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||||
|
info.bindingCount = static_cast<uint32_t>(bindings.size());
|
||||||
|
info.pBindings = bindings.data();
|
||||||
|
|
||||||
|
// Create the layout
|
||||||
|
if (vkCreateDescriptorSetLayout(VulkanManager::getInstance()->getDevice()->logical, &info, nullptr, &layout) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to create Descriptor layout");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Descriptor::createSimplePool(uint32_t images) {
|
||||||
|
// Set the size of the pool we want
|
||||||
|
VkDescriptorPoolSize size = {};
|
||||||
|
size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
|
size.descriptorCount = images;
|
||||||
|
|
||||||
|
std::array<VkDescriptorPoolSize, 1> sizes = {
|
||||||
|
size
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prepare to create the pool
|
||||||
|
VkDescriptorPoolCreateInfo createInfo = {};
|
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||||
|
createInfo.poolSizeCount = static_cast<uint32_t>(sizes.size());
|
||||||
|
createInfo.pPoolSizes = sizes.data();
|
||||||
|
createInfo.maxSets = images;
|
||||||
|
|
||||||
|
// Create the pool
|
||||||
|
if (vkCreateDescriptorPool(VulkanManager::getInstance()->getDevice()->logical, &createInfo, nullptr, &pool) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to create Descriptor pool");
|
||||||
|
|
||||||
|
// Prepare to allocate the set
|
||||||
|
std::vector<VkDescriptorSetLayout> layouts(images, layout);
|
||||||
|
VkDescriptorSetAllocateInfo allocateInfo = {};
|
||||||
|
allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||||
|
allocateInfo.descriptorPool = pool;
|
||||||
|
allocateInfo.descriptorSetCount = images;
|
||||||
|
allocateInfo.pSetLayouts = layouts.data();
|
||||||
|
|
||||||
|
// Allocate the Set
|
||||||
|
if (vkAllocateDescriptorSets(VulkanManager::getInstance()->getDevice()->logical, &allocateInfo, &set) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to allocate Descriptor set");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Descriptor::populate(uint32_t images, VkBuffer uniforms, size_t bufferSize) {
|
||||||
|
// Iterate images
|
||||||
|
for (size_t i = 0; i < images; i++) {
|
||||||
|
// Set up the uniform buffer
|
||||||
|
VkDescriptorBufferInfo buffer = {};
|
||||||
|
buffer.buffer = uniforms;
|
||||||
|
buffer.offset = 0;
|
||||||
|
buffer.range = bufferSize;
|
||||||
|
|
||||||
|
// Prepare to write the buffer into the Descriptor set
|
||||||
|
VkWriteDescriptorSet write = {};
|
||||||
|
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
write.pNext = nullptr;
|
||||||
|
write.dstSet = set;
|
||||||
|
write.dstBinding = 0;
|
||||||
|
write.dstArrayElement = 0;
|
||||||
|
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
|
write.descriptorCount = 1;
|
||||||
|
write.pBufferInfo = &buffer;
|
||||||
|
write.pImageInfo = nullptr;
|
||||||
|
write.pTexelBufferView = nullptr;
|
||||||
|
|
||||||
|
std::array<VkWriteDescriptorSet, 1> writes = {
|
||||||
|
write
|
||||||
|
};
|
||||||
|
|
||||||
|
// Write the buffer into the descriptor
|
||||||
|
vkUpdateDescriptorSets(VulkanManager::getInstance()->getDevice()->logical, static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Descriptor::destroy() {
|
||||||
|
vkDestroyDescriptorPool(VulkanManager::getInstance()->getDevice()->logical, pool, nullptr);
|
||||||
|
vkDestroyDescriptorSetLayout(VulkanManager::getInstance()->getDevice()->logical, layout, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkDescriptorSetLayout createBindingWrapper(std::vector<VkDescriptorSetLayoutBinding> bindings) {
|
||||||
|
VkDescriptorSetLayoutCreateInfo info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||||
|
info.bindingCount = static_cast<uint32_t>(bindings.size());
|
||||||
|
info.pBindings = bindings.data();
|
||||||
|
|
||||||
|
VkDescriptorSetLayout layout;
|
||||||
|
|
||||||
|
// Create the layout
|
||||||
|
if (vkCreateDescriptorSetLayout(VulkanManager::getInstance()->getDevice()->logical, &info, nullptr, &layout) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to create Descriptor layout");
|
||||||
|
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
VkDescriptorPool createPoolWrapper(std::vector<VkDescriptorPoolSize> sizes, uint32_t images) {
|
||||||
|
// Prepare to create the pool
|
||||||
|
VkDescriptorPoolCreateInfo createInfo = {};
|
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||||
|
createInfo.poolSizeCount = static_cast<uint32_t>(sizes.size());
|
||||||
|
createInfo.pPoolSizes = sizes.data();
|
||||||
|
createInfo.maxSets = images;
|
||||||
|
|
||||||
|
VkDescriptorPool pool;
|
||||||
|
|
||||||
|
// Create the pool
|
||||||
|
if (vkCreateDescriptorPool(VulkanManager::getInstance()->getDevice()->logical, &createInfo, nullptr, &pool) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to create Descriptor pool");
|
||||||
|
|
||||||
|
return pool;
|
||||||
|
}
|
101
projs/shadow/shadow-renderer/src/render/shader/GeoBuffers.cpp
Normal file
101
projs/shadow/shadow-renderer/src/render/shader/GeoBuffers.cpp
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
#include <vlkx/render/shader/GeoBuffers.h>
|
||||||
|
#include <vlkx/vulkan/Tools.h>
|
||||||
|
#include <vlkx/vulkan/VulkanManager.h>
|
||||||
|
|
||||||
|
GeoBuffers::GeoBuffers() {}
|
||||||
|
GeoBuffers::~GeoBuffers() {}
|
||||||
|
|
||||||
|
void GeoBuffers::createBuffers(Geo::MeshType type) {
|
||||||
|
// Set vertex and indice data
|
||||||
|
switch (type) {
|
||||||
|
case Geo::MeshType::Triangle:
|
||||||
|
Geo::Mesh::setTriData(vertices, indices);
|
||||||
|
break;
|
||||||
|
case Geo::MeshType::Quad:
|
||||||
|
Geo::Mesh::setQuadData(vertices, indices);
|
||||||
|
break;
|
||||||
|
case Geo::MeshType::Cube:
|
||||||
|
Geo::Mesh::setCubeData(vertices, indices);
|
||||||
|
break;
|
||||||
|
case Geo::MeshType::Sphere:
|
||||||
|
Geo::Mesh::setSphereData(vertices, indices);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create buffers
|
||||||
|
createVertexBuffer();
|
||||||
|
createIndexBuffer();
|
||||||
|
createUniformBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeoBuffers::createVertexBuffer() {
|
||||||
|
// Gather the size of the buffer we need..
|
||||||
|
VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size();
|
||||||
|
|
||||||
|
// Create and bind a temporary buffer
|
||||||
|
VkTools::ManagedBuffer staging = VkTools::createGPUBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VulkanManager::getInstance()->getDevice()->logical, VulkanManager::getInstance()->getDevice()->physical);
|
||||||
|
|
||||||
|
// Get ready to write into our GPU buffer
|
||||||
|
void* data;
|
||||||
|
vmaMapMemory(VulkanManager::getInstance()->getAllocator(), staging.allocation, &data);
|
||||||
|
|
||||||
|
// Copy vertex data into the buffer
|
||||||
|
memcpy(data, vertices.data(), (size_t)bufferSize);
|
||||||
|
|
||||||
|
// Unmap the buffer from stack space
|
||||||
|
vmaUnmapMemory(VulkanManager::getInstance()->getAllocator(), staging.allocation);
|
||||||
|
|
||||||
|
// Prepare an area in the GPU that we cannot see, that the vertex data can live permanently.
|
||||||
|
vertexBuffer = VkTools::createGPUBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VulkanManager::getInstance()->getDevice()->logical, VulkanManager::getInstance()->getDevice()->physical);
|
||||||
|
|
||||||
|
// Copy our data from the staging buffer into the new permanent buffer
|
||||||
|
VkTools::copyGPUBuffer(staging.buffer, vertexBuffer.buffer, bufferSize, VulkanManager::getInstance()->getDevice()->logical, VulkanManager::getInstance()->getDevice()->graphicsQueue, VulkanManager::getInstance()->getDevice()->queueData.graphics);
|
||||||
|
|
||||||
|
// Cleanup the staging area.
|
||||||
|
vmaDestroyBuffer(VulkanManager::getInstance()->getAllocator(), staging.buffer, staging.allocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeoBuffers::createIndexBuffer() {
|
||||||
|
// Gather the size of the buffer we need..
|
||||||
|
VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size();
|
||||||
|
|
||||||
|
// Create and bind a temporary buffer
|
||||||
|
VkTools::ManagedBuffer staging = VkTools::createGPUBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VulkanManager::getInstance()->getDevice()->logical, VulkanManager::getInstance()->getDevice()->physical);
|
||||||
|
|
||||||
|
// Get ready to write into our GPU buffer
|
||||||
|
void* data;
|
||||||
|
vmaMapMemory(VulkanManager::getInstance()->getAllocator(), staging.allocation, &data);
|
||||||
|
|
||||||
|
// Copy index data into the buffer
|
||||||
|
memcpy(data, indices.data(), (size_t)bufferSize);
|
||||||
|
|
||||||
|
// Unmap the buffer from stack space
|
||||||
|
vmaUnmapMemory(VulkanManager::getInstance()->getAllocator(), staging.allocation);
|
||||||
|
|
||||||
|
// Prepare an area in the GPU that we cannot see, that the vertex data can live permanently.
|
||||||
|
indexBuffer = VkTools::createGPUBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VulkanManager::getInstance()->getDevice()->logical, VulkanManager::getInstance()->getDevice()->physical);
|
||||||
|
|
||||||
|
// Copy our data from the staging buffer into the new permanent buffer
|
||||||
|
VkTools::copyGPUBuffer(staging.buffer, indexBuffer.buffer, bufferSize, VulkanManager::getInstance()->getDevice()->logical, VulkanManager::getInstance()->getDevice()->graphicsQueue, VulkanManager::getInstance()->getDevice()->queueData.graphics);
|
||||||
|
|
||||||
|
// Cleanup the staging area.
|
||||||
|
vmaDestroyBuffer(VulkanManager::getInstance()->getAllocator(), staging.buffer, staging.allocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeoBuffers::createUniformBuffer() {
|
||||||
|
// Allocate a buffer of the right size - we don't need to copy uniforms.
|
||||||
|
VkDeviceSize bufferSize = sizeof(Geo::UniformBufferObject);
|
||||||
|
|
||||||
|
uniformBuffer = VkTools::createGPUBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VulkanManager::getInstance()->getDevice()->logical, VulkanManager::getInstance()->getDevice()->physical);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeoBuffers::destroy() {
|
||||||
|
// Cleanup uniform buffers
|
||||||
|
vmaDestroyBuffer(VulkanManager::getInstance()->getAllocator(), uniformBuffer.buffer, uniformBuffer.allocation);
|
||||||
|
|
||||||
|
// Cleanup index buffers
|
||||||
|
vmaDestroyBuffer(VulkanManager::getInstance()->getAllocator(), indexBuffer.buffer, indexBuffer.allocation);
|
||||||
|
|
||||||
|
// Cleanup vertex buffers
|
||||||
|
vmaDestroyBuffer(VulkanManager::getInstance()->getAllocator(), vertexBuffer.buffer, vertexBuffer.allocation);
|
||||||
|
}
|
200
projs/shadow/shadow-renderer/src/render/shader/Pipeline.cpp
Normal file
200
projs/shadow/shadow-renderer/src/render/shader/Pipeline.cpp
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
#include <vlkx/render/shader/Pipeline.h>
|
||||||
|
#include <vlkx/vulkan/VulkanManager.h>
|
||||||
|
|
||||||
|
#include <vlkx/render/Geometry.h>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
Pipeline::Pipeline() {}
|
||||||
|
Pipeline::~Pipeline() {}
|
||||||
|
|
||||||
|
void Pipeline::create(VkExtent2D extent, VkDescriptorSetLayout set, VkRenderPass renderPass) {
|
||||||
|
createPipelineLayout(set);
|
||||||
|
createVertexPipeline(extent, renderPass);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pipeline::createPipelineLayout(VkDescriptorSetLayout set) {
|
||||||
|
// Prepare to create the pipeline layout
|
||||||
|
VkPipelineLayoutCreateInfo info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||||
|
info.setLayoutCount = 1;
|
||||||
|
info.pSetLayouts = &set;
|
||||||
|
|
||||||
|
// Create the layout
|
||||||
|
if (vkCreatePipelineLayout(VulkanManager::getInstance()->getDevice()->logical, &info, nullptr, &layout) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to create Pipeline Layout.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> Pipeline::readFile(const std::string& filename) {
|
||||||
|
// Prepare the stream for reading the file
|
||||||
|
std::ifstream file(filename, std::ios::ate | std::ios::binary);
|
||||||
|
|
||||||
|
if (!file.is_open())
|
||||||
|
throw std::runtime_error(std::string("Unable to open shader file: ").append(filename));
|
||||||
|
|
||||||
|
size_t fileSize = (size_t)file.tellg();
|
||||||
|
|
||||||
|
// Read the file into a vector
|
||||||
|
std::vector<char> buffer(fileSize);
|
||||||
|
file.seekg(0);
|
||||||
|
file.read(buffer.data(), fileSize);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkShaderModule Pipeline::createShaderModule(const std::vector<char>& code) {
|
||||||
|
// Prepare to create the shader module
|
||||||
|
VkShaderModuleCreateInfo info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||||
|
info.codeSize = code.size();
|
||||||
|
info.pCode = reinterpret_cast<const uint32_t*>(code.data());
|
||||||
|
|
||||||
|
// Create the module
|
||||||
|
VkShaderModule shaderModule;
|
||||||
|
if (vkCreateShaderModule(VulkanManager::getInstance()->getDevice()->logical, &info, nullptr, &shaderModule) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to create Shader module from SPIR-V binary");
|
||||||
|
|
||||||
|
return shaderModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pipeline::createVertexPipeline(VkExtent2D extent, VkRenderPass renderPass) {
|
||||||
|
|
||||||
|
// ------- Stage: Shader ------- //
|
||||||
|
|
||||||
|
// Read the vertex shader
|
||||||
|
auto vertexShaderCode = readFile("vlkx-resources/shader/SPIRV/basic.vert.spv");
|
||||||
|
VkShaderModule vertexShaderModule = createShaderModule(vertexShaderCode);
|
||||||
|
|
||||||
|
// Prepare to create the Shader Stage
|
||||||
|
VkPipelineShaderStageCreateInfo vertexShaderCreateInfo = {};
|
||||||
|
vertexShaderCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
vertexShaderCreateInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||||
|
vertexShaderCreateInfo.module = vertexShaderModule;
|
||||||
|
vertexShaderCreateInfo.pName = "main";
|
||||||
|
|
||||||
|
|
||||||
|
// Read the fragment shader
|
||||||
|
auto fragmentShaderCode = readFile("vlkx-resources/shader/SPIRV/basic.frag.spv");
|
||||||
|
VkShaderModule fragmentShaderModule = createShaderModule(fragmentShaderCode);
|
||||||
|
|
||||||
|
// Prepare to create the Shader Stage
|
||||||
|
VkPipelineShaderStageCreateInfo fragmentShaderCreateInfo = {};
|
||||||
|
fragmentShaderCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
fragmentShaderCreateInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
|
fragmentShaderCreateInfo.module = fragmentShaderModule;
|
||||||
|
fragmentShaderCreateInfo.pName = "main";
|
||||||
|
|
||||||
|
VkPipelineShaderStageCreateInfo shaderStages[] = {
|
||||||
|
vertexShaderCreateInfo,
|
||||||
|
fragmentShaderCreateInfo
|
||||||
|
};
|
||||||
|
|
||||||
|
// ------- Stage: Shader Input ------- //
|
||||||
|
|
||||||
|
auto bindingDescription = Geo::Vertex::getBindingDesc();
|
||||||
|
auto attributeDescription = Geo::Vertex::getAttributeDesc();
|
||||||
|
|
||||||
|
VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo = {};
|
||||||
|
vertexInputCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||||
|
vertexInputCreateInfo.vertexBindingDescriptionCount = 1;
|
||||||
|
vertexInputCreateInfo.pVertexBindingDescriptions = &bindingDescription;
|
||||||
|
vertexInputCreateInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescription.size());
|
||||||
|
vertexInputCreateInfo.pVertexAttributeDescriptions = attributeDescription.data();
|
||||||
|
|
||||||
|
// ------- Stage: Input Assembly ------- //
|
||||||
|
|
||||||
|
VkPipelineInputAssemblyStateCreateInfo inputAssemblyCreateInfo = {};
|
||||||
|
inputAssemblyCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||||
|
inputAssemblyCreateInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||||
|
inputAssemblyCreateInfo.primitiveRestartEnable = VK_FALSE;
|
||||||
|
|
||||||
|
// ------- Stage: Rasterization ------- //
|
||||||
|
|
||||||
|
VkPipelineRasterizationStateCreateInfo rasterCreateInfo = {};
|
||||||
|
rasterCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||||
|
rasterCreateInfo.depthClampEnable = VK_FALSE;
|
||||||
|
rasterCreateInfo.rasterizerDiscardEnable = VK_FALSE;
|
||||||
|
rasterCreateInfo.polygonMode = VK_POLYGON_MODE_FILL;
|
||||||
|
rasterCreateInfo.lineWidth = 1.0f;
|
||||||
|
rasterCreateInfo.cullMode = VK_CULL_MODE_BACK_BIT;
|
||||||
|
rasterCreateInfo.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
||||||
|
rasterCreateInfo.depthBiasEnable = VK_FALSE;
|
||||||
|
rasterCreateInfo.depthBiasConstantFactor = 0.0f;
|
||||||
|
rasterCreateInfo.depthBiasClamp = 0.0f;
|
||||||
|
rasterCreateInfo.depthBiasSlopeFactor = 0.0f;
|
||||||
|
|
||||||
|
// ------- Stage: MultiSample ------- //
|
||||||
|
|
||||||
|
VkPipelineMultisampleStateCreateInfo multisampleCreateInfo = {};
|
||||||
|
multisampleCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||||
|
multisampleCreateInfo.sampleShadingEnable = VK_FALSE;
|
||||||
|
multisampleCreateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
|
||||||
|
// ------- Stage: Color Blending ------- //
|
||||||
|
|
||||||
|
VkPipelineColorBlendAttachmentState colorBlendAttach = {};
|
||||||
|
colorBlendAttach.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||||
|
colorBlendAttach.blendEnable = VK_FALSE;
|
||||||
|
|
||||||
|
VkPipelineColorBlendStateCreateInfo colorBlendCreateInfo = {};
|
||||||
|
colorBlendCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||||
|
colorBlendCreateInfo.attachmentCount = 1;
|
||||||
|
colorBlendCreateInfo.pAttachments = &colorBlendAttach;
|
||||||
|
|
||||||
|
// ------- Stage: Viewport ------- //
|
||||||
|
|
||||||
|
VkViewport viewport = {};
|
||||||
|
viewport.x = 0;
|
||||||
|
viewport.y = 0;
|
||||||
|
viewport.width = (float)extent.width;
|
||||||
|
viewport.height = (float)extent.height;
|
||||||
|
viewport.minDepth = 0.0f;
|
||||||
|
viewport.maxDepth = 1.0f;
|
||||||
|
|
||||||
|
VkRect2D scissor = {};
|
||||||
|
scissor.offset = { 0, 0 };
|
||||||
|
scissor.extent = extent;
|
||||||
|
|
||||||
|
VkPipelineViewportStateCreateInfo viewportCreateInfo = {};
|
||||||
|
viewportCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||||
|
viewportCreateInfo.viewportCount = 1;
|
||||||
|
viewportCreateInfo.pViewports = &viewport;
|
||||||
|
viewportCreateInfo.scissorCount = 1;
|
||||||
|
viewportCreateInfo.pScissors = &scissor;
|
||||||
|
|
||||||
|
// ------- Create the Pipeline ------- //
|
||||||
|
|
||||||
|
VkGraphicsPipelineCreateInfo pipelineInfo = {};
|
||||||
|
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||||
|
pipelineInfo.stageCount = 2;
|
||||||
|
pipelineInfo.pStages = shaderStages;
|
||||||
|
|
||||||
|
pipelineInfo.pVertexInputState = &vertexInputCreateInfo;
|
||||||
|
pipelineInfo.pInputAssemblyState = &inputAssemblyCreateInfo;
|
||||||
|
pipelineInfo.pRasterizationState = &rasterCreateInfo;
|
||||||
|
pipelineInfo.pMultisampleState = &multisampleCreateInfo;
|
||||||
|
pipelineInfo.pDepthStencilState = nullptr;
|
||||||
|
pipelineInfo.pColorBlendState = &colorBlendCreateInfo;
|
||||||
|
pipelineInfo.pDynamicState = nullptr;
|
||||||
|
pipelineInfo.pViewportState = &viewportCreateInfo;
|
||||||
|
|
||||||
|
pipelineInfo.layout = layout;
|
||||||
|
pipelineInfo.renderPass = renderPass;
|
||||||
|
pipelineInfo.subpass = 0;
|
||||||
|
|
||||||
|
if (vkCreateGraphicsPipelines(VulkanManager::getInstance()->getDevice()->logical, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to create a Vertex rendering Pipeline.");
|
||||||
|
|
||||||
|
|
||||||
|
// ------- Cleanup temporary allocations ------- //
|
||||||
|
|
||||||
|
vkDestroyShaderModule(VulkanManager::getInstance()->getDevice()->logical, vertexShaderModule, nullptr);
|
||||||
|
vkDestroyShaderModule(VulkanManager::getInstance()->getDevice()->logical, fragmentShaderModule, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Pipeline::destroy() {
|
||||||
|
vkDestroyPipeline(VulkanManager::getInstance()->getDevice()->logical, pipeline, nullptr);
|
||||||
|
vkDestroyPipelineLayout(VulkanManager::getInstance()->getDevice()->logical, layout, nullptr);
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
#include <vlkx/render/texture/RenderTexture.h>
|
||||||
|
#include <vlkx/vulkan/VulkanManager.h>
|
||||||
|
#include <vlkx/vulkan/Tools.h>
|
||||||
|
|
||||||
|
SingleRenderTexture::SingleRenderTexture() {}
|
||||||
|
SingleRenderTexture::~SingleRenderTexture() {}
|
||||||
|
|
||||||
|
void SingleRenderTexture::createViewsAndFramebuffer(std::vector<VkImage> images, VkFormat format, VkExtent2D extent, VkRenderPass pass) {
|
||||||
|
// Initialize members
|
||||||
|
this->swapChainImages = images;
|
||||||
|
this->swapChainImageExtent = extent;
|
||||||
|
|
||||||
|
createViews(format);
|
||||||
|
createFramebuffer(extent, pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleRenderTexture::createViews(VkFormat format) {
|
||||||
|
// Prepare maximum size
|
||||||
|
swapChainImageViews.resize(swapChainImages.size());
|
||||||
|
|
||||||
|
// Iterate images and create views for each.
|
||||||
|
for (size_t i = 0; i < swapChainImages.size(); i++) {
|
||||||
|
swapChainImageViews[i] = VkTools::createImageView(swapChainImages[i], format, VK_IMAGE_ASPECT_COLOR_BIT, VulkanManager::getInstance()->getDevice()->logical);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleRenderTexture::createFramebuffer(VkExtent2D extent, VkRenderPass pass) {
|
||||||
|
// Prepare maximum size
|
||||||
|
swapChainFramebuffers.resize(swapChainImageViews.size());
|
||||||
|
|
||||||
|
// Iterate views and create a framebuffer for each.
|
||||||
|
for (size_t i = 0; i < swapChainImageViews.size(); i++) {
|
||||||
|
// Create an array with the image view as an attachment.
|
||||||
|
std::array<VkImageView, 1> attachments = {
|
||||||
|
swapChainImageViews[i]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prepare the creation of a new framebuffer.
|
||||||
|
// One attachment, width and height from the extent, and one layer (one viewport)
|
||||||
|
VkFramebufferCreateInfo info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||||
|
info.renderPass = pass;
|
||||||
|
info.attachmentCount = static_cast<uint32_t>(attachments.size());
|
||||||
|
info.pAttachments = attachments.data();
|
||||||
|
info.width = extent.width;
|
||||||
|
info.height = extent.height;
|
||||||
|
info.layers = 1;
|
||||||
|
|
||||||
|
// Create the framebuffer
|
||||||
|
if (vkCreateFramebuffer(VulkanManager::getInstance()->getDevice()->logical, &info, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to create framebuffer for a texture.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleRenderTexture::destroy() {
|
||||||
|
// Destroy Image Views first
|
||||||
|
for (auto imageView : swapChainImageViews) {
|
||||||
|
vkDestroyImageView(VulkanManager::getInstance()->getDevice()->logical, imageView, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Framebuffers
|
||||||
|
for (auto framebuffer : swapChainFramebuffers) {
|
||||||
|
vkDestroyFramebuffer(VulkanManager::getInstance()->getDevice()->logical, framebuffer, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
60
projs/shadow/shadow-renderer/src/vulkan/CommandBuffer.cpp
Normal file
60
projs/shadow/shadow-renderer/src/vulkan/CommandBuffer.cpp
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#include <vlkx/vulkan/CommandBuffer.h>
|
||||||
|
#include <vlkx/vulkan/VulkanManager.h>
|
||||||
|
|
||||||
|
CommandBuffer::CommandBuffer() {}
|
||||||
|
CommandBuffer::~CommandBuffer() {}
|
||||||
|
|
||||||
|
void CommandBuffer::createCommandPoolAndBuffers(size_t size) {
|
||||||
|
createCommandPool();
|
||||||
|
allocateCommandBuffers(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBuffer::createCommandPool() {
|
||||||
|
// Prepare queues
|
||||||
|
QueueFamilies families = VulkanManager::getInstance()->getDevice()->getQueues();
|
||||||
|
|
||||||
|
// Prepare command pool creation data
|
||||||
|
VkCommandPoolCreateInfo info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||||
|
info.queueFamilyIndex = families.graphics;
|
||||||
|
info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||||
|
|
||||||
|
// Attempt creation
|
||||||
|
if (vkCreateCommandPool(VulkanManager::getInstance()->getDevice()->logical, &info, nullptr, &commands) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to create a command pool.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBuffer::allocateCommandBuffers(size_t size) {
|
||||||
|
// Prepare allocation info
|
||||||
|
buffers.resize(size);
|
||||||
|
|
||||||
|
VkCommandBufferAllocateInfo info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||||
|
info.commandPool = commands;
|
||||||
|
info.commandBufferCount = (uint32_t)buffers.size();
|
||||||
|
|
||||||
|
// Attempt allocation
|
||||||
|
if (vkAllocateCommandBuffers(VulkanManager::getInstance()->getDevice()->logical, &info, buffers.data()) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to allocate command buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBuffer::beginCommandBuffer(VkCommandBuffer buffer) {
|
||||||
|
// Prepare to begin listening on this buffer
|
||||||
|
VkCommandBufferBeginInfo info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||||
|
info.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; // Allow writing to this buffer while another is being read.
|
||||||
|
|
||||||
|
// Attempt to begin listening
|
||||||
|
if (vkBeginCommandBuffer(buffer, &info) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to begin a command buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBuffer::endCommandBuffer(VkCommandBuffer buffer) {
|
||||||
|
// Attempt to end listening
|
||||||
|
if (vkEndCommandBuffer(buffer) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to end listening for a command buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBuffer::destroy() {
|
||||||
|
vkDestroyCommandPool(VulkanManager::getInstance()->getDevice()->logical, commands, nullptr);
|
||||||
|
}
|
103
projs/shadow/shadow-renderer/src/vulkan/SwapChain.cpp
Normal file
103
projs/shadow/shadow-renderer/src/vulkan/SwapChain.cpp
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
#include <vlkx/vulkan/SwapChain.h>
|
||||||
|
#include <vlkx/vulkan/VulkanManager.h>
|
||||||
|
|
||||||
|
SwapChain::SwapChain() {}
|
||||||
|
SwapChain::~SwapChain() {}
|
||||||
|
|
||||||
|
VkSurfaceFormatKHR SwapChain::chooseFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {
|
||||||
|
// Check if we don't have any options
|
||||||
|
if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED)
|
||||||
|
// Default to BGRA, sRGB
|
||||||
|
return { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
|
||||||
|
|
||||||
|
for (const auto& format : availableFormats) {
|
||||||
|
// Prefer BGRA sRGB if it's available
|
||||||
|
if (format.format == VK_FORMAT_B8G8R8A8_UNORM && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If BGRA sRGB isn't an option, choose whatever Vulkan thinks is the best option.
|
||||||
|
return availableFormats[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPresentModeKHR SwapChain::chooseMode(const std::vector<VkPresentModeKHR>& availableModes) {
|
||||||
|
|
||||||
|
// We want Mailbox, Immediate or FIFO, in that order.
|
||||||
|
VkPresentModeKHR mode = availableModes[0];
|
||||||
|
if (mode == VK_PRESENT_MODE_MAILBOX_KHR || mode == VK_PRESENT_MODE_IMMEDIATE_KHR)
|
||||||
|
return mode;
|
||||||
|
|
||||||
|
return VK_PRESENT_MODE_FIFO_KHR;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkExtent2D SwapChain::chooseExtent(const VkSurfaceCapabilitiesKHR& capabilities) {
|
||||||
|
// If our extent is valid, use it
|
||||||
|
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max())
|
||||||
|
return capabilities.currentExtent;
|
||||||
|
else {
|
||||||
|
// Create a new 1280x720 extent and use that
|
||||||
|
VkExtent2D size = { 1280, 720 };
|
||||||
|
size.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, size.width));
|
||||||
|
size.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, size.height));
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapChain::create(VkSurfaceKHR surface) {
|
||||||
|
SwapChainMeta info = VulkanManager::getInstance()->getDevice()->swapChain;
|
||||||
|
|
||||||
|
VkSurfaceFormatKHR chosenFormat = chooseFormat(info.formats);
|
||||||
|
VkPresentModeKHR chosenMode = chooseMode(info.modes);
|
||||||
|
VkExtent2D chosenExtent = chooseExtent(info.capabilities);
|
||||||
|
|
||||||
|
// use the max if it's set, otherwise the minimum
|
||||||
|
uint32_t imageCount = std::max(info.capabilities.minImageCount, (uint32_t) 2);
|
||||||
|
std::cout << "SwapChain has " << imageCount << " images." << std::endl;
|
||||||
|
|
||||||
|
// Prepare the creation data
|
||||||
|
VkSwapchainCreateInfoKHR createInfo = {};
|
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||||
|
createInfo.surface = surface;
|
||||||
|
createInfo.minImageCount = imageCount;
|
||||||
|
createInfo.imageFormat = chosenFormat.format;
|
||||||
|
createInfo.imageColorSpace = chosenFormat.colorSpace;
|
||||||
|
createInfo.imageExtent = chosenExtent;
|
||||||
|
createInfo.imageArrayLayers = 1; // 2 for VR
|
||||||
|
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||||
|
|
||||||
|
QueueFamilies queues = VulkanManager::getInstance()->getDevice()->getQueues();
|
||||||
|
uint32_t indices[] = { static_cast<uint32_t>(queues.graphics), static_cast<uint32_t>(queues.presentation) };
|
||||||
|
|
||||||
|
if (queues.graphics != queues.presentation) {
|
||||||
|
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||||
|
createInfo.queueFamilyIndexCount = 2;
|
||||||
|
createInfo.pQueueFamilyIndices = indices;
|
||||||
|
} else {
|
||||||
|
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
createInfo.queueFamilyIndexCount = 0;
|
||||||
|
createInfo.pQueueFamilyIndices = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
createInfo.preTransform = info.capabilities.currentTransform;
|
||||||
|
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||||
|
createInfo.presentMode = chosenMode;
|
||||||
|
createInfo.clipped = VK_TRUE;
|
||||||
|
createInfo.oldSwapchain = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
// Create the swap-chain
|
||||||
|
if (vkCreateSwapchainKHR(VulkanManager::getInstance()->getDevice()->logical, &createInfo, nullptr, &swapChain))
|
||||||
|
throw std::runtime_error("Failed to create swap-chain");
|
||||||
|
|
||||||
|
// Fetch our images from the swapchain
|
||||||
|
vkGetSwapchainImagesKHR(VulkanManager::getInstance()->getDevice()->logical, swapChain, &imageCount, nullptr);
|
||||||
|
images.resize(imageCount);
|
||||||
|
vkGetSwapchainImagesKHR(VulkanManager::getInstance()->getDevice()->logical, swapChain, &imageCount, images.data());
|
||||||
|
|
||||||
|
// Set gloabls
|
||||||
|
format = chosenFormat.format;
|
||||||
|
extent = chosenExtent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapChain::destroy() {
|
||||||
|
vkDestroySwapchainKHR(VulkanManager::getInstance()->getDevice()->logical, swapChain, nullptr);
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
#include <vlkx/vulkan/ValidationAndExtension.h>
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <SDL_vulkan.h>
|
||||||
|
|
||||||
|
ValidationAndExtension::ValidationAndExtension() {}
|
||||||
|
ValidationAndExtension::~ValidationAndExtension() {}
|
||||||
|
|
||||||
|
bool ValidationAndExtension::checkValidationSupport() {
|
||||||
|
// Get number of properties
|
||||||
|
uint32_t layers;
|
||||||
|
vkEnumerateInstanceLayerProperties(&layers, nullptr);
|
||||||
|
|
||||||
|
// Instantiate a vector of proper size and retrieve data
|
||||||
|
std::vector<VkLayerProperties> availableProperties(layers);
|
||||||
|
vkEnumerateInstanceLayerProperties(&layers, availableProperties.data());
|
||||||
|
|
||||||
|
// Iterate the validation layers we require
|
||||||
|
for (const char* layer : requiredValidations) {
|
||||||
|
bool layerFound = false;
|
||||||
|
|
||||||
|
// Search for a match with the ones we have
|
||||||
|
for (const auto& property : availableProperties) {
|
||||||
|
if (strcmp(layer, property.layerName) == 0) {
|
||||||
|
layerFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any are not found, then we don't support what we need
|
||||||
|
if (!layerFound)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all are found, then we can continue
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const char*> ValidationAndExtension::getRequiredExtensions(SDL_Window* window, bool validationsRequired) {
|
||||||
|
|
||||||
|
unsigned int count;
|
||||||
|
SDL_Vulkan_GetInstanceExtensions(window, &count, nullptr);
|
||||||
|
|
||||||
|
std::vector<const char*> extensions = {
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t additional_extension_count = extensions.size();
|
||||||
|
extensions.resize(additional_extension_count + count);
|
||||||
|
|
||||||
|
SDL_Vulkan_GetInstanceExtensions(window, &count, extensions.data() + additional_extension_count);
|
||||||
|
|
||||||
|
if (validationsRequired) {
|
||||||
|
extensions.push_back("VK_EXT_debug_report"); // Add debug report if we want to validate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the new list
|
||||||
|
return extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
|
||||||
|
VkDebugReportFlagsEXT flags,
|
||||||
|
VkDebugReportObjectTypeEXT objExt,
|
||||||
|
size_t obj,
|
||||||
|
size_t location,
|
||||||
|
int32_t code,
|
||||||
|
const char* layer,
|
||||||
|
const char* message,
|
||||||
|
void* user) {
|
||||||
|
|
||||||
|
std::cerr << "Validation from layer " << layer << ": " << message << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValidationAndExtension::setupDebugCallback(bool validationRequired, VkInstance vulkan) {
|
||||||
|
if (!validationRequired)
|
||||||
|
return;
|
||||||
|
|
||||||
|
VkDebugReportCallbackCreateInfoEXT info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
|
||||||
|
info.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
|
||||||
|
info.pfnCallback = debugCallback;
|
||||||
|
|
||||||
|
if (createDebugReportCallbackEXT(vulkan, &info, nullptr, &callback) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create log dumper.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValidationAndExtension::destroy(bool validationRequired, VkInstance vulkan) {
|
||||||
|
if (validationRequired)
|
||||||
|
destroyDebugReportCallbackEXT(vulkan, callback, nullptr);
|
||||||
|
}
|
191
projs/shadow/shadow-renderer/src/vulkan/VulkanDevice.cpp
Normal file
191
projs/shadow/shadow-renderer/src/vulkan/VulkanDevice.cpp
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
#include <vlkx/vulkan/VulkanDevice.h>
|
||||||
|
|
||||||
|
VulkanDevice::VulkanDevice() : physical(VK_NULL_HANDLE), logical(VK_NULL_HANDLE), graphicsQueue(VK_NULL_HANDLE), presentationQueue(VK_NULL_HANDLE) {}
|
||||||
|
VulkanDevice::~VulkanDevice() = default;
|
||||||
|
|
||||||
|
void VulkanDevice::choosePhysicalDevice(VkInstance* vulkan, VkSurfaceKHR surface) {
|
||||||
|
// Count devices
|
||||||
|
uint32_t deviceCount = 0;
|
||||||
|
vkEnumeratePhysicalDevices(*vulkan, &deviceCount, nullptr);
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (deviceCount == 0) {
|
||||||
|
throw std::runtime_error("Vulkan not supported on this system. No Devices available");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Found " << deviceCount << " devices that are Vulkan-compatible." << std::endl;
|
||||||
|
|
||||||
|
// Gather devices
|
||||||
|
std::vector<VkPhysicalDevice> physicals(deviceCount);
|
||||||
|
vkEnumeratePhysicalDevices(*vulkan, &deviceCount, physicals.data());
|
||||||
|
|
||||||
|
// Enumerate devices
|
||||||
|
std::string finalDeviceName;
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "Device List" << std::endl;
|
||||||
|
for (const auto& device : physicals) {
|
||||||
|
VkPhysicalDeviceProperties props;
|
||||||
|
vkGetPhysicalDeviceProperties(device, &props);
|
||||||
|
|
||||||
|
std::cout << "Device: " << props.deviceName << std::endl;
|
||||||
|
if (physical == VK_NULL_HANDLE && isSuitable(device, surface) ) {
|
||||||
|
finalDeviceName = props.deviceName;
|
||||||
|
physical = device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << std::endl << "Using device " << finalDeviceName << "." << std::endl;
|
||||||
|
|
||||||
|
// Sanity check that at least one was found.
|
||||||
|
if (physical == VK_NULL_HANDLE)
|
||||||
|
throw std::runtime_error("No suitable GPU found");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanDevice::isSuitable(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||||
|
// Find queues
|
||||||
|
|
||||||
|
QueueFamilies families = checkQueues(device, surface);
|
||||||
|
|
||||||
|
bool supported = isSupported(device);
|
||||||
|
bool swapChainWorks = false;
|
||||||
|
|
||||||
|
if (supported) {
|
||||||
|
swapChain = checkSwapchain(device, surface);
|
||||||
|
swapChainWorks = !swapChain.formats.empty() && !swapChain.modes.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPhysicalDeviceFeatures supportedFeatures;
|
||||||
|
vkGetPhysicalDeviceFeatures(device, &supportedFeatures);
|
||||||
|
|
||||||
|
return families.present() && supported && swapChainWorks && supportedFeatures.samplerAnisotropy;
|
||||||
|
}
|
||||||
|
|
||||||
|
QueueFamilies VulkanDevice::checkQueues(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||||
|
QueueFamilies families;
|
||||||
|
// Enumerate queueues
|
||||||
|
uint32_t queueCount = 0;
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueCount, nullptr);
|
||||||
|
|
||||||
|
std::vector<VkQueueFamilyProperties> queues(queueCount);
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueCount, queues.data());
|
||||||
|
|
||||||
|
// Find a valid graphics (drawing) and presentation (display) queue
|
||||||
|
int i = 0;
|
||||||
|
for (const auto& family : queues) {
|
||||||
|
// If the graphics bit is set, this is a valid graphics queue
|
||||||
|
if (family.queueCount > 0 && family.queueFlags & VK_QUEUE_GRAPHICS_BIT)
|
||||||
|
families.graphics = i;
|
||||||
|
|
||||||
|
// Ask Vulkan if this family suppots displaying to the surface from this device
|
||||||
|
VkBool32 presentationSupport = false;
|
||||||
|
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentationSupport);
|
||||||
|
if (family.queueCount > 0 && presentationSupport)
|
||||||
|
families.presentation = i;
|
||||||
|
|
||||||
|
// If we have a valid graphics and presentation queue, we can stop searching
|
||||||
|
if (families.present())
|
||||||
|
break;
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return families;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanDevice::isSupported(VkPhysicalDevice device) {
|
||||||
|
// Enumerate extensions
|
||||||
|
uint32_t extensionCount = 0;
|
||||||
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
|
||||||
|
|
||||||
|
std::vector<VkExtensionProperties> extensions(extensionCount);
|
||||||
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, extensions.data());
|
||||||
|
|
||||||
|
// Filter for the ones we have
|
||||||
|
std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
|
||||||
|
for (const auto& extension : extensions) {
|
||||||
|
// Remove the ones we have from the ones we need
|
||||||
|
requiredExtensions.erase(extension.extensionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have every needed extension, then we're good to go
|
||||||
|
return requiredExtensions.empty();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SwapChainMeta VulkanDevice::checkSwapchain(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||||
|
SwapChainMeta meta;
|
||||||
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &meta.capabilities);
|
||||||
|
|
||||||
|
// Check display formats (ARGB, RBGA, etc)
|
||||||
|
uint32_t formatCount = 0;
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
|
||||||
|
|
||||||
|
if (formatCount != 0) {
|
||||||
|
meta.formats.resize(formatCount);
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, meta.formats.data());
|
||||||
|
}
|
||||||
|
// Check Presentation modes (bit depth, etc)
|
||||||
|
uint32_t modeCount = 0;
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &modeCount, nullptr);
|
||||||
|
|
||||||
|
if (modeCount != 0) {
|
||||||
|
meta.modes.resize(modeCount);
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &modeCount, meta.modes.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanDevice::createLogicalDevice(VkSurfaceKHR surface, bool validationRequired, ValidationAndExtension* validator) {
|
||||||
|
|
||||||
|
// Get the queue data, prepare it for the logical device
|
||||||
|
QueueFamilies families = checkQueues(physical, surface);
|
||||||
|
|
||||||
|
std::vector<VkDeviceQueueCreateInfo> queueCreation;
|
||||||
|
std::set<int> queues = { families.graphics, families.presentation };
|
||||||
|
|
||||||
|
float priority = 1;
|
||||||
|
for (int family : queues) {
|
||||||
|
VkDeviceQueueCreateInfo createInfo = {};
|
||||||
|
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
|
createInfo.queueFamilyIndex = family;
|
||||||
|
createInfo.queueCount = 1;
|
||||||
|
createInfo.pQueuePriorities = &priority;
|
||||||
|
queueCreation.push_back(createInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the characteristics of the device we want
|
||||||
|
VkPhysicalDeviceFeatures features = {};
|
||||||
|
features.samplerAnisotropy = VK_TRUE;
|
||||||
|
|
||||||
|
// Prepare the device for construction
|
||||||
|
VkDeviceCreateInfo device = {};
|
||||||
|
device.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||||
|
device.pQueueCreateInfos = queueCreation.data();
|
||||||
|
device.queueCreateInfoCount = static_cast<uint32_t>(queueCreation.size());
|
||||||
|
device.pEnabledFeatures = &features;
|
||||||
|
device.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
|
||||||
|
device.ppEnabledExtensionNames = deviceExtensions.data();
|
||||||
|
|
||||||
|
if (validationRequired) {
|
||||||
|
device.enabledLayerCount = static_cast<uint32_t>(validator->requiredValidations.size());
|
||||||
|
device.ppEnabledLayerNames = validator->requiredValidations.data();
|
||||||
|
} else {
|
||||||
|
device.enabledLayerCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the device
|
||||||
|
|
||||||
|
if (vkCreateDevice(physical, &device, nullptr, &logical) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create logical device");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the proper queue metadata from the GPU
|
||||||
|
queueData = families;
|
||||||
|
vkGetDeviceQueue(logical, families.graphics, 0, &graphicsQueue);
|
||||||
|
vkGetDeviceQueue(logical, families.presentation, 0, &presentationQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanDevice::destroy() {
|
||||||
|
vkDestroyDevice(logical, nullptr);
|
||||||
|
}
|
227
projs/shadow/shadow-renderer/src/vulkan/VulkanManager.cpp
Normal file
227
projs/shadow/shadow-renderer/src/vulkan/VulkanManager.cpp
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
#define VMA_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include <vulkan/vk_mem_alloc.h>
|
||||||
|
|
||||||
|
#define VKTOOLS_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include <vlkx/vulkan/Tools.h>
|
||||||
|
|
||||||
|
#include <vlkx\vulkan\VulkanManager.h>
|
||||||
|
|
||||||
|
VulkanManager::VulkanManager() { rayTraceMode = false; }
|
||||||
|
|
||||||
|
VulkanManager::~VulkanManager() = default;
|
||||||
|
|
||||||
|
VulkanManager* VulkanManager::instance = nullptr;
|
||||||
|
|
||||||
|
|
||||||
|
VmaAllocator VkTools::g_allocator;
|
||||||
|
VkInstance VkTools::g_Instance = VK_NULL_HANDLE;
|
||||||
|
VkPhysicalDevice VkTools::g_PhysicalDevice = VK_NULL_HANDLE;
|
||||||
|
VkDevice VkTools::g_Device = VK_NULL_HANDLE;
|
||||||
|
uint32_t VkTools::g_QueueFamily = (uint32_t)-1;
|
||||||
|
VkQueue VkTools::g_Queue = VK_NULL_HANDLE;
|
||||||
|
VkDebugReportCallbackEXT VkTools::g_DebugReport = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
VulkanManager* VulkanManager::getInstance() {
|
||||||
|
return VulkanManager::instance != nullptr ? VulkanManager::instance
|
||||||
|
: (VulkanManager::instance = new VulkanManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanManager::createAppAndVulkanInstance(bool enableValidation, ValidationAndExtension* validations) {
|
||||||
|
VkApplicationInfo info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||||
|
info.pApplicationName = "Sup";
|
||||||
|
info.applicationVersion = VK_MAKE_API_VERSION(0, 1, 0, 0);
|
||||||
|
info.pEngineName = "Infinity Drive";
|
||||||
|
info.engineVersion = VK_MAKE_API_VERSION(0, 1, 0, 0);
|
||||||
|
info.apiVersion = VK_API_VERSION_1_0;
|
||||||
|
|
||||||
|
VkInstanceCreateInfo instanceInfo = {};
|
||||||
|
instanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||||
|
instanceInfo.pApplicationInfo = &info;
|
||||||
|
|
||||||
|
auto extensions = validations->getRequiredExtensions(wnd, true);
|
||||||
|
instanceInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
|
||||||
|
instanceInfo.ppEnabledExtensionNames = &extensions[0];
|
||||||
|
|
||||||
|
auto layers = validations->requiredValidations;
|
||||||
|
if (enableValidation) {
|
||||||
|
instanceInfo.enabledLayerCount = static_cast<uint32_t>(layers.size());
|
||||||
|
instanceInfo.ppEnabledLayerNames = &layers[0];
|
||||||
|
} else {
|
||||||
|
instanceInfo.enabledLayerCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto status = vkCreateInstance(&instanceInfo, nullptr, &vulkan);
|
||||||
|
if (status != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to initialize Vulkan: " + std::to_string(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanManager::initVulkan(SDL_Window* window) {
|
||||||
|
wnd = window;
|
||||||
|
validators = new ValidationAndExtension();
|
||||||
|
|
||||||
|
if (!validators->checkValidationSupport())
|
||||||
|
throw std::runtime_error("Validation not available");
|
||||||
|
|
||||||
|
createAppAndVulkanInstance(validationRequired, validators);
|
||||||
|
|
||||||
|
validators->setupDebugCallback(validationRequired, vulkan);
|
||||||
|
|
||||||
|
if (SDL_Vulkan_CreateSurface(window, vulkan, &surface) != SDL_TRUE)
|
||||||
|
throw std::runtime_error("Unable to create Vulkan Surface");
|
||||||
|
|
||||||
|
this->device = new VulkanDevice();
|
||||||
|
this->device->choosePhysicalDevice(&vulkan, surface);
|
||||||
|
this->device->createLogicalDevice(surface, validationRequired, validators);
|
||||||
|
|
||||||
|
VmaAllocatorCreateInfo allocatorInfo = {};
|
||||||
|
allocatorInfo.physicalDevice = this->device->physical;
|
||||||
|
allocatorInfo.device = this->device->logical;
|
||||||
|
allocatorInfo.instance = this->vulkan;
|
||||||
|
vmaCreateAllocator(&allocatorInfo, &this->allocator);
|
||||||
|
VkTools::g_allocator = this->allocator;
|
||||||
|
VkTools::g_PhysicalDevice = this->device->physical;
|
||||||
|
VkTools::g_Device = this->device->logical;
|
||||||
|
VkTools::g_Instance = this->vulkan;
|
||||||
|
|
||||||
|
this->swapchain = new SwapChain();
|
||||||
|
this->swapchain->create(surface);
|
||||||
|
|
||||||
|
this->renderPass = new RenderPass();
|
||||||
|
|
||||||
|
// Set up for vertex rendering
|
||||||
|
this->renderPass->createVertexRenderPass(swapchain->format);
|
||||||
|
this->renderTexture = new SingleRenderTexture();
|
||||||
|
|
||||||
|
this->renderTexture->createViewsAndFramebuffer(swapchain->images, swapchain->format,
|
||||||
|
swapchain->extent,renderPass->pass
|
||||||
|
);
|
||||||
|
|
||||||
|
this->buffers = new CommandBuffer();
|
||||||
|
this->buffers->createCommandPoolAndBuffers(swapchain->images.size());
|
||||||
|
|
||||||
|
// Create semaphores for render events
|
||||||
|
VkSemaphoreCreateInfo semaphoreInfo = {};
|
||||||
|
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||||
|
vkCreateSemaphore(device->logical, &semaphoreInfo, nullptr, &newImageSem);
|
||||||
|
vkCreateSemaphore(device->logical, &semaphoreInfo, nullptr, &renderDoneSem);
|
||||||
|
|
||||||
|
// Create fences for the frames
|
||||||
|
inFlight.resize(MAX_FRAMES);
|
||||||
|
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; i++) {
|
||||||
|
if (vkCreateFence(device->logical, &fenceInfo, nullptr, &inFlight[i]) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("Unable to create fence for a frame");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Vulkan Initialization Finished" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanManager::startDraw() {
|
||||||
|
// Prepare for a new frame to start
|
||||||
|
vkAcquireNextImageKHR(device->logical, swapchain->swapChain,
|
||||||
|
std::numeric_limits<uint64_t>::max(), newImageSem,VK_NULL_HANDLE, &imageIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
vkWaitForFences(device->logical, 1, &inFlight[imageIndex], VK_TRUE,
|
||||||
|
std::numeric_limits<uint64_t>::max()
|
||||||
|
);
|
||||||
|
|
||||||
|
vkResetFences(device->logical, 1, &inFlight[imageIndex]);
|
||||||
|
|
||||||
|
// Fetch the next command buffer
|
||||||
|
currentCommandBuffer = buffers->buffers[imageIndex];
|
||||||
|
buffers->beginCommandBuffer(currentCommandBuffer);
|
||||||
|
|
||||||
|
// Setup render pass; setup clear color
|
||||||
|
VkClearValue clearColor = { 1.0f, 0.0f, 0.0f, 1.0f }; // Red
|
||||||
|
|
||||||
|
// Execute render pass
|
||||||
|
renderPass->beginRenderPass({ clearColor }, currentCommandBuffer, dynamic_cast<SingleRenderTexture*>(renderTexture)->swapChainFramebuffers[imageIndex], dynamic_cast<SingleRenderTexture*>(renderTexture)->swapChainImageExtent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanManager::endDraw() {
|
||||||
|
// End command buffer first
|
||||||
|
renderPass->endRenderPass(currentCommandBuffer);
|
||||||
|
buffers->endCommandBuffer(currentCommandBuffer);
|
||||||
|
|
||||||
|
// Prepare to submit all draw commands to the GPU
|
||||||
|
VkPipelineStageFlags waitStages[] = {
|
||||||
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
|
||||||
|
};
|
||||||
|
|
||||||
|
VkSubmitInfo submitInfo = {};
|
||||||
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||||
|
submitInfo.commandBufferCount = 1;
|
||||||
|
submitInfo.pCommandBuffers = ¤tCommandBuffer;
|
||||||
|
submitInfo.pWaitDstStageMask = waitStages;
|
||||||
|
// Wait for the New Image semaphore, and signal the Render Done semaphore when finished
|
||||||
|
submitInfo.waitSemaphoreCount = 1;
|
||||||
|
submitInfo.pWaitSemaphores = &newImageSem;
|
||||||
|
submitInfo.signalSemaphoreCount = 1;
|
||||||
|
submitInfo.pSignalSemaphores = &renderDoneSem;
|
||||||
|
|
||||||
|
// Submit.
|
||||||
|
vkQueueSubmit(VulkanManager::getInstance()->getDevice()->graphicsQueue, 1, &submitInfo, inFlight[imageIndex]);
|
||||||
|
|
||||||
|
// Prepare to show the drawn frame on the screen.
|
||||||
|
VkPresentInfoKHR presentInfo = {};
|
||||||
|
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||||
|
presentInfo.swapchainCount = 1;
|
||||||
|
presentInfo.pSwapchains = &swapchain->swapChain;
|
||||||
|
presentInfo.pImageIndices = &imageIndex;
|
||||||
|
// Wait until render is finished before presenting.
|
||||||
|
presentInfo.waitSemaphoreCount = 1;
|
||||||
|
presentInfo.pWaitSemaphores = &renderDoneSem;
|
||||||
|
|
||||||
|
// Show.
|
||||||
|
vkQueuePresentKHR(VulkanManager::getInstance()->getDevice()->presentationQueue, &presentInfo);
|
||||||
|
|
||||||
|
// Wait for the GPU to catch up
|
||||||
|
vkQueueWaitIdle(VulkanManager::getInstance()->getDevice()->presentationQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanManager::cleanup() {
|
||||||
|
// Wait for the GPU to not be busy
|
||||||
|
vkDeviceWaitIdle(VulkanManager::getInstance()->getDevice()->logical);
|
||||||
|
|
||||||
|
// Destroy our own data
|
||||||
|
vkDestroySemaphore(device->logical, renderDoneSem, nullptr);
|
||||||
|
vkDestroySemaphore(device->logical, newImageSem, nullptr);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < MAX_FRAMES; i++) {
|
||||||
|
vkDestroyFence(device->logical, inFlight[i], nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffers->destroy();
|
||||||
|
renderTexture->destroy();
|
||||||
|
renderPass->destroy();
|
||||||
|
swapchain->destroy();
|
||||||
|
|
||||||
|
// Destroy the Vulkan Device
|
||||||
|
VulkanManager::getInstance()->getDevice()->destroy();
|
||||||
|
|
||||||
|
// Delete the layer validators.
|
||||||
|
validators->destroy(validationRequired, vulkan);
|
||||||
|
|
||||||
|
// Delete the surface and Vulkan instance.
|
||||||
|
vkDestroySurfaceKHR(vulkan, surface, nullptr);
|
||||||
|
vkDestroyInstance(vulkan, nullptr);
|
||||||
|
|
||||||
|
vmaDestroyAllocator(allocator);
|
||||||
|
|
||||||
|
// Delete allocated memory for our own data.
|
||||||
|
delete buffers;
|
||||||
|
delete renderTexture;
|
||||||
|
delete renderPass;
|
||||||
|
delete swapchain;
|
||||||
|
delete device;
|
||||||
|
delete validators;
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
find_package(SDL2 REQUIRED)
|
find_package(SDL2 REQUIRED)
|
||||||
|
|
||||||
add_executable(shadow-runtime src/main.cpp)
|
FILE(GLOB_RECURSE SOURCES src/*.cpp src/*.h)
|
||||||
|
|
||||||
|
add_executable(shadow-runtime ${SOURCES})
|
||||||
|
|
||||||
target_include_directories(shadow-runtime PRIVATE ${SDL2_INCLUDE_DIRS})
|
target_include_directories(shadow-runtime PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||||
target_link_libraries(shadow-runtime PRIVATE SDL2::Main shadow-engine)
|
target_link_libraries(shadow-runtime PRIVATE SDL2::Main shadow-engine)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user