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)
|
||||
|
||||
# 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
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
||||
|
||||
|
@ -36,5 +50,8 @@ add_subdirectory(projs/shadow/shadow-reflection)
|
|||
# Core engine
|
||||
add_subdirectory(projs/shadow/shadow-engine)
|
||||
|
||||
# Renderer
|
||||
add_subdirectory(projs/shadow/shadow-renderer)
|
||||
|
||||
# Runtime executable
|
||||
add_subdirectory(projs/shadow/shadow-runtime)
|
|
@ -9,4 +9,4 @@ FILE(GLOB_RECURSE HEADERS src/*.h)
|
|||
add_library(shadow-engine ${SOURCES})
|
||||
|
||||
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 "Time.h"
|
||||
#include <vlkx/vulkan/VulkanManager.h>
|
||||
#include <vlkx/render/Camera.h>
|
||||
#include <vlkx/render/geometry/SingleRenderer.h>
|
||||
|
||||
namespace ShadowEngine {
|
||||
|
||||
|
@ -46,6 +49,8 @@ namespace ShadowEngine {
|
|||
|
||||
window_ = new ShadowWindow(800,450);
|
||||
|
||||
VulkanManager::getInstance()->initVulkan(window_->sdlWindowPtr);
|
||||
|
||||
/*
|
||||
moduleManager.PushModule(new Log());
|
||||
moduleManager.PushModule(new EventSystem::ShadowEventManager());
|
||||
|
@ -70,6 +75,15 @@ namespace ShadowEngine {
|
|||
|
||||
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;
|
||||
while (running)
|
||||
{
|
||||
|
@ -78,6 +92,16 @@ namespace ShadowEngine {
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
ShadowWindow::ShadowWindow(int W, int H) : Height(H), Width(W)
|
||||
{
|
||||
// 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
|
||||
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;
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
|
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)
|
||||
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_link_libraries(shadow-runtime PRIVATE SDL2::Main shadow-engine)
|
||||
|
|
Loading…
Reference in New Issue
Block a user