Replace the renderer with a modern, module version. (#7)
* New Vulkan Renderer Module & Associated Fixups
This commit is contained in:
parent
71b95e1ccf
commit
a3e89b4e8f
41
imgui.ini
Normal file
41
imgui.ini
Normal file
|
@ -0,0 +1,41 @@
|
|||
[Window][Debug##Default]
|
||||
Pos=876,8
|
||||
Size=315,195
|
||||
Collapsed=0
|
||||
DockId=0x00000008,0
|
||||
|
||||
[Window][Dear ImGui Demo]
|
||||
Pos=1193,178
|
||||
Size=62,25
|
||||
Collapsed=0
|
||||
DockId=0x00000007,0
|
||||
|
||||
[Window][Game module window]
|
||||
Pos=1193,8
|
||||
Size=62,50
|
||||
Collapsed=0
|
||||
DockId=0x00000004,0
|
||||
|
||||
[Window][Time]
|
||||
Pos=1193,60
|
||||
Size=62,49
|
||||
Collapsed=0
|
||||
DockId=0x00000005,0
|
||||
|
||||
[Window][Active Modules]
|
||||
Pos=1193,111
|
||||
Size=62,65
|
||||
Collapsed=0
|
||||
DockId=0x00000006,0
|
||||
|
||||
[Docking][Data]
|
||||
DockNode ID=0x00000001 Pos=1196,188 Size=379,195 Split=X
|
||||
DockNode ID=0x00000008 Parent=0x00000001 SizeRef=652,419 HiddenTabBar=1 Selected=0x55954704
|
||||
DockNode ID=0x00000009 Parent=0x00000001 SizeRef=129,419 Split=Y
|
||||
DockNode ID=0x00000002 Parent=0x00000009 SizeRef=219,34 Split=Y Selected=0xFC1D20C0
|
||||
DockNode ID=0x00000004 Parent=0x00000002 SizeRef=219,64 Selected=0xFC1D20C0
|
||||
DockNode ID=0x00000005 Parent=0x00000002 SizeRef=219,62 Selected=0xE75A179E
|
||||
DockNode ID=0x00000003 Parent=0x00000009 SizeRef=219,31 Split=Y Selected=0xEE305C78
|
||||
DockNode ID=0x00000006 Parent=0x00000003 SizeRef=219,142 Selected=0xEE305C78
|
||||
DockNode ID=0x00000007 Parent=0x00000003 SizeRef=219,55 Selected=0xE87781F4
|
||||
|
|
@ -9,11 +9,13 @@ FILE(GLOB_RECURSE SOURCES
|
|||
core/src/*.cpp
|
||||
shadow-renderer/src/*.cpp
|
||||
shadow-reflection/src/*.cpp
|
||||
shadow-utility/src/*.cpp
|
||||
)
|
||||
FILE(GLOB_RECURSE HEADERS
|
||||
core/inc/*.h
|
||||
shadow-renderer/inc/*.h
|
||||
shadow-reflection/inc/*.h
|
||||
shadow-utility/inc/*.h
|
||||
)
|
||||
|
||||
add_library(shadow-engine SHARED ${SOURCES} $<TARGET_OBJECTS:imgui>)
|
||||
|
@ -24,6 +26,7 @@ target_include_directories(shadow-engine
|
|||
core/inc
|
||||
shadow-renderer/inc
|
||||
shadow-reflection/inc
|
||||
shadow-utility/inc
|
||||
${glm_SOURCE_DIR}
|
||||
INTERFACE
|
||||
${imgui_SOURCE_DIR}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
#define UMBRA_MODULE_H
|
||||
|
||||
#include "SHObject.h"
|
||||
#include <SDL_events.h>
|
||||
#include "SDL_events.h"
|
||||
#include "vlkx/vulkan/abstraction/Commands.h"
|
||||
|
||||
namespace ShadowEngine {
|
||||
|
||||
|
@ -29,13 +30,17 @@ namespace ShadowEngine {
|
|||
/// <summary>
|
||||
/// update is called each frame
|
||||
/// </summary>
|
||||
virtual void Update() = 0;
|
||||
virtual void Update(int frame) = 0;
|
||||
|
||||
virtual void Recreate() = 0;
|
||||
|
||||
virtual void PreRender() = 0;
|
||||
|
||||
virtual void Render() = 0;
|
||||
virtual void Render(VkCommandBuffer& commands, int frame) = 0;
|
||||
|
||||
virtual void LateRender() = 0;
|
||||
virtual void LateRender(VkCommandBuffer& commands, int frame) = 0;
|
||||
|
||||
virtual void OverlayRender() = 0;
|
||||
|
||||
virtual void AfterFrameEnd() = 0;
|
||||
|
||||
|
@ -52,6 +57,21 @@ namespace ShadowEngine {
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* A class especially for modules that are renderers.
|
||||
* Allows the engine to access state from the renderer independent of implementation.
|
||||
*/
|
||||
class RendererModule : public Module {
|
||||
public:
|
||||
// Begin the render pass using the given commands.
|
||||
// Will call out through the regular modules to gather geometry to render.
|
||||
virtual void BeginRenderPass(const std::unique_ptr<vlkx::RenderCommand>& commands) = 0;
|
||||
|
||||
virtual void EnableEditor() = 0;
|
||||
|
||||
virtual VkExtent2D GetRenderExtent() = 0;
|
||||
};
|
||||
|
||||
} // ShadowEngine
|
||||
|
||||
#endif //UMBRA_MODULE_H
|
||||
|
|
|
@ -11,22 +11,29 @@ namespace ShadowEngine {
|
|||
public:
|
||||
std::shared_ptr<Module> module;
|
||||
std::string domain;
|
||||
|
||||
// Reinterpret this module as if it were a Renderer Module.
|
||||
// A shortcut for `std::static_pointer_cast<std::shared_ptr<RendererModule>>(ShadowEngine::ModuleManager::instance->GetModule("renderer"))
|
||||
std::shared_ptr<RendererModule> operator->() const { return std::static_pointer_cast<RendererModule>(module); }
|
||||
};
|
||||
|
||||
class ModuleManager {
|
||||
public:
|
||||
static ModuleManager *instance;
|
||||
static API ModuleManager *instance;
|
||||
static ModuleManager* getInstance() { return instance; }
|
||||
|
||||
std::list<ModuleRef> modules;
|
||||
ModuleRef renderer;
|
||||
|
||||
ModuleManager();
|
||||
|
||||
~ModuleManager();
|
||||
|
||||
void PushModule(std::shared_ptr<Module> module, std::string domain);
|
||||
void PushModule(const std::shared_ptr<Module>& module, const std::string& domain);
|
||||
|
||||
Module &GetModule(std::string name);
|
||||
Module &GetModule(const std::string& name);
|
||||
|
||||
/*
|
||||
template<typename T>
|
||||
T *GetModuleByType() {
|
||||
for (auto &module: modules) {
|
||||
|
@ -35,15 +42,19 @@ namespace ShadowEngine {
|
|||
}
|
||||
//SH_CORE_ERROR("Can't find the module {0}", T::Type());
|
||||
return nullptr;
|
||||
}
|
||||
} */
|
||||
|
||||
void Init();
|
||||
|
||||
void Update();
|
||||
void Update(int frame);
|
||||
|
||||
void LateRender();
|
||||
void LateRender(VkCommandBuffer& commands, int frame);
|
||||
|
||||
void Render();
|
||||
void OverlayRender();
|
||||
|
||||
void Recreate();
|
||||
|
||||
void Render(VkCommandBuffer& commands, int frame);
|
||||
|
||||
void PreRender();
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include "Module.h"
|
||||
#include "ShadowWindow.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include "SDL.h"
|
||||
|
||||
namespace ShadowEngine {
|
||||
|
||||
|
@ -23,11 +23,15 @@ namespace ShadowEngine {
|
|||
|
||||
void PreInit() override;
|
||||
|
||||
void Update() override;
|
||||
void Update(int frame) override;
|
||||
|
||||
void Render() override;
|
||||
void Recreate() override;
|
||||
|
||||
void LateRender() override;
|
||||
void Render(VkCommandBuffer& commands, int frame) override;
|
||||
|
||||
void OverlayRender() override;
|
||||
|
||||
void LateRender(VkCommandBuffer& commands, int frame) override;
|
||||
|
||||
std::string GetName() override;
|
||||
|
||||
|
|
|
@ -68,6 +68,6 @@ namespace ShadowEngine {
|
|||
void Init();
|
||||
void Start();
|
||||
|
||||
|
||||
};
|
||||
void PollEvents();
|
||||
};
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include "SDL.h"
|
||||
|
||||
namespace ShadowEngine {
|
||||
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include "exports.h"
|
||||
|
||||
class Time
|
||||
{
|
||||
static int NOW;
|
||||
|
||||
public:
|
||||
static int LAST;
|
||||
static API int NOW;
|
||||
static API int LAST;
|
||||
|
||||
static double deltaTime;
|
||||
static double deltaTime_ms;
|
||||
static API double deltaTime;
|
||||
static API double deltaTime_ms;
|
||||
|
||||
static API double timeSinceStart;
|
||||
static API double startTime;
|
||||
|
||||
static void UpdateTime();
|
||||
};
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
#ifndef UMBRA_DEBUGMODULE_H
|
||||
#define UMBRA_DEBUGMODULE_H
|
||||
|
||||
#include <SDL_events.h>
|
||||
#include "SDL_events.h"
|
||||
#include "core/Module.h"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace ShadowEngine::Debug {
|
||||
|
||||
|
@ -17,15 +18,19 @@ namespace ShadowEngine::Debug {
|
|||
bool active;
|
||||
|
||||
public:
|
||||
void Render() override;
|
||||
void Render(VkCommandBuffer& commands, int frame) override {};
|
||||
|
||||
void PreInit() override { };
|
||||
|
||||
void Init() override { };
|
||||
|
||||
void Update() override { };
|
||||
void Recreate() override {};
|
||||
|
||||
void LateRender() override { };
|
||||
void OverlayRender() override;
|
||||
|
||||
void Update(int frame) override { };
|
||||
|
||||
void LateRender(VkCommandBuffer& commands, int frame) override { };
|
||||
|
||||
void AfterFrameEnd() override { };
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
#if defined(_WIN32)
|
||||
# if defined(EXPORTING_SH_ENGINE)
|
||||
# define SH_EXPORT __declspec(dllexport)
|
||||
# define API __declspec(dllexport)
|
||||
# else
|
||||
# define SH_EXPORT __declspec(dllimport)
|
||||
# define API __declspec(dllimport)
|
||||
# endif
|
||||
#else // non windows
|
||||
# define SH_EXPORT
|
||||
|
|
348
projs/shadow/shadow-engine/core/src/core/App2.txt
Normal file
348
projs/shadow/shadow-engine/core/src/core/App2.txt
Normal file
|
@ -0,0 +1,348 @@
|
|||
#include "ShadowApplication.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#include "time.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_vulkan.h"
|
||||
#include "imgui_impl_sdl.h"
|
||||
#include <vlkx/vulkan/VulkanManager.h>
|
||||
#include "vlkx/render/Camera.h"
|
||||
#include "vlkx/render/render_pass/ScreenRenderPass.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "vlkx/vulkan/abstraction/Commands.h"
|
||||
#include "vlkx/render/Geometry.h"
|
||||
#include "temp/model/Builder.h"
|
||||
|
||||
#define CATCH(x) \
|
||||
try { x } catch (std::exception& e) { spdlog::error(e.what()); exit(0); }
|
||||
|
||||
namespace ShadowEngine {
|
||||
|
||||
struct SkyboxTransform {
|
||||
alignas(sizeof(glm::mat4)) glm::mat4 value;
|
||||
};
|
||||
|
||||
struct PlanetTransform {
|
||||
alignas(sizeof(glm::mat4)) glm::mat4 model;
|
||||
alignas(sizeof(glm::mat4)) glm::mat4 projection;
|
||||
};
|
||||
|
||||
struct Light {
|
||||
alignas(sizeof(glm::vec4)) glm::vec4 dirTime;
|
||||
};
|
||||
|
||||
std::unique_ptr<vlkx::ScreenRenderPassManager> passManager;
|
||||
std::unique_ptr<vlkx::RenderCommand> renderCommands;
|
||||
std::unique_ptr<vlkx::UserPerspectiveCamera> camera;
|
||||
std::unique_ptr<vlkx::UniformBuffer> light;
|
||||
|
||||
std::unique_ptr<vlkx::PushConstant> skyboxConstant;
|
||||
std::unique_ptr<vlkx::PushConstant> planetConstant;
|
||||
std::unique_ptr<vlkxtemp::Model> skyboxModel;
|
||||
std::unique_ptr<vlkxtemp::Model> planetModel;
|
||||
std::unique_ptr<vlkxtemp::Model> asteroidModel;
|
||||
|
||||
float aspectRatio;
|
||||
|
||||
ShadowApplication* ShadowApplication::instance = nullptr;
|
||||
|
||||
ShadowApplication::ShadowApplication(int argc, char* argv[])
|
||||
{
|
||||
instance = this;
|
||||
|
||||
if(argc > 1)
|
||||
{
|
||||
for (size_t i = 0; i < argc; i++)
|
||||
{
|
||||
std::string param(argv[i]);
|
||||
if(param == "-no-gui")
|
||||
{
|
||||
this->no_gui = true;
|
||||
}
|
||||
if(param == "-game")
|
||||
{
|
||||
this->game = argv[i+1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//game = _setupFunc();
|
||||
}
|
||||
|
||||
|
||||
ShadowApplication::~ShadowApplication()
|
||||
{
|
||||
}
|
||||
|
||||
void ShadowApplication::Init()
|
||||
{
|
||||
// Initialize SDL. SDL_Init will return -1 if it fails.
|
||||
if ( SDL_Init( SDL_INIT_EVERYTHING ) < 0 ) {
|
||||
//std::cout << "Error initializing SDL: " << SDL_GetError() << std::endl;
|
||||
//system("pause");
|
||||
// End the program
|
||||
//return 1;
|
||||
}
|
||||
|
||||
window_ = new ShadowWindow(800,800);
|
||||
|
||||
CATCH(VulkanManager::getInstance()->initVulkan(window_->sdlWindowPtr);)
|
||||
|
||||
renderCommands = std::make_unique<vlkx::RenderCommand>(2);
|
||||
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
|
||||
// Setup Dear ImGui style
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
VkDescriptorPool imGuiPool;
|
||||
VulkanManager* vk = VulkanManager::getInstance();
|
||||
VkDescriptorPoolSize pool_sizes[] =
|
||||
{
|
||||
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
|
||||
};
|
||||
|
||||
VkDescriptorPoolCreateInfo pool_info = {};
|
||||
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
||||
pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes);
|
||||
pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);
|
||||
pool_info.pPoolSizes = pool_sizes;
|
||||
vkCreateDescriptorPool(vk->getDevice()->logical, &pool_info, VK_NULL_HANDLE, &imGuiPool);
|
||||
|
||||
// Setup Platform/Renderer backends
|
||||
ImGui_ImplSDL2_InitForVulkan(window_->sdlWindowPtr);
|
||||
ImGui_ImplVulkan_InitInfo init_info = {};
|
||||
init_info.Instance = vk->getVulkan();
|
||||
init_info.PhysicalDevice = vk->getDevice()->physical;
|
||||
init_info.Device = vk->getDevice()->logical;
|
||||
init_info.QueueFamily = vk->getDevice()->queueData.graphics;
|
||||
init_info.Queue = vk->getDevice()->graphicsQueue;
|
||||
init_info.PipelineCache = VK_NULL_HANDLE;
|
||||
init_info.DescriptorPool = imGuiPool;
|
||||
init_info.Subpass = 1;
|
||||
init_info.MinImageCount = vk->getSwapchain()->images.size();
|
||||
init_info.ImageCount = vk->getSwapchain()->images.size();
|
||||
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
init_info.Allocator = VK_NULL_HANDLE;
|
||||
init_info.CheckVkResultFn = nullptr;
|
||||
|
||||
vlkxtemp::ModelBuilder::ShaderPool pool;
|
||||
|
||||
renderCommands = std::make_unique<vlkx::RenderCommand>(2);
|
||||
skyboxConstant = std::make_unique<vlkx::PushConstant>(sizeof(SkyboxTransform), 2);
|
||||
planetConstant = std::make_unique<vlkx::PushConstant>(sizeof(PlanetTransform), 2);
|
||||
light = std::make_unique<vlkx::UniformBuffer>(sizeof(Light), 2);
|
||||
|
||||
aspectRatio = (float) window_->Width / window_->Height;
|
||||
|
||||
vlkx::Camera::Config conf {};
|
||||
camera = vlkx::UserPerspectiveCamera::create( {}, {}, { 110, aspectRatio });
|
||||
|
||||
using vlkxtemp::ModelBuilder;
|
||||
|
||||
|
||||
const vlkx::RefCountedTexture::CubemapLocation skybox {
|
||||
"resources/planets/bg",
|
||||
{
|
||||
"left.png", "right.png",
|
||||
"top.png", "bottom.png",
|
||||
"front.png", "back.png"
|
||||
}
|
||||
};
|
||||
|
||||
skyboxModel = ModelBuilder {
|
||||
"Skybox", 2, aspectRatio,
|
||||
ModelBuilder::SingleMeshModel { "resources/planets/skybox.obj", 1,
|
||||
{ { ModelBuilder::TextureType::Cubemap, { { skybox } } } }
|
||||
}}
|
||||
.bindTextures(ModelBuilder::TextureType::Cubemap, 1)
|
||||
.pushStage(VK_SHADER_STAGE_VERTEX_BIT)
|
||||
.pushConstant(skyboxConstant.get(), 0)
|
||||
.shader(VK_SHADER_STAGE_VERTEX_BIT, "resources/planets/skybox.vert.spv")
|
||||
.shader(VK_SHADER_STAGE_FRAGMENT_BIT, "resources/planets/skybox.frag.spv")
|
||||
.build();
|
||||
|
||||
planetModel = ModelBuilder {
|
||||
"Walrus", 2, aspectRatio,
|
||||
ModelBuilder::SingleMeshModel {"resources/walrus/walrus.obj", 1,
|
||||
{{ ModelBuilder::TextureType::Diffuse, { { "resources/walrus/texture.png" } } } }
|
||||
}}
|
||||
.bindTextures(ModelBuilder::TextureType::Diffuse, 2)
|
||||
.uniform(VK_SHADER_STAGE_FRAGMENT_BIT, {{1, 1}})
|
||||
.uniformBuffer(1, *light)
|
||||
.pushStage(VK_SHADER_STAGE_VERTEX_BIT)
|
||||
.pushConstant(planetConstant.get(), 0)
|
||||
.shader(VK_SHADER_STAGE_VERTEX_BIT, "resources/walrus/walrus.vert.spv")
|
||||
.shader(VK_SHADER_STAGE_FRAGMENT_BIT, "resources/walrus/walrus.frag.spv")
|
||||
.build();
|
||||
|
||||
passManager = std::make_unique<vlkx::ScreenRenderPassManager>(vlkx::RendererConfig { 2 });
|
||||
|
||||
passManager->initializeRenderPass();
|
||||
|
||||
skyboxModel->update(true, VulkanManager::getInstance()->getSwapchain()->extent, VK_SAMPLE_COUNT_1_BIT, *passManager->getPass(), 0);
|
||||
int cursorX, cursorY;
|
||||
SDL_GetMouseState(&cursorX, &cursorY);
|
||||
camera->setPos({ cursorX, cursorY });
|
||||
|
||||
planetModel->update(true, VulkanManager::getInstance()->getSwapchain()->extent, VK_SAMPLE_COUNT_1_BIT, *passManager->getPass(), 0);
|
||||
|
||||
ImGui_ImplVulkan_Init(&init_info, **passManager->getPass());
|
||||
// Upload Fonts
|
||||
VkTools::immediateExecute([](const VkCommandBuffer& commands) { ImGui_ImplVulkan_CreateFontsTexture(commands); }, VulkanManager::getInstance()->getDevice());
|
||||
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
}
|
||||
|
||||
void printMatrix(glm::mat4 mat) {
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
for (size_t j = 0; j < 4; j++) {
|
||||
std::cout << mat[i][j] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
std::cout << std::endl << std::endl;
|
||||
}
|
||||
|
||||
void updateData(int frame) {
|
||||
const float elapsed_time = Time::timeSinceStart;
|
||||
|
||||
const glm::vec3 lightDir{glm::sin(elapsed_time * 0.0006f), -0.3f,
|
||||
glm::cos(elapsed_time * 0.0006f)};
|
||||
*light->getData<Light>(frame) =
|
||||
{glm::vec4{lightDir, elapsed_time}};
|
||||
light->upload(frame);
|
||||
|
||||
glm::mat4 modelMatrix { 1 };
|
||||
modelMatrix = glm::rotate(modelMatrix, elapsed_time * glm::radians(0.0005f), glm::vec3 { 0, 1, 0 });
|
||||
const vlkx::Camera& cam = camera->getCamera();
|
||||
|
||||
const glm::mat4 view = glm::lookAt(glm::vec3{3.0f}, glm::vec3{0.0f},
|
||||
glm::vec3{0.0f, 1.0f, 0.0f});
|
||||
const glm::mat4 proj = glm::perspective(
|
||||
glm::radians(45.0f), aspectRatio,
|
||||
0.1f, 100.0f);
|
||||
|
||||
glm::mat4 planetProjection = cam.getProjMatrix() * cam.getViewMatrix();
|
||||
*planetConstant->getData<PlanetTransform>(frame) = { modelMatrix, planetProjection };
|
||||
glm::mat4 skyboxMat = cam.getProjMatrix() * cam.getSkyboxView();
|
||||
skyboxConstant->getData<SkyboxTransform>(frame)->value = skyboxMat;
|
||||
}
|
||||
|
||||
void imGuiStartDraw() {
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
void imGuiEndDraw(const VkCommandBuffer& commands) {
|
||||
ImGui::Render();
|
||||
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), commands);
|
||||
}
|
||||
|
||||
void showDebugWindow(std::unique_ptr<vlkx::UserPerspectiveCamera>* cam) {
|
||||
#define camPos cam->get()->getCamera().getPosition()
|
||||
#define camFwd cam->get()->getCamera().getForward()
|
||||
ImGui::Begin("Camera Debug");
|
||||
ImGui::Text("Camera position: (x %f, y %f, z %f)", camPos.x, camPos.y, camPos.z );
|
||||
ImGui::Text("Camera target: (x %f, y %f, z %f)", camPos.x + camFwd.x, camPos.y + camFwd.y, camPos.z + camFwd.z);
|
||||
ImGui::Text("Camera FOV: %f", cam->get()->getCamera().getFieldOfView());
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ShadowApplication::PollEvents() {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) { // poll until all events are handled!
|
||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||
|
||||
switch(event.type) {
|
||||
case SDL_KEYDOWN:
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_ESCAPE:
|
||||
camera->reset(); break;
|
||||
case SDLK_w:
|
||||
camera->press(vlkx::Camera::Input::Up, Time::deltaTime); break;
|
||||
case SDLK_s:
|
||||
camera->press(vlkx::Camera::Input::Down, Time::deltaTime); break;
|
||||
case SDLK_a:
|
||||
camera->press(vlkx::Camera::Input::Left, Time::deltaTime); break;
|
||||
case SDLK_d:
|
||||
camera->press(vlkx::Camera::Input::Right, Time::deltaTime); break;
|
||||
} break;
|
||||
case SDL_MOUSEMOTION:
|
||||
camera->move(-event.motion.xrel, -event.motion.yrel); break;
|
||||
|
||||
case SDL_MOUSEWHEEL:
|
||||
camera->scroll(event.wheel.y, 1, 170); break;
|
||||
case SDL_QUIT:
|
||||
running = false; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowApplication::Start()
|
||||
{
|
||||
const auto update = [](const int frame) { updateData(frame); };
|
||||
|
||||
while (running)
|
||||
{
|
||||
PollEvents();
|
||||
|
||||
const auto result = renderCommands->execute(renderCommands->getFrame(), VulkanManager::getInstance()->getSwapchain()->swapChain, update,
|
||||
[](const VkCommandBuffer& buffer, uint32_t frame) {
|
||||
passManager->getPass()->execute(buffer, frame, {
|
||||
// Render our model
|
||||
[&frame](const VkCommandBuffer& commands) {
|
||||
planetModel->draw(commands, frame, 1);
|
||||
skyboxModel->draw(commands, frame, 1);
|
||||
},
|
||||
// Render ImGUI
|
||||
[&](const VkCommandBuffer& commands) {
|
||||
imGuiStartDraw();
|
||||
|
||||
bool showDemo = true;
|
||||
//if (showDemo)
|
||||
// ImGui::ShowDemoWindow(&showDemo);
|
||||
|
||||
showDebugWindow(&camera);
|
||||
|
||||
imGuiEndDraw(commands);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (result.has_value())
|
||||
throw std::runtime_error("Resizing is not implemented");
|
||||
|
||||
renderCommands->nextFrame();
|
||||
|
||||
Time::UpdateTime();
|
||||
|
||||
camera->active(true);
|
||||
}
|
||||
|
||||
vkDeviceWaitIdle(VulkanManager::getInstance()->getDevice()->logical);
|
||||
|
||||
ImGui_ImplVulkan_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
SDL_DestroyWindow(window_->sdlWindowPtr);
|
||||
SDL_Quit();
|
||||
}
|
||||
}
|
|
@ -18,17 +18,18 @@ ShadowEngine::ModuleManager::ModuleManager()
|
|||
}
|
||||
|
||||
ShadowEngine::ModuleManager::~ModuleManager()
|
||||
{
|
||||
}
|
||||
= default;
|
||||
|
||||
void ShadowEngine::ModuleManager::PushModule(std::shared_ptr<Module> module, const std::string domain)
|
||||
void ShadowEngine::ModuleManager::PushModule(const std::shared_ptr<Module>& module, const std::string& domain)
|
||||
{
|
||||
ModuleRef r = {module, domain};
|
||||
modules.emplace_back(r);
|
||||
if (domain == "renderer")
|
||||
renderer = r;
|
||||
module->PreInit();
|
||||
}
|
||||
|
||||
ShadowEngine::Module& ShadowEngine::ModuleManager::GetModule(std::string name)
|
||||
ShadowEngine::Module& ShadowEngine::ModuleManager::GetModule(const std::string& name)
|
||||
{
|
||||
for (auto& module : modules)
|
||||
{
|
||||
|
@ -72,30 +73,47 @@ void ShadowEngine::ModuleManager::Event(SDL_Event* evt)
|
|||
}
|
||||
}
|
||||
|
||||
void ShadowEngine::ModuleManager::Update()
|
||||
void ShadowEngine::ModuleManager::Update(int frame)
|
||||
{
|
||||
for (auto& module : modules)
|
||||
{
|
||||
module.module->Update();
|
||||
module.module->Update(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowEngine::ModuleManager::LateRender()
|
||||
void ShadowEngine::ModuleManager::LateRender(VkCommandBuffer& commands, int frame)
|
||||
{
|
||||
for (auto& module : modules)
|
||||
{
|
||||
module.module->LateRender();
|
||||
module.module->LateRender(commands, frame);
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowEngine::ModuleManager::Render()
|
||||
void ShadowEngine::ModuleManager::Render(VkCommandBuffer& commands, int frame)
|
||||
{
|
||||
for (auto& module : modules)
|
||||
{
|
||||
module.module->Render();
|
||||
module.module->Render(commands, frame);
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowEngine::ModuleManager::OverlayRender()
|
||||
{
|
||||
for (auto& module : modules)
|
||||
{
|
||||
module.module->OverlayRender();
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowEngine::ModuleManager::Recreate()
|
||||
{
|
||||
for (auto& module : modules)
|
||||
{
|
||||
module.module->Recreate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ShadowEngine::ModuleManager::AfterFrameEnd()
|
||||
{
|
||||
for (auto& module : modules)
|
||||
|
|
|
@ -21,20 +21,20 @@ void ShadowEngine::SDL2Module::PreInit() {
|
|||
//return 1;
|
||||
}
|
||||
|
||||
_window = new ShadowWindow(800,450);
|
||||
_window = new ShadowWindow(1280,720);
|
||||
SDL_SetWindowResizable(_window->sdlWindowPtr, SDL_TRUE);
|
||||
//SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
}
|
||||
|
||||
void ShadowEngine::SDL2Module::Update() {
|
||||
void ShadowEngine::SDL2Module::Update(int frame) {}
|
||||
|
||||
}
|
||||
void ShadowEngine::SDL2Module::Recreate() {}
|
||||
|
||||
void ShadowEngine::SDL2Module::Render() {
|
||||
void ShadowEngine::SDL2Module::Render(VkCommandBuffer& commands, int frame) {}
|
||||
|
||||
}
|
||||
void ShadowEngine::SDL2Module::OverlayRender() {}
|
||||
|
||||
void ShadowEngine::SDL2Module::LateRender() {
|
||||
|
||||
}
|
||||
void ShadowEngine::SDL2Module::LateRender(VkCommandBuffer& commands, int frame) {}
|
||||
|
||||
std::string ShadowEngine::SDL2Module::GetName() {
|
||||
return this->GetType();
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
#include "core/ShadowApplication.h"
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
|
||||
#include "core/ShadowApplication.h"
|
||||
#include "core/Time.h"
|
||||
#include "core/SDL2Module.h"
|
||||
#include "debug/DebugModule.h"
|
||||
#include "dylib.hpp"
|
||||
|
||||
|
||||
#include "vlkx/vulkan/abstraction/Commands.h"
|
||||
#include <imgui.h>
|
||||
#include <imgui_impl_vulkan.h>
|
||||
#include <imgui_impl_sdl.h>
|
||||
#include <vlkx/vulkan/VulkanManager.h>
|
||||
#include <vlkx/render/Camera.h>
|
||||
#include <vlkx/render/geometry/SingleRenderer.h>
|
||||
#include <vlkx/vulkan/VulkanModule.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#define CATCH(x) \
|
||||
|
@ -23,6 +20,8 @@ namespace ShadowEngine {
|
|||
|
||||
ShadowApplication* ShadowApplication::instance = nullptr;
|
||||
|
||||
std::unique_ptr<vlkx::RenderCommand> renderCommands;
|
||||
|
||||
ShadowApplication::ShadowApplication(int argc, char* argv[])
|
||||
{
|
||||
instance = this;
|
||||
|
@ -70,23 +69,17 @@ namespace ShadowEngine {
|
|||
|
||||
void ShadowApplication::Init()
|
||||
{
|
||||
spdlog::info("Starting Shadow Engine!");
|
||||
moduleManager.PushModule(std::make_shared<SDL2Module>(),"core");
|
||||
auto renderer = std::make_shared<VulkanModule>();
|
||||
renderer->EnableEditor();
|
||||
moduleManager.PushModule(renderer, "renderer");
|
||||
|
||||
loadGame();
|
||||
|
||||
printf("exe side: %p \n", VulkanManager::getInstance());
|
||||
printf("exe next ID: %llu \n", ShadowEngine::SHObject::GenerateId());
|
||||
|
||||
moduleManager.PushModule(std::make_shared<SDL2Module>(),"core");
|
||||
moduleManager.PushModule(std::make_shared<Debug::DebugModule>(), "core");
|
||||
|
||||
moduleManager.Init();
|
||||
|
||||
auto sdl2module = moduleManager.GetModuleByType<SDL2Module>();
|
||||
|
||||
window_ = sdl2module->_window;
|
||||
|
||||
|
||||
renderCommands = std::make_unique<vlkx::RenderCommand>(2);
|
||||
}
|
||||
|
||||
void ShadowApplication::Start()
|
||||
|
@ -100,16 +93,14 @@ namespace ShadowEngine {
|
|||
running = false;
|
||||
}
|
||||
|
||||
moduleManager.Update();
|
||||
moduleManager.PreRender();
|
||||
|
||||
VulkanManager::getInstance()->startDraw();
|
||||
moduleManager.Render();
|
||||
|
||||
moduleManager.LateRender();
|
||||
VulkanManager::getInstance()->endDraw();
|
||||
moduleManager.renderer->BeginRenderPass(renderCommands);
|
||||
|
||||
moduleManager.AfterFrameEnd();
|
||||
|
||||
renderCommands->nextFrame();
|
||||
Time::UpdateTime();
|
||||
}
|
||||
|
||||
moduleManager.Destroy();
|
||||
|
|
|
@ -1,20 +1,27 @@
|
|||
#include "core/Time.h"
|
||||
//#include <SDL_hints.h>
|
||||
//#include <SDL.h>
|
||||
#include <chrono>
|
||||
|
||||
int Time::NOW = 0;//SDL_GetPerformanceCounter();
|
||||
int Time::LAST = 0;
|
||||
double Time::deltaTime_ms = 0;
|
||||
double Time::deltaTime = 0;
|
||||
API int Time::NOW = 0;//SDL_GetPerformanceCounter();
|
||||
API int Time::LAST = 0;
|
||||
API double lastFrame = 0;
|
||||
API double Time::deltaTime_ms = 0;
|
||||
API double Time::deltaTime = 0;
|
||||
API double Time::startTime = 0;
|
||||
API double Time::timeSinceStart = 0;
|
||||
|
||||
void Time::UpdateTime()
|
||||
{
|
||||
/*
|
||||
NOW = SDL_GetTicks();
|
||||
deltaTime_ms = LAST > 0 ? (NOW - LAST) *10 : (1.0f / 60.0f);
|
||||
deltaTime_ms = deltaTime_ms == 0 ? (1.0f / 60.0f) : deltaTime_ms;
|
||||
using namespace std::chrono;
|
||||
auto now = system_clock::now();
|
||||
auto now_ms = time_point_cast<milliseconds>(now);
|
||||
|
||||
LAST = NOW;
|
||||
deltaTime = deltaTime_ms * 0.001;
|
||||
*/
|
||||
auto value = now_ms.time_since_epoch();
|
||||
double duration = value.count();
|
||||
|
||||
deltaTime = duration - lastFrame;
|
||||
if (startTime == 0)
|
||||
startTime = duration;
|
||||
timeSinceStart = duration - startTime;
|
||||
|
||||
lastFrame = duration;
|
||||
}
|
||||
|
|
|
@ -9,29 +9,26 @@
|
|||
|
||||
SHObject_Base_Impl(ShadowEngine::Debug::DebugModule)
|
||||
|
||||
void ShadowEngine::Debug::DebugModule::Render() {
|
||||
void ShadowEngine::Debug::DebugModule::OverlayRender() {
|
||||
|
||||
ImGui::Begin("Time", &active, ImGuiWindowFlags_MenuBar);
|
||||
|
||||
ImGui::Text("delta time in ms: %lf", Time::deltaTime_ms);
|
||||
ImGui::Text("delta time in s: %lf", Time::deltaTime);
|
||||
ImGui::Text("LAST time in: %ld", Time::LAST);
|
||||
if (ImGui::Begin("Time", &active, ImGuiWindowFlags_MenuBar)) {
|
||||
ImGui::Text("delta time in ms: %lf", Time::deltaTime_ms);
|
||||
ImGui::Text("delta time in s: %lf", Time::deltaTime);
|
||||
ImGui::Text("LAST time in: %d", Time::LAST);
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("Active Modules", &active, ImGuiWindowFlags_MenuBar);
|
||||
if (ImGui::Begin("Active Modules", &active, ImGuiWindowFlags_MenuBar)) {
|
||||
|
||||
ShadowEngine::ModuleManager* m = ShadowEngine::ModuleManager::instance;
|
||||
ShadowEngine::ModuleManager *m = ShadowEngine::ModuleManager::instance;
|
||||
|
||||
|
||||
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.4f, 1.0f), "Active Modules:");
|
||||
for (auto& module : m->modules)
|
||||
{
|
||||
ImGui::Text("%s", module.module->GetName().c_str());
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.4f, 1.0f), "Active Modules:");
|
||||
for (auto &module: m->modules) {
|
||||
ImGui::Text("%s", module.module->GetName().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ImGui::End();
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "SFFParser.h"
|
||||
#include "../../shadow-utility/src/string-helpers.h"
|
||||
#include "string-helpers.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch2/catch.hpp>
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
TEST_CASE("15 is less than 20", "[numbers]") {
|
||||
REQUIRE(15 < 20);
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace ShadowEngine {
|
|||
* \brief Generates a new UID for each call
|
||||
* \return the next Unique ID that was just generated
|
||||
*/
|
||||
SH_EXPORT static uint64_t GenerateId() noexcept;
|
||||
API static uint64_t GenerateId() noexcept;
|
||||
|
||||
public:
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
=====
|
||||
|
||||
This folder exists to store VLKX renderer objects that are TEMPORARY.
|
||||
It currently contains:
|
||||
|
||||
=====
|
||||
|
||||
Model Loader system
|
||||
|
||||
The Model Loader is temporarily implemented as a raw file reader and OBJ parser.
|
||||
It should be removed when the File Format system is able to parse model and texture files.
|
||||
|
||||
====
|
||||
|
||||
Model Abstraction system
|
||||
|
||||
The Model Abstraction allows you to create a model with:
|
||||
- A mesh
|
||||
- An arbitrary number of textures
|
||||
- A push constant
|
||||
- An arbitrarily large uniform buffer
|
||||
- A fragment and vertex shader
|
||||
|
||||
In all, it contains a custom Render Pipeline that will be used to draw the model.
|
||||
This allows for drastic and visually appealing effects.
|
||||
|
||||
It should be maintained and moved into the appropriate Shadow module once ready.
|
||||
|
||||
====
|
||||
|
||||
Model Builder system
|
||||
|
||||
The model Builder allows for simple construction of Models using the Model Abstraction system above.
|
||||
It consumes the Model Loader and returns a Model Abstraction.
|
||||
|
||||
It should be MOVED into the File Format parsing system once it is ready.
|
|
@ -0,0 +1,137 @@
|
|||
#pragma once
|
||||
|
||||
#include "vlkx/render/shader/Pipeline.h"
|
||||
#include "Loader.h"
|
||||
#include "vlkx/render/render_pass/GenericRenderPass.h"
|
||||
#include "vlkx/vulkan/abstraction/Descriptor.h"
|
||||
|
||||
namespace vlkxtemp {
|
||||
|
||||
class Model;
|
||||
|
||||
class ModelBuilder {
|
||||
public:
|
||||
|
||||
using ShaderPool = vlkx::ShaderModule::ReleasePool;
|
||||
using TextureType = ModelLoader::TextureType;
|
||||
using TexturePerMesh = std::array<std::vector<std::unique_ptr<vlkx::SamplableImage>>, static_cast<int>(TextureType::Count)>;
|
||||
using BindingPoints = std::map<TextureType, uint32_t>;
|
||||
using TextureSource = vlkx::RefCountedTexture::ImageLocation;
|
||||
using TextureSources = std::map<TextureType, std::vector<TextureSource>>;
|
||||
|
||||
class ModelResource {
|
||||
public:
|
||||
virtual ~ModelResource() = default;
|
||||
virtual void load(ModelBuilder* builder) const = 0;
|
||||
};
|
||||
|
||||
class SingleMeshModel : public ModelResource {
|
||||
public:
|
||||
SingleMeshModel(std::string&& path, int indexBase, TextureSources&& sources)
|
||||
: objFile(std::move(path)), objIndexBase(indexBase), textureSources(std::move(sources)) {}
|
||||
|
||||
void load(ModelBuilder* builder) const override;
|
||||
private:
|
||||
const std::string objFile;
|
||||
const int objIndexBase;
|
||||
const TextureSources textureSources;
|
||||
};
|
||||
|
||||
class MultiMeshModel : public ModelResource {
|
||||
public:
|
||||
MultiMeshModel(std::string&& modelDir, std::string&& textureDir)
|
||||
: models(std::move(modelDir)), textures(std::move(textureDir)) {}
|
||||
|
||||
void load(ModelBuilder* builder) const override;
|
||||
|
||||
private:
|
||||
const std::string models;
|
||||
const std::string textures;
|
||||
};
|
||||
|
||||
struct ModelPushConstant {
|
||||
struct Meta {
|
||||
const vlkx::PushConstant* constants;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
VkShaderStageFlags stage;
|
||||
std::vector<Meta> constants;
|
||||
};
|
||||
|
||||
using Descriptors = std::vector<std::unique_ptr<vlkx::StaticDescriptor>>;
|
||||
|
||||
ModelBuilder(std::string&& name, int frames, float aspect, const ModelResource& resource);
|
||||
|
||||
ModelBuilder(const ModelBuilder&) = delete;
|
||||
ModelBuilder& operator=(const ModelBuilder&) = delete;
|
||||
|
||||
ModelBuilder& texture(TextureType type, const TextureSource& source);
|
||||
ModelBuilder& bindTextures(TextureType type, uint32_t point);
|
||||
ModelBuilder& instanceBuffer(vlkx::PerInstanceVertexBuffer* buffer);
|
||||
ModelBuilder& uniform(VkShaderStageFlags stage, std::vector<vlkx::Descriptor::Meta::Binding>&& bindings);
|
||||
ModelBuilder& uniformBuffer(uint32_t point, const vlkx::UniformBuffer& buffer);
|
||||
ModelBuilder& pushStage(VkShaderStageFlags stage);
|
||||
ModelBuilder& pushConstant(const vlkx::PushConstant* constant, uint32_t offset);
|
||||
ModelBuilder& shader(VkShaderStageFlagBits stage, std::string&& file);
|
||||
|
||||
std::unique_ptr<Model> build();
|
||||
|
||||
private:
|
||||
std::vector<Descriptors> createDescs() const;
|
||||
|
||||
const int frames;
|
||||
const float aspectRatio;
|
||||
|
||||
std::unique_ptr<vlkx::StaticPerVertexBuffer> vertexBuffer;
|
||||
std::vector<TexturePerMesh> textures;
|
||||
TexturePerMesh sharedTextures;
|
||||
BindingPoints bindPoints;
|
||||
|
||||
std::vector<vlkx::PerInstanceVertexBuffer*> instanceBuffers;
|
||||
std::vector<vlkx::Descriptor::Meta> uniformMeta;
|
||||
std::vector<vlkx::Descriptor::BufferInfos> uniformBufferMeta;
|
||||
|
||||
std::optional<ModelPushConstant> pushConstants;
|
||||
std::unique_ptr<vlkx::GraphicsPipelineBuilder> pipelineBuilder;
|
||||
};
|
||||
|
||||
class Model {
|
||||
public:
|
||||
|
||||
Model(const Model&) = delete;
|
||||
Model& operator=(const Model&) = delete;
|
||||
|
||||
void update(bool opaque, const VkExtent2D& frame, VkSampleCountFlagBits samples, const vlkx::RenderPass& pass, uint32_t subpass, bool flipY = true);
|
||||
void draw(const VkCommandBuffer& commands, int frame, uint32_t instances) const;
|
||||
|
||||
private:
|
||||
friend std::unique_ptr<Model> ModelBuilder::build();
|
||||
using Descriptors = ModelBuilder::Descriptors;
|
||||
using ModelPushConstant = ModelBuilder::ModelPushConstant;
|
||||
using TexturePerMesh = ModelBuilder::TexturePerMesh;
|
||||
|
||||
Model(float aspectRatio,
|
||||
std::unique_ptr<vlkx::StaticPerVertexBuffer>&& vertexBuffer,
|
||||
std::vector<vlkx::PerInstanceVertexBuffer*>&& perInstanceBuffers,
|
||||
std::optional<ModelPushConstant>&& pushConstants,
|
||||
TexturePerMesh&& sharedTextures,
|
||||
std::vector<TexturePerMesh>&& textures,
|
||||
std::vector<Descriptors>&& descriptors,
|
||||
std::unique_ptr<vlkx::GraphicsPipelineBuilder>&& pipelineBuilder)
|
||||
: aspectRatio(aspectRatio), vertexBuffer(std::move(vertexBuffer)), perInstanceBuffers(std::move(perInstanceBuffers)),
|
||||
pushConstants(std::move(pushConstants)), sharedTextures(std::move(sharedTextures)), textures(std::move(textures)),
|
||||
descriptors(std::move(descriptors)), pipelineBuilder(std::move(pipelineBuilder)) {}
|
||||
|
||||
const float aspectRatio;
|
||||
const std::unique_ptr<vlkx::StaticPerVertexBuffer> vertexBuffer;
|
||||
const std::vector<vlkx::PerInstanceVertexBuffer*> perInstanceBuffers;
|
||||
const std::optional<ModelPushConstant> pushConstants;
|
||||
const TexturePerMesh sharedTextures;
|
||||
const std::vector<TexturePerMesh> textures;
|
||||
const std::vector<Descriptors> descriptors;
|
||||
|
||||
std::unique_ptr<vlkx::GraphicsPipelineBuilder> pipelineBuilder;
|
||||
std::unique_ptr<vlkx::Pipeline> pipeline;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "vlkx/render/Geometry.h"
|
||||
|
||||
namespace vlkxtemp {
|
||||
|
||||
struct Wavefront {
|
||||
Wavefront(std::string_view path, size_t base);
|
||||
Wavefront(const Wavefront&) = delete;
|
||||
|
||||
Wavefront& operator=(const Wavefront&) = delete;
|
||||
|
||||
std::vector<uint32_t> indices;
|
||||
std::vector<Geo::VertexAll> vertices;
|
||||
};
|
||||
|
||||
class ModelLoader {
|
||||
public:
|
||||
enum class TextureType {
|
||||
Diffuse,
|
||||
Specular,
|
||||
Reflection,
|
||||
Cubemap,
|
||||
Count
|
||||
};
|
||||
|
||||
struct TextureData {
|
||||
TextureData(TextureData&&) noexcept = default;
|
||||
TextureData& operator=(TextureData&&) noexcept = default;
|
||||
|
||||
std::string path;
|
||||
TextureType type;
|
||||
};
|
||||
|
||||
struct MeshData {
|
||||
MeshData() = default;
|
||||
MeshData(MeshData&&) noexcept = default;
|
||||
MeshData& operator=(MeshData&&) noexcept = default;
|
||||
|
||||
std::vector<Geo::VertexAll> vertices;
|
||||
std::vector<uint32_t> indices;
|
||||
std::vector<TextureData> textures;
|
||||
};
|
||||
|
||||
ModelLoader(const std::string& model, const std::string& textures);
|
||||
|
||||
ModelLoader(const ModelLoader&) = delete;
|
||||
ModelLoader& operator=(const ModelLoader&) = delete;
|
||||
|
||||
const std::vector<MeshData>& getMeshes() const { return meshes; }
|
||||
|
||||
private:
|
||||
|
||||
std::vector<MeshData> meshes;
|
||||
};
|
||||
}
|
|
@ -1,23 +1,213 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#define GLM_FORCE_RADIAN
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <functional>
|
||||
|
||||
class Camera {
|
||||
public:
|
||||
namespace vlkx {
|
||||
class Camera {
|
||||
public:
|
||||
|
||||
// Set up the camera.
|
||||
void init(float fov, float width, float height, float near, float far);
|
||||
enum class Input {
|
||||
Up, Down, Left, Right
|
||||
};
|
||||
struct Movement {
|
||||
float moveSpeed = 10;
|
||||
float turnSpeed = 0.0005f;
|
||||
std::optional<glm::vec3> center;
|
||||
};
|
||||
|
||||
// Move the camera.
|
||||
void setPosition(glm::vec3 positionIn);
|
||||
/**
|
||||
* Camera configuration; with defaults.
|
||||
* Left and forward vectors are calculated from up, pos and target.
|
||||
*/
|
||||
struct Config {
|
||||
float nearPlane = 0.1f; // The nearest a vertex can be to the camera before being clipped
|
||||
float farPlane = 100; // The furthest a vertex can be from the camera before clipped
|
||||
glm::vec3 upV{0, 1, 0}; // The vector pointing straight up from the camera
|
||||
glm::vec3 pos{0, 0, 0}; // The position of the camera in the world
|
||||
glm::vec3 target{1, 0, 0}; // The point the camera is looking at
|
||||
};
|
||||
|
||||
glm::mat4 getViewMatrix() { return viewMatrix; };
|
||||
glm::mat4 getProjectionMatrix() { return projectionMatrix; }
|
||||
Camera(const Camera &) = delete;
|
||||
|
||||
private:
|
||||
glm::mat4 projectionMatrix;
|
||||
glm::mat4 viewMatrix;
|
||||
glm::vec3 position;
|
||||
};
|
||||
Camera &operator=(const Camera &) = delete;
|
||||
|
||||
virtual ~Camera() = default;
|
||||
|
||||
Camera &move(const glm::vec3 &delta);
|
||||
|
||||
Camera &setPos(const glm::vec3 &pos);
|
||||
|
||||
Camera &up(const glm::vec3 &up);
|
||||
|
||||
Camera &forward(const glm::vec3 &forward);
|
||||
|
||||
glm::mat4 getViewMatrix() const;
|
||||
|
||||
glm::mat4 getSkyboxView() const {
|
||||
return glm::mat4{glm::mat3{getViewMatrix()}};
|
||||
}
|
||||
|
||||
virtual glm::mat4 getProjMatrix() const = 0;
|
||||
|
||||
const glm::vec3& getPosition() const { return position; }
|
||||
const glm::vec3& getUp() const { return upVector; }
|
||||
const glm::vec3& getForward() const { return frontVector; }
|
||||
const glm::vec3& getRight() const { return rightVector; }
|
||||
|
||||
protected:
|
||||
explicit Camera(const Config &conf) : nearPlane(conf.nearPlane), farPlane(conf.farPlane), position(conf.pos),
|
||||
upVector(glm::normalize(conf.upV)) {
|
||||
forward(conf.target - position);
|
||||
}
|
||||
|
||||
const float nearPlane;
|
||||
const float farPlane;
|
||||
private:
|
||||
|
||||
glm::vec3 position;
|
||||
glm::vec3 upVector;
|
||||
glm::vec3 frontVector;
|
||||
glm::vec3 rightVector;
|
||||
|
||||
};
|
||||
|
||||
class PerspectiveCamera : public Camera {
|
||||
public:
|
||||
|
||||
struct Frustum {
|
||||
float fov;
|
||||
float aspect;
|
||||
};
|
||||
|
||||
struct RT {
|
||||
glm::vec3 up;
|
||||
glm::vec3 forward;
|
||||
glm::vec3 right;
|
||||
};
|
||||
|
||||
PerspectiveCamera(const Camera::Config &conf, const Frustum &frus) :
|
||||
Camera(conf), aspectRatio(frus.aspect), fov(frus.fov) {}
|
||||
|
||||
PerspectiveCamera(const PerspectiveCamera &) = delete;
|
||||
|
||||
PerspectiveCamera &operator=(const PerspectiveCamera &) = delete;
|
||||
|
||||
PerspectiveCamera &fieldOfView(float newFov);
|
||||
|
||||
RT getRT() const;
|
||||
|
||||
glm::mat4 getProjMatrix() const override;
|
||||
|
||||
float getFieldOfView() const { return fov; }
|
||||
|
||||
float getAspect() const { return aspectRatio; }
|
||||
|
||||
private:
|
||||
|
||||
const float aspectRatio;
|
||||
float fov;
|
||||
};
|
||||
|
||||
class OrthographicCamera : public Camera {
|
||||
public:
|
||||
struct OrthoConfig {
|
||||
float width;
|
||||
float aspect;
|
||||
};
|
||||
|
||||
static OrthoConfig getFullscreenConfig() {
|
||||
return {2, 1};
|
||||
}
|
||||
|
||||
OrthographicCamera(const Camera::Config &config, const OrthoConfig &ortho)
|
||||
: Camera(config), aspectRatio(ortho.aspect), width(ortho.width) {}
|
||||
|
||||
OrthographicCamera(const OrthographicCamera &) = delete;
|
||||
|
||||
OrthographicCamera &operator=(const OrthographicCamera &) = delete;
|
||||
|
||||
OrthographicCamera &setWidth(float width);
|
||||
|
||||
glm::mat4 getProjMatrix() const override;
|
||||
|
||||
float getWidth() const { return width; }
|
||||
|
||||
private:
|
||||
|
||||
const float aspectRatio;
|
||||
float width;
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
class UserCamera {
|
||||
public:
|
||||
UserCamera(const UserCamera &) = delete;
|
||||
|
||||
UserCamera &operator=(const UserCamera &) = delete;
|
||||
|
||||
virtual ~UserCamera() = default;
|
||||
|
||||
void setInternal(std::function<void(Type *)> op);
|
||||
|
||||
void setPos(const glm::dvec2 &pos) { cursorPos = pos; }
|
||||
|
||||
void move(double x, double y);
|
||||
|
||||
bool scroll(double delta, double min, double max);
|
||||
|
||||
void press(Camera::Input key, float time);
|
||||
|
||||
void active(bool active) { isActive = active; }
|
||||
|
||||
const Type &getCamera() const { return *camera; }
|
||||
|
||||
UserCamera(const Camera::Movement &movement, std::unique_ptr<Type> &&cam)
|
||||
: config(movement), camera(std::move(cam)) {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
const Camera::Movement config;
|
||||
bool isActive = false;
|
||||
|
||||
std::unique_ptr<Type> camera;
|
||||
glm::dvec2 cursorPos;
|
||||
glm::vec3 refForward;
|
||||
glm::vec3 refLeft;
|
||||
|
||||
float pitch;
|
||||
float yaw;
|
||||
};
|
||||
|
||||
class UserPerspectiveCamera : public UserCamera<PerspectiveCamera> {
|
||||
public:
|
||||
static std::unique_ptr<UserPerspectiveCamera>
|
||||
create(const Camera::Movement &movement, const Camera::Config &config,
|
||||
const PerspectiveCamera::Frustum &frustum) {
|
||||
return std::make_unique<UserPerspectiveCamera>(movement,
|
||||
std::make_unique<PerspectiveCamera>(config, frustum));
|
||||
}
|
||||
|
||||
protected:
|
||||
using UserCamera<PerspectiveCamera>::UserCamera;
|
||||
};
|
||||
|
||||
class UserOrthoCamera : public UserCamera<OrthographicCamera> {
|
||||
public:
|
||||
static std::unique_ptr<UserOrthoCamera> create(const Camera::Movement &movement, const Camera::Config &config,
|
||||
const OrthographicCamera::OrthoConfig &ortho) {
|
||||
return std::make_unique<UserOrthoCamera>(movement, std::make_unique<OrthographicCamera>(config, ortho));
|
||||
}
|
||||
|
||||
protected:
|
||||
using UserCamera<OrthographicCamera>::UserCamera;
|
||||
};
|
||||
}
|
|
@ -28,65 +28,66 @@ namespace Geo {
|
|||
};
|
||||
|
||||
// All of the metadata involved with a vertex.
|
||||
struct Vertex {
|
||||
struct VertexAll {
|
||||
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.stride = sizeof(VertexAll);
|
||||
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;
|
||||
static std::vector<VkVertexInputAttributeDescription> getAttributeDesc() {
|
||||
return {
|
||||
{ 0, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexAll, position)) },
|
||||
{ 0, 1, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexAll, normal)) },
|
||||
{ 0, 2, VK_FORMAT_R32G32_SFLOAT, static_cast<uint32_t>(offsetof(VertexAll, texture)) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// All of the metadata involved with a vertex.
|
||||
struct VertexColor {
|
||||
glm::vec3 position; // XYZ coordinates of the vertex's position.
|
||||
glm::vec3 color; // The color of the vertex.
|
||||
|
||||
// How fast should vertex data be read from RAM?
|
||||
static VkVertexInputBindingDescription getBindingDesc() {
|
||||
VkVertexInputBindingDescription desc = {};
|
||||
desc.binding = 0;
|
||||
desc.stride = sizeof(VertexColor);
|
||||
desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
// How should vertexes be handled?
|
||||
static std::vector<VkVertexInputAttributeDescription> getAttributeDesc() {
|
||||
return {
|
||||
{ 0, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexColor, position)) },
|
||||
{ 0, 1, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexColor, color)) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// 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);
|
||||
static void setTriData(std::vector<VertexAll>& 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);
|
||||
static void setQuadData(std::vector<VertexAll>& 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);
|
||||
static void setCubeData(std::vector<VertexAll>& 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);
|
||||
static void setSphereData(std::vector<VertexAll>& vertices, std::vector<uint32_t>& indices);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
#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();
|
||||
};
|
|
@ -1,35 +0,0 @@
|
|||
#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; }
|
||||
|
||||
Descriptor getDescriptor() { return descriptor; }
|
||||
|
||||
glm::mat4 getRotation() { return rotation; }
|
||||
private:
|
||||
|
||||
Pipeline pipeline;
|
||||
GeoBuffers buffers;
|
||||
Descriptor descriptor;
|
||||
|
||||
glm::vec3 position;
|
||||
glm::vec3 scale;
|
||||
glm::mat4 rotation;
|
||||
};
|
|
@ -0,0 +1,242 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include "vlkx/vulkan/abstraction/ImageUsage.h"
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include "GenericRenderPass.h"
|
||||
|
||||
namespace vlkx {
|
||||
|
||||
/**
|
||||
* The common base class for rendering and computing passes that run on the GPU.
|
||||
* Provides some utility methods for handling attachment metadata between subpasses.
|
||||
*/
|
||||
class CommonPass {
|
||||
public:
|
||||
|
||||
explicit CommonPass(int passes) : numPasses(passes) {}
|
||||
|
||||
// Delete the copy and move constructors
|
||||
CommonPass(const CommonPass&) = delete;
|
||||
CommonPass& operator=(const CommonPass&) = delete;
|
||||
virtual ~CommonPass() = default;
|
||||
|
||||
// Get the image layout of the given image at the start of this pass
|
||||
VkImageLayout getInitialLayout(const std::string& name) const;
|
||||
// Get the image layout of the given image at the end of this pass
|
||||
VkImageLayout getFinalLayout(const std::string& name) const;
|
||||
// Get the image layout of the given image before the given subpass starts
|
||||
VkImageLayout getSubpassLayout(const std::string& name, int subpass) const;
|
||||
|
||||
// Update the state of the given image's usage tracker.
|
||||
void update(const std::string& name, MultiImageTracker& tracker) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Some metadata about the usage of an image between subpasses.
|
||||
*/
|
||||
struct Usages {
|
||||
Usages(const int last, const ImageUsage* prev, const ImageUsage* curr) : lastSubpass(last), lastUsage(*prev), currentUsage(*curr) {}
|
||||
const int lastSubpass;
|
||||
const ImageUsage& lastUsage;
|
||||
const ImageUsage& currentUsage;
|
||||
};
|
||||
|
||||
// Add the usage of an image in the pass to its' tracker.
|
||||
void addUsage(std::string&& name, UsageTracker&& tracker);
|
||||
|
||||
// Get the full history of the image's usages up to this rendering pass.
|
||||
const UsageTracker& getHistory(const std::string& name) const;
|
||||
|
||||
// Get the usage of an image at the start of the given pass.
|
||||
const ImageUsage* getUsage(const std::string& name, int pass) const;
|
||||
|
||||
// Retrieve image usage data, but only if the image is barriered at the given pass.
|
||||
std::optional<Usages> checkForSync(const std::string& name, int pass) const;
|
||||
|
||||
// Validate that the subpass is valid for the given image.
|
||||
// The meaning of includeVirtual is defined by the child implementation.
|
||||
void validate(int pass, const std::string& image, bool includeVirtual) const;
|
||||
|
||||
int getVirtualInitial() const { return -1; }
|
||||
int getVirtualFinal() const { return numPasses; }
|
||||
|
||||
protected:
|
||||
std::map<std::string, UsageTracker> usageHistory;
|
||||
const int numPasses;
|
||||
};
|
||||
|
||||
/**
|
||||
* The Common Pass implementation for Graphics passes; that is, render passes that output to color buffers
|
||||
* for presentation to the screen, or to be used as textures in such.
|
||||
* The instance of the GraphicsPass can be stored and reused to create multiple RenderPassBuilders.
|
||||
* In this way it is essentially a RenderPassBuilderFactory.
|
||||
*/
|
||||
class GraphicsPass : public CommonPass {
|
||||
public:
|
||||
|
||||
using LocationGetter = std::function<int(int pass)>;
|
||||
|
||||
explicit GraphicsPass(int passes) : CommonPass {passes} {}
|
||||
|
||||
GraphicsPass(const GraphicsPass&) = delete;
|
||||
GraphicsPass& operator=(const GraphicsPass&) = delete;
|
||||
|
||||
// Get the default render ops for a color buffer.
|
||||
static RenderPassBuilder::Attachment::OpsType getDefaultOps() {
|
||||
return RenderPassBuilder::Attachment::ColorOps { VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE };
|
||||
}
|
||||
|
||||
// Get the default render ops for a stencil buffer.
|
||||
static RenderPassBuilder::Attachment::OpsType getStencilOps() {
|
||||
return RenderPassBuilder::Attachment::StencilDepthOps { VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_DONT_CARE };
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an image reference that is used in this render pass.
|
||||
* @param name the name of the image used
|
||||
* @param history the usage history of the image, for tracking purposes
|
||||
* @param getter a function to get the location of the image, only if the image is used as a render target.
|
||||
* @param ops optional; uses the static defaults if not present.
|
||||
* @return the index into the VkAttachmentDescriptions.
|
||||
*/
|
||||
int add(const std::string& name, UsageTracker&& history, LocationGetter&& getter, const std::optional<RenderPassBuilder::Attachment::OpsType> ops = std::nullopt);
|
||||
|
||||
#define fluent GraphicsPass&
|
||||
|
||||
// Specifies that the source image will be resolved to the single destination at the given pass.
|
||||
fluent addMultisample(const std::string& source, const std::string& dest, int pass);
|
||||
|
||||
// Build a RenderPassBuilder with the information provided so far.
|
||||
std::unique_ptr<RenderPassBuilder> build(int framebuffers);
|
||||
|
||||
private:
|
||||
struct AttachmentMeta {
|
||||
int index;
|
||||
LocationGetter getter;
|
||||
vlkx::RenderPassBuilder::Attachment::OpsType ops;
|
||||
std::map<int, std::string> multisample;
|
||||
};
|
||||
|
||||
void setAttachments();
|
||||
void setSubpasses();
|
||||
void setDependencies();
|
||||
|
||||
/**
|
||||
* Find the first subpass where the given image is used as a render target.
|
||||
* @param history the usage history of the image; what it was used at at each subpass.
|
||||
* @return nullopt if the image was not used as a render target, the index of the subpass where it was, if not.
|
||||
*/
|
||||
std::optional<int> getFirstRenderTarget(const UsageTracker& history) const;
|
||||
|
||||
/**
|
||||
* Return the operations that should be used for the given image attachment.
|
||||
* If the user specified ops, it will be checekd against the history.
|
||||
* @param name the name of the image to use as the attachment
|
||||
* @param history the usage history of the attachment, for internal checks
|
||||
* @param userOps operations to use for the image, as an optional override.
|
||||
* @return the ColorOps to use for the given attachment.
|
||||
*/
|
||||
RenderPassBuilder::Attachment::OpsType getOps(const std::string& name, const UsageTracker& history, const std::optional<RenderPassBuilder::Attachment::OpsType>& userOps) const;
|
||||
|
||||
/**
|
||||
* Get the usage type of the image.
|
||||
* Assumption: an image is only ever used as a color OR depth stencil. Never both.
|
||||
* Assumption: Multisample == RenderTarget
|
||||
* @param name the name of the image to check
|
||||
* @param history the history of the image's usages in the GPU.
|
||||
* @return whether the image is a RenderTarget or a DepthStencil buffer.
|
||||
*/
|
||||
ImageUsage::Type getUsageType(const std::string& name, const UsageTracker& history) const;
|
||||
|
||||
/**
|
||||
* Ensure that the image is used as type at subpass in its' history.
|
||||
*/
|
||||
bool verifyImageUsage(const UsageTracker& history, int subpass, ImageUsage::Type type) const;
|
||||
|
||||
/**
|
||||
* Return whether the subpass is virtual.
|
||||
* For a Render Pass, virtual means it is a preprocessing step.
|
||||
*/
|
||||
bool isVirtual(int subpass) const {
|
||||
return subpass == getVirtualInitial() || subpass == getVirtualFinal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the subpass index; for virtual passes, it uses an EXTERNAL subpass.
|
||||
*/
|
||||
uint32_t checkSubpass(int subpass) const {
|
||||
return isVirtual(subpass) ? VK_SUBPASS_EXTERNAL : (uint32_t) subpass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the image's usages are compatible with a render pass.
|
||||
* For example, compute shader linear buffers cannot be used as render targets, etc.
|
||||
*/
|
||||
void verifyHistory(const std::string& image, const UsageTracker& history) const;
|
||||
|
||||
std::map<std::string, AttachmentMeta> metas;
|
||||
std::unique_ptr<vlkx::RenderPassBuilder> builder;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The Common Pass implementation for Compute Shaders.
|
||||
* That is, shaders that do not write to color buffers.
|
||||
* A subpass can execute multiple compute shaders unbarriered, which increases efficiency.
|
||||
* We still need to transition images between passes when necessary, hence the wrapper.
|
||||
*/
|
||||
class ComputePass : public CommonPass {
|
||||
public:
|
||||
|
||||
ComputePass(const ComputePass&) = delete;
|
||||
ComputePass& operator=(const ComputePass&) = delete;
|
||||
|
||||
#define fluent ComputePass&
|
||||
|
||||
/**
|
||||
* Add the given image as an attachment to the compute shader pass.
|
||||
* @param name the name of the image
|
||||
* @param history the usage history of the image
|
||||
* @return the ComputePass instance, for chaining.
|
||||
*/
|
||||
fluent add(std::string&& name, UsageTracker&& history);
|
||||
fluent add(const std::string& name, UsageTracker&& history) {
|
||||
return add(std::string(name), std::move(history));
|
||||
}
|
||||
|
||||
/**
|
||||
* Run computeOps, insert memory barriers to transition used images into the appropriate format.
|
||||
* Images must be a superset of all images that were called with add().
|
||||
* Compute_ops must be equal to the number of subpasses.
|
||||
* Commands must be recording.
|
||||
* @param commands the command buffer to write into.
|
||||
* @param queueFamily the family to use for inserting barriers
|
||||
* @param images the list of images that were used in the compute pass
|
||||
* @param computeOps the compute functions to upload to the GPU
|
||||
*/
|
||||
void execute(const VkCommandBuffer& commands, uint32_t queueFamily, const std::map<std::string, const VkImage*>& images, const std::vector<std::function<void()>>& computeOps) const;
|
||||
|
||||
/**
|
||||
* Insert a memory barrier, to transition the layout of the image from the previous to the curent.
|
||||
* The barrier is performed using the given queue family.
|
||||
* @param commands the command buffer to write into.
|
||||
* @param queueFamily the family to use for inserting barriers.
|
||||
* @param image the list of images that were used in the compute pass
|
||||
* @param prev the previous usage of the image; the state being transitioned from
|
||||
* @param current the new usage of the image; the state being transitioned to.
|
||||
*/
|
||||
void barrier(const VkCommandBuffer& commands, uint32_t queueFamily, const VkImage& image, const ImageUsage& prev, const ImageUsage& current) const;
|
||||
|
||||
/**
|
||||
* Verify whether the previous usages of the given image in its' history is compatible with a compute shader.
|
||||
* For example, a fragment shader output image is not compatible.
|
||||
* @param name the name of the image being checked
|
||||
* @param history the usage history of the image/
|
||||
*/
|
||||
void verify(const std::string& name, const UsageTracker& history) const;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <variant>
|
||||
#include <optional>
|
||||
#include "vlkx/vulkan/Tools.h"
|
||||
#include "vlkx/vulkan/abstraction/Image.h"
|
||||
|
||||
namespace vlkx {
|
||||
|
||||
/**
|
||||
* Gathers the operations that the GPU should perform when rendering to a framebuffer.
|
||||
* Subpasses and dependencies are to be configured automatically using the builder below.
|
||||
* RenderPass objects are disposable, and should be discarded when the framebuffer changes.
|
||||
*/
|
||||
class RenderPass {
|
||||
public:
|
||||
using RenderFunc = std::function<void(const VkCommandBuffer& buffer)>;
|
||||
|
||||
// Delete copy and move constructors to prevent the GPU getting confused with what we're trying to do
|
||||
RenderPass(const RenderPass&) = delete;
|
||||
RenderPass& operator=(const RenderPass&) = delete;
|
||||
~RenderPass();
|
||||
|
||||
RenderPass(int subpasses, VkRenderPass pass, std::vector<VkClearValue> clear, VkExtent2D ext, std::vector<VkFramebuffer> fbs, std::vector<int> attachs)
|
||||
: subpassCount(subpasses), renderPass(pass), clearValues(std::move(clear)), extent(ext), framebuffers(std::move(fbs)), attachments(std::move(attachs)) {}
|
||||
|
||||
const VkRenderPass& operator*() const { return renderPass; }
|
||||
|
||||
int getAttachsInSubpass(int subpass) const {
|
||||
return attachments[subpass];
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload all of the subpass render commands to the command buffer.
|
||||
* The size of ops must be equal to the number of subpasses in this render pass.
|
||||
* @param commands the command buffer to execute on; must be recording
|
||||
* @param imageIndex the index of the image on the swapchain that we're rendering to; the target framebuffer.
|
||||
* @param ops the render operations to add onto the command buffer.
|
||||
*/
|
||||
void execute(const VkCommandBuffer& commands, int imageIndex, std::vector<RenderFunc> ops) const;
|
||||
|
||||
private:
|
||||
// The number of sub-render-passes in this pass.
|
||||
const int subpassCount;
|
||||
// The VkRenderPass that this class wraps.
|
||||
VkRenderPass renderPass;
|
||||
// The clear values that will wipe all framebuffers to their empty states.
|
||||
const std::vector<VkClearValue> clearValues;
|
||||
// The size of the framebuffers (all are the same size)
|
||||
const VkExtent2D extent;
|
||||
// The framebuffers that we can render to
|
||||
const std::vector<VkFramebuffer> framebuffers;
|
||||
// The number of color attachments (sampled color images) in each subpass, by subpass index.
|
||||
const std::vector<int> attachments;
|
||||
};
|
||||
|
||||
/**
|
||||
* A stateful, fluent way to create Render Passes.
|
||||
* This object can be stored and reused; when the window size changes, simply set the extent and
|
||||
* export a new RenderPass to be used in the pipeline.
|
||||
*
|
||||
* Allows setting sub-passes, sub-pass dependencies, operations to read and write them, etc.
|
||||
*/
|
||||
class RenderPassBuilder {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Information required to define an attachment to be used in a render pass.
|
||||
* Contains information on the layout, the operations to use on read and write, etc.
|
||||
*/
|
||||
struct Attachment {
|
||||
// Operations to use on color attachments.
|
||||
struct ColorOps {
|
||||
VkAttachmentLoadOp LOAD; // Load data in the color attachment
|
||||
VkAttachmentStoreOp STORE; // Store data into the color attachment
|
||||
};
|
||||
|
||||
// Operations to use on depth and stencil buffers.
|
||||
struct StencilDepthOps {
|
||||
VkAttachmentLoadOp DEPTH_LOAD; // Load data in the depth attachment
|
||||
VkAttachmentStoreOp DEPTH_STORE; // Store data in the depth attachment
|
||||
VkAttachmentLoadOp STENCIL_LOAD; // Load data in the stencil attachment
|
||||
VkAttachmentStoreOp STENCIL_STORE; // Store data in the stencil attachment
|
||||
};
|
||||
|
||||
using OpsType = std::variant<ColorOps, StencilDepthOps>;
|
||||
|
||||
// The operations that can be performed on this attachment
|
||||
OpsType ops;
|
||||
// The initial layout of an image in this attachment (pre-GPU upload)
|
||||
VkImageLayout layoutInitial;
|
||||
// The final layout of an image in this attachment (as seen by the shader)
|
||||
VkImageLayout layoutFinal;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes attachments used in each subpass, in terms of Vulkan data.
|
||||
* Attachment here is used in the conceptual sense, not referring to the Attachment struct above.
|
||||
*
|
||||
* If multisampleReferences is non-zero, its' size must be equal to colorReferences' size.
|
||||
* Each index of multisampleReferences refers to the same-index colorReferences entry.
|
||||
*
|
||||
* If stencilDepthReference is non-zero, it is shared between all subpasses.
|
||||
*/
|
||||
struct SubpassAttachments {
|
||||
std::vector<VkAttachmentReference> colorReferences;
|
||||
std::vector<VkAttachmentReference> multisampleReferences;
|
||||
std::optional<VkAttachmentReference> stencilDepthReference;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the dependencies between each sub-pass.
|
||||
* It describes how each subpass will read or modify the data written by the last subpass, if at all.
|
||||
* This dependency information can allow the GPU to run some passes in parallel, and enforce the
|
||||
* strict ordering of those that require it.
|
||||
*/
|
||||
struct SubpassDependency {
|
||||
|
||||
/**
|
||||
* Defines some metadata about the subpass.
|
||||
* Contains the index of the subpass, how it will use data from the last pass, and what exactly it will do.
|
||||
*/
|
||||
struct SubpassMeta {
|
||||
/**
|
||||
* Index of the subpass.
|
||||
*/
|
||||
uint32_t index;
|
||||
|
||||
/**
|
||||
* Describes how we want to modify the data passed to us from the last subpass.
|
||||
* Will change how the next subpass will wait for the completion of this subpass, if at all.
|
||||
*
|
||||
* VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT:
|
||||
* read/write to the color attachment
|
||||
*
|
||||
* VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT:
|
||||
* VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT:
|
||||
* read/write to the depth or stencil attachment
|
||||
*
|
||||
* VK_PIPELINE_STAGE_VERTEX_SHADER_BIT:
|
||||
* VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT:
|
||||
* read all attachments
|
||||
*
|
||||
*/
|
||||
VkPipelineStageFlags stage;
|
||||
|
||||
/**
|
||||
* Describes how we want to synchronize with the subpass after this one.
|
||||
*
|
||||
* VK_ACCESS_SHADER_READ_BIT:
|
||||
* VK_ACCESS_SHADER_WRITE_BIT:
|
||||
* read a texture or write to a color buffer
|
||||
*
|
||||
* VK_ACCESS_COLOR_ATTACHMENT_READ_BIT:
|
||||
* VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT:
|
||||
* VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT:
|
||||
* VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT:
|
||||
* read/write to an attachment.
|
||||
*
|
||||
* VK_ACCESS_INPUT_ATTACHMENT_READ_BIT:
|
||||
* for accessing the inputAttachment of a subpass.
|
||||
*
|
||||
* 0:
|
||||
* do not write, but the next subpass will.
|
||||
* Will automatically barrier the render pass.
|
||||
*/
|
||||
VkAccessFlags access;
|
||||
};
|
||||
|
||||
SubpassMeta source; // The source subpass of this dependency (will take effect after this pass completes)
|
||||
SubpassMeta destination; // The destination subpass of this dependency (will take effect before this pass)
|
||||
VkDependencyFlags flags; // Other information that Vulkan needs to know about this dependency; for example, if we use an inputAttachment.
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes a color attachment.
|
||||
* Effectively RGB images that live entirely on the GPU.
|
||||
*
|
||||
* Describes the resolution mechanics of a multisampled image.
|
||||
*/
|
||||
struct ColorAttachmentMeta {
|
||||
int location; // Where the GPU shaders expect this attachment to be available.
|
||||
int descriptionIdx; // Index of this attachment in the VkAttachmentDescription data.
|
||||
VkImageLayout layout; // Vulkan image layout. Shader optimized or host readable.
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a list of VkAttachmentReference that describes the attachments used per subpass.
|
||||
*/
|
||||
static std::vector<VkAttachmentReference> parseColorReferences(std::vector<ColorAttachmentMeta> meta);
|
||||
|
||||
/**
|
||||
* Create a list of VkAttachmentReference that describes the multisampling setup.
|
||||
*/
|
||||
static std::vector<VkAttachmentReference> parseMutisampling(int colorReferencesCount, std::vector<ColorAttachmentMeta> meta);
|
||||
|
||||
RenderPassBuilder(const RenderPassBuilder&) = delete;
|
||||
RenderPassBuilder& operator=(const RenderPassBuilder&) = delete;
|
||||
~RenderPassBuilder() = default;
|
||||
RenderPassBuilder() = default;
|
||||
|
||||
/** Fluent API Features; chain calls to set data on the render pass.*/
|
||||
#define fluent RenderPassBuilder&
|
||||
|
||||
// Set the number of framebuffers in the render pass
|
||||
fluent setFramebufferCount(int count);
|
||||
// Set an attachment description in the render pass
|
||||
fluent setAttachment(int idx, const Attachment& attachment);
|
||||
// Update the image backing an attachment. The function must be executable during execute() later on.
|
||||
fluent updateAttachmentBacking(int idx, std::function<const Image&(int idx)>&& getBacking);
|
||||
// Set a specific subpass. Use the static parse methods to create these vectors.
|
||||
fluent setSubpass(int idx, std::vector<VkAttachmentReference>&& color, std::vector<VkAttachmentReference>&& multisample, VkAttachmentReference& depthStencil);
|
||||
// Add a dependency between two subpasses.
|
||||
fluent addDependency(const SubpassDependency& dep);
|
||||
|
||||
// Build the Render Pass with all the information given.
|
||||
// Can be called multiple times with the same Builder.
|
||||
[[nodiscard]] std::unique_ptr<vlkx::RenderPass> build() const;
|
||||
|
||||
private:
|
||||
// Number of framebuffers in the render pass
|
||||
std::optional<int> framebufferCount;
|
||||
// Descriptions of used attachments
|
||||
std::vector<VkAttachmentDescription> attachmentDescriptors;
|
||||
// Functions to return attachment images.
|
||||
std::vector<std::function<const Image&(int idx)>> attachmentGetters;
|
||||
// Values to clear all attachments
|
||||
std::vector<VkClearValue> clearValues;
|
||||
// Descriptions of subpasses.
|
||||
std::vector<SubpassAttachments> subpassAttachments;
|
||||
// Descriptions of subpass dependencies.
|
||||
std::vector<VkSubpassDependency> subpassDependencies;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include "GenericRenderPass.h"
|
||||
#include "vlkx/vulkan/abstraction/ImageUsage.h"
|
||||
#include "vlkx/vulkan/abstraction/Image.h"
|
||||
|
||||
namespace vlkx {
|
||||
|
||||
// A simple and versatile way to configure render passes.
|
||||
// Intended to be used with the SimpleRenderPass and the ScreenRenderPass.
|
||||
class RendererConfig {
|
||||
public:
|
||||
RendererConfig(std::vector<std::unique_ptr<vlkx::Image>>& destinations, bool toScreen = true) : renderImages(destinations) {
|
||||
numOpaquePasses = 1;
|
||||
rendersToScreen = toScreen;
|
||||
}
|
||||
|
||||
RendererConfig(int passCount, std::vector<std::unique_ptr<vlkx::Image>>& destinations, bool toScreen = true, std::optional<int> firstTransparent = std::nullopt, std::optional<int> firstOverlay = std::nullopt);
|
||||
|
||||
// Get the number of passes that use the depth buffer.
|
||||
int depthPasses() const {
|
||||
return numOpaquePasses + numTransparentPasses;
|
||||
}
|
||||
|
||||
// Get the total number of passes.
|
||||
int passes() const {
|
||||
return depthPasses() + numOverlayPasses;
|
||||
}
|
||||
|
||||
// Get whether any passes use the depth buffer.
|
||||
bool usesDepth() const {
|
||||
return depthPasses() > 0;
|
||||
}
|
||||
|
||||
// Create the render pass builder. Can be called multiple times.
|
||||
void build();
|
||||
|
||||
RendererConfig(RendererConfig&) noexcept = default;
|
||||
RendererConfig(const RendererConfig&) = default;
|
||||
|
||||
int numOpaquePasses = 0;
|
||||
int numTransparentPasses = 0;
|
||||
std::vector<std::unique_ptr<vlkx::Image>>& renderImages;
|
||||
bool rendersToScreen;
|
||||
private:
|
||||
int numOverlayPasses = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores all of the information required to use an attachment.
|
||||
* This is heavy, so is only used when the image is being finalized.
|
||||
*/
|
||||
struct AttachmentConfig {
|
||||
AttachmentConfig(std::string_view name, std::optional<int>* index)
|
||||
: name(name), index(*index) {}
|
||||
|
||||
AttachmentConfig& setOps(const RenderPassBuilder::Attachment::OpsType& ops) {
|
||||
loadStoreOps = ops;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AttachmentConfig& setUsage(const ImageUsage& final) {
|
||||
finalUsage = final;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
std::optional<int>& index;
|
||||
std::optional<RenderPassBuilder::Attachment::OpsType> loadStoreOps;
|
||||
std::optional<ImageUsage> finalUsage;
|
||||
};
|
||||
|
||||
/**
|
||||
* A lighter version of the Attachment, used primarily in the On-Screen Pass Manager.
|
||||
*/
|
||||
struct Attachment {
|
||||
explicit Attachment(std::string_view image) : name(image) {}
|
||||
|
||||
// Adds the image to the tracker and initializes state.
|
||||
void add(MultiImageTracker& tracker, const Image& image) {
|
||||
tracker.track(name, image.getUsage());
|
||||
}
|
||||
|
||||
AttachmentConfig makeConfig() { return { name, &index }; }
|
||||
|
||||
const std::string name;
|
||||
std::optional<int> index;
|
||||
};
|
||||
|
||||
// Manages Render Passes that will output to the screen.
|
||||
// This is necessarily exclusively a graphical pass.
|
||||
// If necessary, a depth and stencil buffer will be maintained.
|
||||
// The color buffer is automatically assumed to be the swapchain.
|
||||
class ScreenRenderPassManager {
|
||||
public:
|
||||
explicit ScreenRenderPassManager(RendererConfig renderConfig) : config(renderConfig) {}
|
||||
|
||||
ScreenRenderPassManager(const ScreenRenderPassManager&) = delete;
|
||||
ScreenRenderPassManager& operator=(const ScreenRenderPassManager&) = delete;
|
||||
|
||||
// Initialize the render pass we're managing.
|
||||
void initializeRenderPass();
|
||||
|
||||
std::unique_ptr<vlkx::RenderPass>& getPass() { return pass; }
|
||||
|
||||
private:
|
||||
|
||||
// Prepare the Render Pass builder.
|
||||
void preparePassBuilder();
|
||||
|
||||
const RendererConfig config;
|
||||
|
||||
Attachment destinationInfo { "Destination" };
|
||||
Attachment multisampleInfo { "Multisample" };
|
||||
Attachment depthStencilInfo { "Depth-Stencil" };
|
||||
|
||||
std::unique_ptr<vlkx::Image> depthStencilImage;
|
||||
std::unique_ptr<vlkx::RenderPassBuilder> passBuilder;
|
||||
std::unique_ptr<vlkx::RenderPass> pass;
|
||||
};
|
||||
|
||||
/**
|
||||
* A utility namespace used to house a constructor for creating a "simple" render pass with all the defaults.
|
||||
*/
|
||||
namespace SimpleRenderPass {
|
||||
static std::unique_ptr<RenderPassBuilder> createBuilder(int framebuffers, const RendererConfig& config, const AttachmentConfig& color, const AttachmentConfig* multisample, const AttachmentConfig* depthStencil, MultiImageTracker& tracker);
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
#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);
|
||||
|
||||
};
|
|
@ -1,37 +0,0 @@
|
|||
#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();
|
||||
|
||||
};
|
|
@ -1,31 +1,205 @@
|
|||
#pragma once
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <fstream>
|
||||
#include "shadow/util/RefCounter.h"
|
||||
#include "vlkx/vulkan/VulkanModule.h"
|
||||
|
||||
class Pipeline {
|
||||
public:
|
||||
Pipeline();
|
||||
~Pipeline();
|
||||
namespace vlkx {
|
||||
class Pipeline;
|
||||
|
||||
// The active Graphics Pipeline layout.
|
||||
VkPipelineLayout layout;
|
||||
// The active Graphics Pipeline instance.
|
||||
VkPipeline pipeline;
|
||||
// A simple wrapper for the shader module used by the pipeline.
|
||||
class ShaderModule {
|
||||
public:
|
||||
using CountedShader = shadowutil::RefCounter<ShaderModule>;
|
||||
using ReleasePool = CountedShader::AutoRelease;
|
||||
|
||||
// Create the layout and pipeline for a vertex renderer
|
||||
void create(VkExtent2D extent, VkDescriptorSetLayout set, VkRenderPass renderPass);
|
||||
ShaderModule(const std::string& path);
|
||||
|
||||
void destroy();
|
||||
ShaderModule(const ShaderModule&) = delete;
|
||||
ShaderModule& operator=(const ShaderModule&) = delete;
|
||||
|
||||
private:
|
||||
~ShaderModule() {
|
||||
vkDestroyShaderModule(VulkanModule::getInstance()->getDevice()->logical, shader, nullptr);
|
||||
}
|
||||
|
||||
std::vector<char> readFile(const std::string& filename);
|
||||
VkShaderModule createShaderModule(const std::vector<char>& code);
|
||||
const VkShaderModule& operator*() const { return shader; }
|
||||
|
||||
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);
|
||||
};
|
||||
private:
|
||||
VkShaderModule shader;
|
||||
};
|
||||
|
||||
class PipelineBuilder {
|
||||
public:
|
||||
PipelineBuilder(const PipelineBuilder&) = delete;
|
||||
PipelineBuilder& operator=(const PipelineBuilder&) = delete;
|
||||
|
||||
virtual ~PipelineBuilder() {
|
||||
vkDestroyPipelineCache(VulkanModule::getInstance()->getDevice()->logical, cache, nullptr);
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<Pipeline> build() const = 0;
|
||||
|
||||
protected:
|
||||
PipelineBuilder(std::optional<int> maxCache);
|
||||
|
||||
void setName(std::string&& n) { name = std::move(name); }
|
||||
|
||||
void setLayout(std::vector<VkDescriptorSetLayout>&& descLayouts, std::vector<VkPushConstantRange>&& constants);
|
||||
|
||||
const std::string& getName() const { return name; }
|
||||
|
||||
bool hasLayout() const { return layoutInfo.has_value(); }
|
||||
const VkPipelineLayoutCreateInfo& getLayout() const { return layoutInfo.value(); }
|
||||
|
||||
private:
|
||||
VkPipelineCache cache;
|
||||
std::string name;
|
||||
|
||||
std::optional<VkPipelineLayoutCreateInfo> layoutInfo;
|
||||
std::vector<VkDescriptorSetLayout> descLayouts;
|
||||
std::vector<VkPushConstantRange> constants;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use when creating Graphics pipelines.
|
||||
* Internal state is preserved so that multiple pipelines can be created with one builder.
|
||||
* However, shaders are single-usage. Bind a new shader before claling build again.
|
||||
* See ShaderModule for more information, and how to change this.
|
||||
*/
|
||||
class GraphicsPipelineBuilder : public PipelineBuilder {
|
||||
public:
|
||||
struct Viewport {
|
||||
VkViewport viewport;
|
||||
VkRect2D scissor;
|
||||
};
|
||||
|
||||
explicit GraphicsPipelineBuilder(std::optional<int> maxCache = std::nullopt);
|
||||
|
||||
GraphicsPipelineBuilder(const GraphicsPipelineBuilder&) = delete;
|
||||
GraphicsPipelineBuilder& operator=(const GraphicsPipelineBuilder&) = delete;
|
||||
|
||||
#define fluent GraphicsPipelineBuilder&
|
||||
|
||||
fluent name(std::string&& name);
|
||||
fluent depthTest(bool enable, bool write);
|
||||
fluent stencilTest(bool enable);
|
||||
fluent multiSample(VkSampleCountFlagBits samples);
|
||||
fluent topology(VkPrimitiveTopology topology);
|
||||
fluent stencilOp(const VkStencilOpState& state, VkStencilFaceFlags flags);
|
||||
|
||||
fluent addVertex(uint32_t bindPoint, VkVertexInputBindingDescription&& desc, std::vector<VkVertexInputAttributeDescription>&& attrs);
|
||||
fluent layout(std::vector<VkDescriptorSetLayout>&& descLayouts, std::vector<VkPushConstantRange>&& constants);
|
||||
fluent viewport(const Viewport& port, bool flipY = true);
|
||||
fluent renderPass(const VkRenderPass& pass, uint32_t subpass);
|
||||
|
||||
fluent colorBlend(std::vector<VkPipelineColorBlendAttachmentState>&& states);
|
||||
fluent shader(VkShaderStageFlagBits stage, std::string&& file);
|
||||
|
||||
std::unique_ptr<Pipeline> build() const override;
|
||||
|
||||
private:
|
||||
struct PassInfo {
|
||||
VkRenderPass pass;
|
||||
uint32_t subpass;
|
||||
};
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo assemblyInfo;
|
||||
VkPipelineRasterizationStateCreateInfo rasterizationInfo;
|
||||
VkPipelineMultisampleStateCreateInfo multisampleInfo;
|
||||
VkPipelineDepthStencilStateCreateInfo depthStencilInfo;
|
||||
VkPipelineDynamicStateCreateInfo dynamicStateInfo;
|
||||
|
||||
std::vector<VkVertexInputBindingDescription> bindingDescs;
|
||||
std::vector<VkVertexInputAttributeDescription> attrDescs;
|
||||
|
||||
std::optional<Viewport> viewportMeta;
|
||||
std::optional<PassInfo> passMeta;
|
||||
std::vector<VkPipelineColorBlendAttachmentState> blendStates;
|
||||
std::map<VkShaderStageFlagBits, std::string> shaders;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use when creating Compute Shader pipelines.
|
||||
* Internal state is preserved so that multiple pipelines can be created with one builder.
|
||||
* However, shaders are single-usage. Bind a new shader before claling build again.
|
||||
* See ShaderModule for more information, and how to change this.
|
||||
*/
|
||||
class ComputePipelineBuilder : public PipelineBuilder {
|
||||
public:
|
||||
explicit ComputePipelineBuilder(std::optional<int> maxCache = std::nullopt) : PipelineBuilder(maxCache) {}
|
||||
|
||||
ComputePipelineBuilder(const ComputePipelineBuilder&) = delete;
|
||||
ComputePipelineBuilder& operator=(const ComputePipelineBuilder&) = delete;
|
||||
|
||||
#define fluent ComputePipelineBuilder&
|
||||
|
||||
fluent name(std::string&& name);
|
||||
fluent layout(std::vector<VkDescriptorSetLayout>&& descLayouts, std::vector<VkPushConstantRange>&& pushConstants);
|
||||
fluent shader(std::string&& path);
|
||||
|
||||
std::unique_ptr<Pipeline> build() const override;
|
||||
|
||||
private:
|
||||
std::optional<std::string> shaderPath;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pipeline configures:
|
||||
* - Shader Stages
|
||||
* - Fixed Function stages
|
||||
* - Vertex input bindings
|
||||
* - Vertex attributes
|
||||
* - Assembly
|
||||
* - Tesselation
|
||||
* - Viewport and Scissor
|
||||
* - Rasterization
|
||||
* - Multisampling
|
||||
* - Depth testing
|
||||
* - Stencil testing
|
||||
* - Color blending
|
||||
* - Dynamic states
|
||||
* - Pipeline layout
|
||||
* - Descriptor set layout
|
||||
* - Push constant ranges
|
||||
*
|
||||
* Create a Pipeline with one of the builders above.
|
||||
*/
|
||||
class Pipeline {
|
||||
public:
|
||||
Pipeline(const Pipeline&) = delete;
|
||||
Pipeline& operator=(const Pipeline&) = delete;
|
||||
|
||||
~Pipeline();
|
||||
|
||||
void bind(const VkCommandBuffer& buffer) const;
|
||||
|
||||
const VkPipeline& operator*() const { return pipeline; }
|
||||
const VkPipelineLayout& getLayout() const { return layout; }
|
||||
VkPipelineBindPoint getBind() const { return bindPoint; }
|
||||
|
||||
static VkPipelineColorBlendAttachmentState getAlphaBlendState(bool blending) {
|
||||
return {
|
||||
blending, VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
||||
VK_BLEND_OP_ADD, VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
||||
VK_BLEND_OP_ADD, VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
friend std::unique_ptr<Pipeline> GraphicsPipelineBuilder::build() const;
|
||||
friend std::unique_ptr<Pipeline> ComputePipelineBuilder::build() const;
|
||||
|
||||
Pipeline(std::string name, const VkPipeline& line, const VkPipelineLayout& lay, VkPipelineBindPoint bPoint)
|
||||
: name(std::move(name)), pipeline(line), layout(lay), bindPoint(bPoint) {}
|
||||
|
||||
const std::string name;
|
||||
// The active Pipeline layout.
|
||||
const VkPipelineLayout layout;
|
||||
// The active Pipeline instance.
|
||||
const VkPipeline pipeline;
|
||||
// Whether this is a graphics or compute pipeline
|
||||
const VkPipelineBindPoint bindPoint;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
#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;
|
||||
};
|
|
@ -1,23 +0,0 @@
|
|||
#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();
|
||||
};
|
|
@ -4,6 +4,7 @@
|
|||
#include <vector>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include "vlkx/vulkan/abstraction/Image.h"
|
||||
|
||||
class SwapChain {
|
||||
public:
|
||||
|
@ -14,7 +15,8 @@ public:
|
|||
VkFormat format;
|
||||
VkExtent2D extent;
|
||||
|
||||
std::vector<VkImage> images;
|
||||
std::vector<std::unique_ptr<vlkx::Image>> images;
|
||||
std::unique_ptr<vlkx::Image> multisampleImg;
|
||||
|
||||
VkSurfaceFormatKHR chooseFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
|
||||
VkPresentModeKHR chooseMode(const std::vector<VkPresentModeKHR>& availableModes);
|
||||
|
|
|
@ -4,19 +4,12 @@
|
|||
#include <functional>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vlkx/vulkan/VulkanManager.h>
|
||||
|
||||
#include <vulkan/vk_mem_alloc.h>
|
||||
#include "VulkanDevice.h"
|
||||
#include "exports.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;
|
||||
|
@ -27,194 +20,20 @@ namespace VkTools {
|
|||
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, bool hostVisible = true);
|
||||
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);
|
||||
extern API VmaAllocator allocator;
|
||||
|
||||
ManagedImage createImage(VkFormat format, VkImageUsageFlags flags, VkExtent3D extent);
|
||||
|
||||
#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;
|
||||
VkSampler createSampler(VkFilter filters, VkSamplerAddressMode mode, uint32_t mipping, VkDevice device);
|
||||
|
||||
// Prepare the managed image
|
||||
ManagedImage image {};
|
||||
VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags, uint32_t mipping, uint32_t layers,
|
||||
VkDevice device);
|
||||
|
||||
// Set up image allocation
|
||||
VmaAllocationCreateInfo allocateInfo = {};
|
||||
allocateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
||||
ManagedBuffer createGPUBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties,
|
||||
VkDevice logicalDevice, VkPhysicalDevice physicalDevice, bool hostVisible = true);
|
||||
|
||||
// Allocate + create the image
|
||||
vmaCreateImage(g_allocator, &info, &allocateInfo, &image.image, &image.allocation, nullptr);
|
||||
void immediateExecute(const std::function<void(const VkCommandBuffer &)> &execute, VulkanDevice *dev);
|
||||
|
||||
return image;
|
||||
void copyGPUBuffer(VkBuffer source, VkBuffer dest, VkDeviceSize length, VulkanDevice *dev);
|
||||
|
||||
}
|
||||
|
||||
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, bool hostVisible) {
|
||||
// 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 = hostVisible ? VMA_MEMORY_USAGE_CPU_ONLY : VMA_MEMORY_USAGE_GPU_ONLY;
|
||||
vmaInfo.requiredFlags = properties;
|
||||
|
||||
// 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
|
||||
}
|
|
@ -11,7 +11,8 @@ public:
|
|||
~ValidationAndExtension();
|
||||
|
||||
const std::vector<const char*> requiredValidations = {
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
"VK_LAYER_KHRONOS_validation",
|
||||
//"VK_LAYER_LUNARG_api_dump"
|
||||
};
|
||||
|
||||
VkDebugReportCallbackEXT callback;
|
||||
|
|
|
@ -30,6 +30,7 @@ public:
|
|||
|
||||
/** Physical Devices **/
|
||||
VkPhysicalDevice physical;
|
||||
VkPhysicalDeviceLimits limits;
|
||||
SwapChainMeta swapChain;
|
||||
QueueFamilies queueData;
|
||||
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
#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;
|
||||
|
||||
};
|
|
@ -0,0 +1,99 @@
|
|||
#pragma once
|
||||
|
||||
#include <vlkx/vulkan/ValidationAndExtension.h>
|
||||
#include <vlkx/vulkan/VulkanDevice.h>
|
||||
|
||||
#include <vulkan/vk_mem_alloc.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include <SDL_vulkan.h>
|
||||
#include <core/Module.h>
|
||||
#include "SwapChain.h"
|
||||
|
||||
namespace vlkx { class ScreenRenderPassManager; }
|
||||
|
||||
class VulkanModule : public ShadowEngine::RendererModule {
|
||||
SHObject_Base(VulkanModule);
|
||||
public:
|
||||
|
||||
VulkanModule();
|
||||
~VulkanModule() override;
|
||||
|
||||
#ifdef _DEBUG
|
||||
static const bool validationRequired = true;
|
||||
#else
|
||||
static const bool validationRequired = false;
|
||||
#endif
|
||||
|
||||
void PreInit() override;
|
||||
|
||||
void Init() override;
|
||||
|
||||
void Recreate() override;
|
||||
|
||||
void Update(int frame) override;
|
||||
|
||||
void PreRender() override;
|
||||
|
||||
void Render(VkCommandBuffer& commands, int frame) override;
|
||||
|
||||
void OverlayRender() override;
|
||||
|
||||
void LateRender(VkCommandBuffer& commands, int frame) override;
|
||||
|
||||
void AfterFrameEnd() override;
|
||||
|
||||
void Destroy() override;
|
||||
|
||||
void Event(SDL_Event* e) override;
|
||||
|
||||
void BeginRenderPass(const std::unique_ptr<vlkx::RenderCommand>& commands) override;
|
||||
|
||||
void EnableEditor() override;
|
||||
|
||||
VkExtent2D GetRenderExtent() override;
|
||||
|
||||
// VulkanModule is a singleton class.
|
||||
static VulkanModule* instance;
|
||||
static VulkanModule* 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();
|
||||
|
||||
VkInstance getVulkan() { return vulkan; }
|
||||
VulkanDevice* getDevice() { return device; }
|
||||
SwapChain* getSwapchain() { return swapchain; }
|
||||
VmaAllocator getAllocator() { return allocator; }
|
||||
SDL_Window* getWind() { return wnd; }
|
||||
const std::unique_ptr<vlkx::ScreenRenderPassManager>& getRenderPass();
|
||||
|
||||
|
||||
private:
|
||||
bool editorEnabled = false;
|
||||
std::vector<VkDescriptorSet> editorRenderPlanes;
|
||||
std::vector<std::unique_ptr<vlkx::Image>> editorContentFrames;
|
||||
|
||||
// The SDL Window contains the size of the drawable area.
|
||||
SDL_Window* wnd;
|
||||
// To handle the validation of Vulkan API usage
|
||||
ValidationAndExtension* validators{};
|
||||
// To manage interaction with the hardware
|
||||
VulkanDevice* device{};
|
||||
// To handle the framebuffers
|
||||
SwapChain* swapchain{};
|
||||
// To handle automatic management of memory.
|
||||
VmaAllocator allocator{};
|
||||
// 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{};
|
||||
|
||||
};
|
|
@ -0,0 +1,412 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vlkx/vulkan/VulkanDevice.h>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include "vlkx/vulkan/Tools.h"
|
||||
|
||||
namespace vlkx {
|
||||
// Root buffer class.
|
||||
// Used to store & move data between the CPU and GPU.
|
||||
// Basically; hotspot!
|
||||
// Utilities and subclasses exist to optimise and speed up transfer and management of data in bulk.
|
||||
class Buffer {
|
||||
public:
|
||||
// Metadata of CPU->GPU data copying.
|
||||
struct CopyMeta {
|
||||
const void* data; // The start of data in RAM
|
||||
VkDeviceSize length; // The amount of data to move
|
||||
VkDeviceSize start; // The start (destination) in GPU memory
|
||||
};
|
||||
|
||||
// Metadata of bulk CPU->GPU data copying.
|
||||
struct BulkCopyMeta {
|
||||
VkDeviceSize length; // The total data size of all transfers.
|
||||
std::vector<CopyMeta> metas;
|
||||
};
|
||||
|
||||
Buffer(const Buffer&) = delete;
|
||||
Buffer& operator=(const Buffer&) = delete;
|
||||
|
||||
Buffer();
|
||||
|
||||
virtual ~Buffer() {
|
||||
}
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
/*******************************************/
|
||||
/** */
|
||||
/** START OF DATA BUFFERS */
|
||||
/** */
|
||||
/*******************************************/
|
||||
|
||||
// A buffer that stores data on GPU.
|
||||
// Usage of the data is determined by child classes.
|
||||
class DataBuffer : public Buffer {
|
||||
public:
|
||||
DataBuffer(const DataBuffer&) = delete;
|
||||
DataBuffer& operator=(const DataBuffer&) = delete;
|
||||
|
||||
DataBuffer() = default;
|
||||
|
||||
~DataBuffer() override {
|
||||
vmaDestroyBuffer(VkTools::allocator, managed.buffer, managed.allocation);
|
||||
}
|
||||
|
||||
protected:
|
||||
using Buffer::Buffer;
|
||||
|
||||
void setBuffer(const VkTools::ManagedBuffer& buffer) { managed = buffer; }
|
||||
VkTools::ManagedBuffer get() const { return managed; }
|
||||
const VkBuffer& getBuffer() const { return managed.buffer; }
|
||||
|
||||
private:
|
||||
VkTools::ManagedBuffer managed;
|
||||
};
|
||||
|
||||
// A buffer visible to both GPU and CPU.
|
||||
// Useful for uploading data to GPU for format conversions.
|
||||
class StagingBuffer : public DataBuffer {
|
||||
public:
|
||||
StagingBuffer(const BulkCopyMeta& copyMeta);
|
||||
|
||||
StagingBuffer(const StagingBuffer&) = delete;
|
||||
StagingBuffer& operator=(const StagingBuffer&) = delete;
|
||||
|
||||
void copy(const VkBuffer& target) const;
|
||||
|
||||
private:
|
||||
const VkDeviceSize dataSize;
|
||||
};
|
||||
|
||||
// Root class of vertex buffers.
|
||||
// Provides utilities for subclasses.
|
||||
class VertexBuffer : public DataBuffer {
|
||||
public:
|
||||
VertexBuffer(const VertexBuffer&) = delete;
|
||||
VertexBuffer& operator=(const VertexBuffer&) = delete;
|
||||
|
||||
// Get attributes of vertexes in the buffer
|
||||
// Location will start from "start"
|
||||
// Binding will not be set
|
||||
std::vector<VkVertexInputAttributeDescription> getAttrs(uint32_t start) const;
|
||||
|
||||
// Draw these vertexes without a buffer per vertex.
|
||||
static void draw(const VkCommandBuffer& buffer, uint32_t verts, uint32_t instances);
|
||||
|
||||
protected:
|
||||
friend class DynamicBuffer;
|
||||
|
||||
explicit VertexBuffer(std::vector<VkVertexInputAttributeDescription>&& attrs) : DataBuffer(), attributes(attrs) {}
|
||||
|
||||
// Initialize device memory and the managed buffer.
|
||||
// indices and vertexes are put in the same buffer.
|
||||
// if dynamic, the buffer will be host visible, this allows dynamic text.
|
||||
// otherwise, the buffer is device local.
|
||||
void create(VkDeviceSize totalSize, bool dynamic, bool indexes);
|
||||
|
||||
const std::vector<VkVertexInputAttributeDescription> attributes;
|
||||
};
|
||||
|
||||
/*******************************************/
|
||||
/** */
|
||||
/** END OF DATA BUFFERS */
|
||||
/** */
|
||||
/*******************************************/
|
||||
|
||||
// A simple plugin to allow the vertex buffer to be widened when reserved with a larger size.
|
||||
class DynamicBuffer {
|
||||
public:
|
||||
DynamicBuffer(const DynamicBuffer&) = delete;
|
||||
DynamicBuffer& operator=(const DynamicBuffer&) = delete;
|
||||
|
||||
~DynamicBuffer() = default;
|
||||
|
||||
protected:
|
||||
|
||||
DynamicBuffer(size_t size, bool hasIndices, VertexBuffer* buffer);
|
||||
|
||||
// Reallocate the vertex buffer if the given pSize is larger than the available space
|
||||
void resize(size_t pSize);
|
||||
|
||||
VkDeviceSize bufferSize() const { return size; }
|
||||
|
||||
private:
|
||||
|
||||
const bool hasIndices;
|
||||
VertexBuffer* vertexBuffer;
|
||||
VkDeviceSize size = 0;
|
||||
};
|
||||
|
||||
/*******************************************/
|
||||
/** */
|
||||
/** START OF PER-VERTEX BUFFERS */
|
||||
/** */
|
||||
/*******************************************/
|
||||
|
||||
// Root class of buffers that store per-vertex data.
|
||||
// eg. shader data
|
||||
class PerVertexBuffer : public VertexBuffer {
|
||||
public:
|
||||
// Interprets the layout of data in containers (vector, etc)
|
||||
struct VertexDataMeta {
|
||||
template <typename C>
|
||||
VertexDataMeta(const C& cont, int unitsPerMesh) : data(cont.data()), unitsPerMesh(unitsPerMesh), sizePerMesh(sizeof(cont[0]) * unitsPerMesh) {}
|
||||
|
||||
template <typename C>
|
||||
VertexDataMeta(const C& cont) : VertexDataMeta(cont, static_cast<int>(cont.size())) {}
|
||||
|
||||
const void* data;
|
||||
int unitsPerMesh;
|
||||
size_t sizePerMesh;
|
||||
};
|
||||
|
||||
// Interface for buffer data.
|
||||
class BufferDataMeta {
|
||||
public:
|
||||
virtual ~BufferDataMeta() = default;
|
||||
// Populate info and return a bulk copy meta for copying the data to device
|
||||
virtual BulkCopyMeta prepareCopy(PerVertexBuffer* buffer) const = 0;
|
||||
// Indicate whether the buffer contains index data too
|
||||
virtual bool hasIndices() const = 0;
|
||||
};
|
||||
|
||||
// Meshes do not share indices, with variable vertices.
|
||||
class NoIndexBufferMeta : public BufferDataMeta {
|
||||
public:
|
||||
explicit NoIndexBufferMeta(std::vector<VertexDataMeta>&& perVertex) : perMeshVertices(perVertex) {}
|
||||
|
||||
BulkCopyMeta prepareCopy(PerVertexBuffer* buffer) const override;
|
||||
bool hasIndices() const override { return false; };
|
||||
private:
|
||||
const std::vector<VertexDataMeta> perMeshVertices;
|
||||
};
|
||||
|
||||
// Meshes share indices, with static vertices.
|
||||
class SharedIndexMeta : public BufferDataMeta {
|
||||
public:
|
||||
SharedIndexMeta(int meshes, const VertexDataMeta& perVertex, const VertexDataMeta& sharedIndices) : meshes(meshes),
|
||||
perMeshVertex(perVertex),
|
||||
sharedIndices(sharedIndices) {}
|
||||
BulkCopyMeta prepareCopy(PerVertexBuffer* buffer) const override;
|
||||
bool hasIndices() const override { return true; };
|
||||
private:
|
||||
const int meshes;
|
||||
const VertexDataMeta perMeshVertex;
|
||||
const VertexDataMeta sharedIndices;
|
||||
};
|
||||
|
||||
// Meshes do not share indexes, with variable indices and vertices.
|
||||
class NoShareMeta : public BufferDataMeta {
|
||||
public:
|
||||
struct PerMesh {
|
||||
VertexDataMeta indices;
|
||||
VertexDataMeta vertices;
|
||||
};
|
||||
|
||||
explicit NoShareMeta(std::vector<PerMesh>&& perMesh) : perMeshMeta(std::move(perMesh)) {}
|
||||
|
||||
BulkCopyMeta prepareCopy(PerVertexBuffer* buffer) const override;
|
||||
bool hasIndices() const override { return true; };
|
||||
private:
|
||||
const std::vector<PerMesh> perMeshMeta;
|
||||
};
|
||||
|
||||
PerVertexBuffer(const PerVertexBuffer&) = delete;
|
||||
PerVertexBuffer& operator=(const PerVertexBuffer&) = delete;
|
||||
|
||||
// Render mesh a given number of times, into a recording buffer.
|
||||
void draw(const VkCommandBuffer& buffer, uint32_t bind, int index, uint32_t instances) const;
|
||||
|
||||
protected:
|
||||
using VertexBuffer::VertexBuffer;
|
||||
|
||||
// Stores vertex data for buffers without indices
|
||||
struct MeshDataNoIndex {
|
||||
struct Info {
|
||||
uint32_t vertexCount;
|
||||
VkDeviceSize vertexStart;
|
||||
};
|
||||
|
||||
std::vector<Info> info;
|
||||
};
|
||||
|
||||
// Stores vertex and index data for buffers with both
|
||||
struct MeshDataIndex {
|
||||
struct Info {
|
||||
uint32_t indexCount;
|
||||
VkDeviceSize indexStart;
|
||||
VkDeviceSize vertexStart;
|
||||
};
|
||||
|
||||
std::vector<Info> info;
|
||||
};
|
||||
|
||||
std::variant<MeshDataNoIndex, MeshDataIndex>* getInfo() { return &meshDataInfo; }
|
||||
|
||||
private:
|
||||
|
||||
std::variant<MeshDataNoIndex, MeshDataIndex> meshDataInfo;
|
||||
};
|
||||
|
||||
// Stores static data for one-time upload.
|
||||
class StaticPerVertexBuffer : public PerVertexBuffer {
|
||||
public:
|
||||
StaticPerVertexBuffer(const BufferDataMeta& info, std::vector<VkVertexInputAttributeDescription>&& attrs);
|
||||
|
||||
StaticPerVertexBuffer(const StaticPerVertexBuffer&) = delete;
|
||||
StaticPerVertexBuffer& operator=(const StaticPerVertexBuffer&) = delete;
|
||||
};
|
||||
|
||||
// Stores host-visible data that can be reallocated.
|
||||
class DynamicPerVertexBuffer : public PerVertexBuffer, public DynamicBuffer {
|
||||
public:
|
||||
DynamicPerVertexBuffer(size_t size, std::vector<VkVertexInputAttributeDescription>&& attrs) : PerVertexBuffer(std::move(attrs)), DynamicBuffer(size, true, this) {}
|
||||
|
||||
DynamicPerVertexBuffer(const DynamicPerVertexBuffer&) = delete;
|
||||
DynamicPerVertexBuffer& operator=(const DynamicPerVertexBuffer&) = delete;
|
||||
|
||||
void copyToDevice(const BufferDataMeta& meta);
|
||||
};
|
||||
|
||||
/*******************************************/
|
||||
/** */
|
||||
/** END OF PER-VERTEX BUFFERS */
|
||||
/** */
|
||||
/*******************************************/
|
||||
|
||||
// Root class of buffers that store vertex data per instance of a mesh.
|
||||
class PerInstanceVertexBuffer : public VertexBuffer {
|
||||
public:
|
||||
PerInstanceVertexBuffer(const PerInstanceVertexBuffer&) = delete;
|
||||
PerInstanceVertexBuffer& operator=(const PerInstanceVertexBuffer&) = delete;
|
||||
|
||||
void bind(const VkCommandBuffer& commands, uint32_t bindPoint, int offset) const;
|
||||
|
||||
uint32_t getSize() const { return sizePerInstance; }
|
||||
|
||||
protected:
|
||||
PerInstanceVertexBuffer(uint32_t size, std::vector<VkVertexInputAttributeDescription>&& attrs) : VertexBuffer(std::move(attrs)), sizePerInstance(size) {}
|
||||
private:
|
||||
const uint32_t sizePerInstance;
|
||||
};
|
||||
|
||||
// Stores vertices that are static per instance of the mesh.
|
||||
class StaticPerInstanceBuffer : public PerInstanceVertexBuffer {
|
||||
public:
|
||||
StaticPerInstanceBuffer(uint32_t size, const void* data, uint32_t instances, std::vector<VkVertexInputAttributeDescription>&& attrs);
|
||||
|
||||
template <typename C>
|
||||
StaticPerInstanceBuffer(const C& cont, std::vector<VkVertexInputAttributeDescription>&& attrs) : StaticPerInstanceBuffer(sizeof(cont[0]), cont.data(), CONTAINER_SIZE(cont), std::move(attrs)) {}
|
||||
|
||||
StaticPerInstanceBuffer(const StaticPerInstanceBuffer&) = delete;
|
||||
StaticPerInstanceBuffer& operator=(const StaticPerInstanceBuffer&) = delete;
|
||||
};
|
||||
|
||||
// Stores vertices of meshes that are dynamic (ie. text, shifting meshes
|
||||
class DynamicPerInstanceBuffer : public PerInstanceVertexBuffer, public DynamicBuffer {
|
||||
public:
|
||||
DynamicPerInstanceBuffer(uint32_t size, size_t maxInstances, std::vector<VkVertexInputAttributeDescription>&& attrs) : PerInstanceVertexBuffer(size, std::move(attrs)), DynamicBuffer(size * maxInstances, false, this) {}
|
||||
|
||||
DynamicPerInstanceBuffer(const DynamicPerInstanceBuffer&) = delete;
|
||||
DynamicPerInstanceBuffer& operator=(const DynamicPerInstanceBuffer*) = delete;
|
||||
|
||||
void copyToDevice(const void* data, uint32_t instances);
|
||||
|
||||
template <typename C>
|
||||
void copyToDevice(const C& cont) {
|
||||
copyToDevice(cont.data(), CONTAINER_SIZE(cont));
|
||||
}
|
||||
};
|
||||
|
||||
/*******************************************/
|
||||
/** */
|
||||
/** END OF PER-INSTANCE BUFFERS */
|
||||
/** */
|
||||
/*******************************************/
|
||||
|
||||
// Holds uniform data on host and device.
|
||||
// Supports superallocating (allocating more than one "set" of uniforms at once)
|
||||
// Data is stored on host and device simultaneously, so set the host data and flush it to the device.
|
||||
class UniformBuffer : public DataBuffer {
|
||||
public:
|
||||
UniformBuffer(size_t chunkSize, int chunks);
|
||||
|
||||
UniformBuffer(const UniformBuffer&) = delete;
|
||||
UniformBuffer& operator=(const UniformBuffer&) = delete;
|
||||
|
||||
~UniformBuffer() override { delete data; }
|
||||
|
||||
// Whether this buffer holds a single chunk (is not superallocated).
|
||||
// Simplifies certain algorithms significantly if you know this beforehand
|
||||
bool isSingle() const { return numChunks == 1; }
|
||||
|
||||
// Get the data in the buffer, casted to the given type
|
||||
template <typename DataType>
|
||||
DataType* getData(int index) const {
|
||||
checkIndex(index);
|
||||
return reinterpret_cast<DataType*>(data + chunkSize * index);
|
||||
}
|
||||
|
||||
// Upload (flush) the uniform to the GPU
|
||||
void upload(int index) const;
|
||||
void upload(int index, VkDeviceSize dataSize, VkDeviceSize start) const;
|
||||
|
||||
static VkDescriptorType getDescriptorType() { return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; }
|
||||
|
||||
VkDescriptorBufferInfo getDescriptorInfo(int index) const;
|
||||
|
||||
private:
|
||||
void checkIndex(int index) const;
|
||||
|
||||
char* data;
|
||||
const size_t chunkSize;
|
||||
const int numChunks;
|
||||
|
||||
size_t chunkLength;
|
||||
};
|
||||
|
||||
// A small, highly efficient buffer much like the Uniform Buffer.
|
||||
class PushConstant {
|
||||
public:
|
||||
// Size must be < 128.
|
||||
PushConstant(size_t size, int numFrames);
|
||||
|
||||
PushConstant(const PushConstant&) = delete;
|
||||
PushConstant& operator=(const PushConstant&) = delete;
|
||||
|
||||
~PushConstant() { delete[] data; }
|
||||
|
||||
// Whether this buffer holds a single chunk (is not superallocated).
|
||||
// Simplifies certain algorithms significantly if you know this beforehand
|
||||
bool isSingle() const { return numFrames == 1; }
|
||||
|
||||
uint32_t getSize() const { return sizePerFrame; }
|
||||
|
||||
// Get the data in the buffer, casted to the given type
|
||||
template <typename DataType>
|
||||
DataType* getData(int frame) const {
|
||||
checkIndex(frame);
|
||||
return reinterpret_cast<DataType*>(data + (sizePerFrame * frame));
|
||||
}
|
||||
|
||||
VkPushConstantRange makeRange(VkShaderStageFlags stage) {
|
||||
return VkPushConstantRange { stage, 0, sizePerFrame };
|
||||
}
|
||||
|
||||
// Upload (flush) the uniform to the GPU
|
||||
void upload(const VkCommandBuffer& commands, const VkPipelineLayout& pipelineLayout, int frame, uint32_t offset, VkShaderStageFlags stage) const;
|
||||
|
||||
|
||||
private:
|
||||
void checkIndex(int index) const;
|
||||
|
||||
char* data;
|
||||
const uint32_t sizePerFrame;
|
||||
const int numFrames;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include "Queue.h"
|
||||
#include "vlkx/vulkan/VulkanDevice.h"
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
namespace vlkx {
|
||||
|
||||
// Root class of VkCommandBuffer wrappers.
|
||||
class CommandBuffer {
|
||||
public:
|
||||
using Command = std::function<void(const VkCommandBuffer& buffer)>;
|
||||
|
||||
CommandBuffer(const CommandBuffer&) = delete;
|
||||
CommandBuffer& operator=(const CommandBuffer&) = delete;
|
||||
|
||||
virtual ~CommandBuffer() {
|
||||
vkDestroyCommandPool(dev->logical, pool, nullptr);
|
||||
}
|
||||
protected:
|
||||
CommandBuffer();
|
||||
|
||||
void setPool(const VkCommandPool& newPool) { pool = newPool; }
|
||||
VulkanDevice* dev;
|
||||
private:
|
||||
VkCommandPool pool;
|
||||
};
|
||||
|
||||
// A command buffer that will be immediately executed.
|
||||
class ImmediateCommand : public CommandBuffer {
|
||||
public:
|
||||
ImmediateCommand(Queue queue);
|
||||
|
||||
ImmediateCommand(const ImmediateCommand&) = delete;
|
||||
ImmediateCommand& operator=(const ImmediateCommand&) = delete;
|
||||
|
||||
void run(const Command& cmd);
|
||||
|
||||
private:
|
||||
const Queue queue;
|
||||
VkCommandBuffer commands;
|
||||
};
|
||||
|
||||
// A command buffer that will be reused every frame.
|
||||
class RenderCommand : public CommandBuffer {
|
||||
public:
|
||||
using Command = std::function<void(const VkCommandBuffer& commands, uint32_t framebuffer)>;
|
||||
using Update = std::function<void(int frame)>;
|
||||
|
||||
~RenderCommand() override {
|
||||
// Destroy our own data
|
||||
vkDestroySemaphore(dev->logical, renderDoneSem, nullptr);
|
||||
vkDestroySemaphore(dev->logical, newImageSem, nullptr);
|
||||
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
vkDestroyFence(dev->logical, inFlight[i], nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
RenderCommand(int frames);
|
||||
|
||||
RenderCommand(const RenderCommand&) = delete;
|
||||
RenderCommand& operator=(const RenderCommand&) = delete;
|
||||
|
||||
uint32_t getFrame() { return imageIndex; }
|
||||
|
||||
void nextFrame() { imageIndex = (imageIndex + 1) % 2; }
|
||||
|
||||
std::optional<VkResult> execute(int frame, const VkSwapchainKHR& swapchain, const Update& update, const Command& cmd);
|
||||
// Renders a single frame out, no semaphores or fences.
|
||||
std::optional<VkResult> executeSimple(int frame, const Update& update, const Command& cmd);
|
||||
|
||||
private:
|
||||
std::vector<VkCommandBuffer> commands;
|
||||
// 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;
|
||||
|
||||
// The index of the texture that is currently being used by the GPU.
|
||||
uint32_t imageIndex = 0;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <temp/model/Loader.h>
|
||||
#include <vlkx/vulkan/VulkanModule.h>
|
||||
|
||||
namespace vlkx {
|
||||
|
||||
class Descriptor {
|
||||
public:
|
||||
using TextureType = vlkxtemp::ModelLoader::TextureType;
|
||||
using BufferInfos = std::map<uint32_t, std::vector<VkDescriptorBufferInfo>>;
|
||||
using ImageInfos = std::map<uint32_t, std::vector<VkDescriptorImageInfo>>;
|
||||
|
||||
struct Meta {
|
||||
struct Binding {
|
||||
uint32_t bindPoint;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
VkDescriptorType type;
|
||||
VkShaderStageFlags stage;
|
||||
std::vector<Binding> bindings;
|
||||
};
|
||||
|
||||
Descriptor(const Descriptor&) = delete;
|
||||
Descriptor& operator=(const Descriptor&) = delete;
|
||||
|
||||
virtual ~Descriptor() {
|
||||
vkDestroyDescriptorSetLayout(VulkanModule::getInstance()->getDevice()->logical, layout, nullptr);
|
||||
}
|
||||
|
||||
const VkDescriptorSetLayout& getLayout() const { return layout; }
|
||||
|
||||
protected:
|
||||
explicit Descriptor() = default;
|
||||
|
||||
void setLayout(const VkDescriptorSetLayout& newLayout) { layout = newLayout; }
|
||||
|
||||
private:
|
||||
VkDescriptorSetLayout layout;
|
||||
};
|
||||
|
||||
class StaticDescriptor : public Descriptor {
|
||||
public:
|
||||
|
||||
StaticDescriptor(std::vector<Meta> metas);
|
||||
StaticDescriptor(const StaticDescriptor&) = delete;
|
||||
StaticDescriptor& operator=(const StaticDescriptor&) = delete;
|
||||
|
||||
~StaticDescriptor() override {
|
||||
vkDestroyDescriptorPool(VulkanModule::getInstance()->getDevice()->logical, pool, nullptr);
|
||||
}
|
||||
|
||||
const StaticDescriptor& buffers(VkDescriptorType type, const BufferInfos& infos) const;
|
||||
const StaticDescriptor& images(VkDescriptorType type, const ImageInfos& infos) const;
|
||||
|
||||
void bind(const VkCommandBuffer& commands, const VkPipelineLayout& layout, VkPipelineBindPoint bindPoint) const;
|
||||
|
||||
private:
|
||||
|
||||
const StaticDescriptor& updateSet(const std::vector<VkWriteDescriptorSet>& write) const;
|
||||
|
||||
VkDescriptorPool pool;
|
||||
VkDescriptorSet set;
|
||||
};
|
||||
|
||||
// TODO: dynamic sets
|
||||
}
|
|
@ -0,0 +1,331 @@
|
|||
#pragma once
|
||||
|
||||
#include "vlkx/vulkan/Tools.h"
|
||||
#include "ImageUsage.h"
|
||||
#include <shadow/util/RefCounter.h>
|
||||
#include "Buffer.h"
|
||||
#include <array>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace vlkx {
|
||||
|
||||
// Describes an image without initializing or storing any heavy data.
|
||||
class ImageDescriptor {
|
||||
public:
|
||||
enum class Type { Single, Cubemap };
|
||||
struct Dimension {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t channels;
|
||||
|
||||
VkExtent2D getExtent() const { return { width, height }; }
|
||||
size_t getSize() const { return width * height * channels; }
|
||||
};
|
||||
|
||||
Type getType() const { return type; }
|
||||
VkExtent2D getExtent() const { return dimensions.getExtent(); }
|
||||
uint32_t getWidth() const { return dimensions.width; }
|
||||
uint32_t getHeight() const { return dimensions.height; }
|
||||
uint32_t getChannels() const { return dimensions.channels; }
|
||||
|
||||
std::vector<void*> getData() const {
|
||||
if (type == Type::Single) return { (void*) data };
|
||||
std::vector<void*> dataPtrs;
|
||||
dataPtrs.reserve(6);
|
||||
|
||||
size_t offset = 0;
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
dataPtrs.emplace_back((char*) data + offset);
|
||||
offset += dimensions.getSize();
|
||||
}
|
||||
|
||||
return dataPtrs;
|
||||
}
|
||||
|
||||
int getLayers() const { return type == Type::Single ? 1 : 6; }
|
||||
|
||||
ImageDescriptor(Type t, const Dimension& d, const void* ptr) : type(t), dimensions(d), data(ptr) {}
|
||||
|
||||
private:
|
||||
Type type;
|
||||
Dimension dimensions;
|
||||
const void* data;
|
||||
|
||||
};
|
||||
|
||||
// A staging buffer specialized for uploading images.
|
||||
class ImageStagingBuffer : public StagingBuffer {
|
||||
public:
|
||||
using StagingBuffer::StagingBuffer;
|
||||
|
||||
ImageStagingBuffer(const ImageStagingBuffer&) = delete;
|
||||
ImageStagingBuffer& operator=(const ImageStagingBuffer&) = delete;
|
||||
|
||||
void copy(const VkImage& target, const VkExtent3D& extent, uint32_t layers) const;
|
||||
};
|
||||
|
||||
// Root class that stores image data on GPU buffers
|
||||
class ImageBuffer : public Buffer {
|
||||
public:
|
||||
ImageBuffer(const ImageBuffer&) = delete;
|
||||
ImageBuffer& operator=(const ImageBuffer&) = delete;
|
||||
|
||||
~ImageBuffer() override {
|
||||
vmaDestroyImage(VkTools::allocator, image.image, image.allocation);
|
||||
}
|
||||
|
||||
const VkTools::ManagedImage& get() const { return image; }
|
||||
const VkImage& getImage() const { return image.image; }
|
||||
|
||||
protected:
|
||||
using Buffer::Buffer;
|
||||
|
||||
void setImage(const VkTools::ManagedImage newImg) { image = newImg; }
|
||||
|
||||
private:
|
||||
VkTools::ManagedImage image;
|
||||
|
||||
};
|
||||
|
||||
// Base class of all images; stores the common data.
|
||||
class Image {
|
||||
public:
|
||||
Image(const Image&) = delete;
|
||||
Image& operator=(const Image&) = delete;
|
||||
|
||||
virtual ~Image() {
|
||||
vkDestroyImageView(dev->logical, view, nullptr);
|
||||
}
|
||||
|
||||
static VkDescriptorType getSampleType() { return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; }
|
||||
static VkDescriptorType getLinearType() { return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; }
|
||||
|
||||
static ImageDescriptor loadSingleFromDisk(std::string path, bool flipY);
|
||||
// The following are left unimplemented intentionally.
|
||||
//static ImageDescriptor loadSingleFromVFS(std::string path, bool flipY);
|
||||
static ImageDescriptor loadCubeFromDisk(const std::string& directory, const std::array<std::string, 6>& files, bool flipY);
|
||||
//static ImageDescriptor loadCubeFromVFS(std::string directory, const std::array<std::string, 6>& files, bool flipY);
|
||||
|
||||
virtual ImageUsage getUsage() const { return ImageUsage {}; }
|
||||
|
||||
const VkTools::ManagedImage& operator*() const { return get(); }
|
||||
|
||||
virtual const VkTools::ManagedImage& get() const = 0;
|
||||
virtual const VkImage& getImage() const = 0;
|
||||
|
||||
const VkImageView& getView() const { return view; }
|
||||
const VkExtent2D& getExtent() const { return extent; }
|
||||
VkFormat getFormat() const { return format; }
|
||||
virtual VkSampleCountFlagBits getSamples() const { return VK_SAMPLE_COUNT_1_BIT; }
|
||||
|
||||
|
||||
protected:
|
||||
Image(const VkExtent2D& ext, VkFormat form);
|
||||
|
||||
void setView(const VkImageView& imgView) { view = imgView; }
|
||||
|
||||
VulkanDevice* dev;
|
||||
VkImageView view;
|
||||
VkExtent2D extent;
|
||||
VkFormat format;
|
||||
VkSampleCountFlagBits sampleCount;
|
||||
};
|
||||
|
||||
// Configures image sampling in a sensible and extensible way
|
||||
class ImageSampler {
|
||||
public:
|
||||
struct Config {
|
||||
explicit Config(VkFilter filter = VK_FILTER_LINEAR, VkSamplerAddressMode mode = VK_SAMPLER_ADDRESS_MODE_REPEAT) : filter(filter), mode(mode) {}
|
||||
|
||||
VkFilter filter;
|
||||
VkSamplerAddressMode mode;
|
||||
};
|
||||
|
||||
ImageSampler(int mipLevels, const Config& config);
|
||||
|
||||
ImageSampler(const ImageSampler&) = delete;
|
||||
ImageSampler& operator=(const ImageSampler&) = delete;
|
||||
|
||||
~ImageSampler() {
|
||||
vkDestroySampler(dev->logical, sampler, nullptr);
|
||||
}
|
||||
|
||||
const VkSampler& operator*() const { return sampler; }
|
||||
|
||||
private:
|
||||
VkSampler sampler;
|
||||
VulkanDevice* dev;
|
||||
};
|
||||
|
||||
// Root of images which can be sampled.
|
||||
class SamplableImage {
|
||||
public:
|
||||
virtual ~SamplableImage() = default;
|
||||
|
||||
// Return a VkDescriptorImageInfo we can use to update sets.
|
||||
virtual VkDescriptorImageInfo getInfo(VkImageLayout layout) const = 0;
|
||||
VkDescriptorImageInfo getInfoForSampling() const { return getInfo(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); }
|
||||
VkDescriptorImageInfo getInfoForLinear() const { return getInfo(VK_IMAGE_LAYOUT_GENERAL); }
|
||||
};
|
||||
|
||||
// A samplable image that lives on the GPU, with optional mipmapping.
|
||||
// Use a RefCountedTexture when loading from files.
|
||||
class TextureImage : public Image, public SamplableImage {
|
||||
public:
|
||||
// Image metadata
|
||||
struct Meta {
|
||||
VkExtent2D getExtent() const { return { width, height }; }
|
||||
VkExtent3D get3DExtent() const { return { width, height, channels }; }
|
||||
|
||||
Buffer::BulkCopyMeta getCopyMeta() const;
|
||||
|
||||
std::vector<void*> data;
|
||||
std::vector<ImageUsage> usages;
|
||||
VkFormat format;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t channels;
|
||||
};
|
||||
|
||||
TextureImage(bool mipmapping, const ImageSampler::Config& samplerConfig, const Meta& meta);
|
||||
TextureImage(bool mipmapping, const ImageDescriptor& image, const std::vector<ImageUsage>& usages, const ImageSampler::Config& config);
|
||||
|
||||
TextureImage(const TextureImage&) = delete;
|
||||
TextureImage& operator=(const TextureImage&) = delete;
|
||||
|
||||
const VkTools::ManagedImage& get() const override { return buffer.get(); }
|
||||
const VkImage& getImage() const override { return buffer.getImage(); }
|
||||
|
||||
VkDescriptorImageInfo getInfo(VkImageLayout layout) const override {
|
||||
return { *sampler, getView(), layout };
|
||||
}
|
||||
// Textures are sampled in fragment shaders.
|
||||
ImageUsage getUsage() const override {
|
||||
return ImageUsage::sampledFragment();
|
||||
}
|
||||
private:
|
||||
class TextureBuffer : public ImageBuffer {
|
||||
public:
|
||||
TextureBuffer(bool mipmaps, const Meta& meta);
|
||||
|
||||
TextureBuffer(const TextureBuffer&) = delete;
|
||||
TextureBuffer& operator=(const TextureBuffer&) = delete;
|
||||
|
||||
int getMipping() const { return mipLevels; }
|
||||
private:
|
||||
int mipLevels = 1;
|
||||
};
|
||||
|
||||
const TextureBuffer buffer;
|
||||
const ImageSampler sampler;
|
||||
};
|
||||
|
||||
// A texture image that lives on GPU that is reference counted.
|
||||
// This allows it to be reused multiple times without reading the file in more than once.
|
||||
// It also allows the texture to be destructed automatically once nothing in the scene uses it, for eg. level changes.
|
||||
class RefCountedTexture : public SamplableImage {
|
||||
public:
|
||||
// Cubemaps are simply 6 textures, so we include them here for easy instantiation.
|
||||
struct CubemapLocation {
|
||||
std::string directory;
|
||||
std::array<std::string, 6> files;
|
||||
};
|
||||
|
||||
// Reference Counting works on both individual files and cubemaps, so we put them together.
|
||||
using ImageLocation = std::variant<std::string, CubemapLocation>;
|
||||
|
||||
RefCountedTexture(const ImageLocation& location, std::vector<ImageUsage> usages, const ImageSampler::Config& config)
|
||||
: texture(get(location, std::move(usages), config)) {}
|
||||
|
||||
RefCountedTexture(RefCountedTexture&&) noexcept = default;
|
||||
RefCountedTexture& operator=(RefCountedTexture&&) noexcept = default;
|
||||
|
||||
VkDescriptorImageInfo getInfo(VkImageLayout layout) const override {
|
||||
return texture->getInfo(layout);
|
||||
}
|
||||
|
||||
const Image* operator->() const { return texture.operator->(); }
|
||||
|
||||
private:
|
||||
using ReferenceCounter = shadowutil::RefCounter<TextureImage>;
|
||||
// Get or load the specified image.
|
||||
static ReferenceCounter get(const ImageLocation& location, const std::vector<ImageUsage>& usages, const ImageSampler::Config& config);
|
||||
|
||||
ReferenceCounter texture;
|
||||
};
|
||||
|
||||
// TODO: unowned, offscreen images.
|
||||
|
||||
// Image that can be used as a depth / stencil buffer attachment.
|
||||
class DepthStencilImage : public Image {
|
||||
public:
|
||||
DepthStencilImage(const DepthStencilImage&) = delete;
|
||||
DepthStencilImage& operator=(const DepthStencilImage&) = delete;
|
||||
|
||||
DepthStencilImage(const VkExtent2D& extent);
|
||||
|
||||
const VkTools::ManagedImage& get() const override { return buffer.get(); }
|
||||
const VkImage& getImage() const override { return buffer.getImage(); }
|
||||
|
||||
private:
|
||||
class DepthStencilBuffer : public ImageBuffer {
|
||||
public:
|
||||
DepthStencilBuffer(const VkExtent2D& extent, VkFormat format);
|
||||
DepthStencilBuffer(const DepthStencilBuffer&) = delete;
|
||||
DepthStencilBuffer& operator=(const DepthStencilBuffer&) = delete;
|
||||
};
|
||||
|
||||
const DepthStencilBuffer buffer;
|
||||
};
|
||||
|
||||
// Image that references an existing image on the swapchain
|
||||
class SwapchainImage : public Image {
|
||||
public:
|
||||
SwapchainImage(const SwapchainImage&) = delete;
|
||||
SwapchainImage& operator=(const SwapchainImage&) = delete;
|
||||
|
||||
SwapchainImage(const VkImage& image, const VkExtent2D& extent, VkFormat format);
|
||||
|
||||
const VkTools::ManagedImage& get() const override { return managed; }
|
||||
const VkImage& getImage() const override { return image; }
|
||||
|
||||
private:
|
||||
VkImage image;
|
||||
VkTools::ManagedImage managed;
|
||||
};
|
||||
|
||||
class MultisampleImage : public Image {
|
||||
public:
|
||||
enum class Mode {
|
||||
MostEfficient,
|
||||
Highest
|
||||
};
|
||||
|
||||
static std::unique_ptr<Image> createColor(const Image& targetImage, Mode mode);
|
||||
static std::unique_ptr<Image> createDepthStencilMS(const VkExtent2D& extent, Mode mode);
|
||||
static std::unique_ptr<Image> createDepthStencil(VkExtent2D& extent, std::optional<Mode> mode);
|
||||
|
||||
const VkTools::ManagedImage& get() const override { return buffer.get(); }
|
||||
const VkImage& getImage() const override { return buffer.getImage(); }
|
||||
|
||||
VkSampleCountFlagBits getSamples() const override { return samples; }
|
||||
private:
|
||||
class MultisampleBuffer : public ImageBuffer {
|
||||
public:
|
||||
enum class Type { Color, DepthStencil };
|
||||
|
||||
MultisampleBuffer(Type type, const VkExtent2D& extent, VkFormat format, VkSampleCountFlagBits samples);
|
||||
MultisampleBuffer(const MultisampleBuffer&) = delete;
|
||||
MultisampleBuffer& operator=(const MultisampleBuffer&) = delete;
|
||||
};
|
||||
|
||||
MultisampleImage(const VkExtent2D& extent, VkFormat format, Mode mode, MultisampleBuffer::Type type);
|
||||
|
||||
VkSampleCountFlagBits chooseSamples(Mode mode);
|
||||
|
||||
const VkSampleCountFlagBits samples;
|
||||
const MultisampleBuffer buffer;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace vlkx {
|
||||
/**
|
||||
* Describes how an image (collection of color data) will be used in the GPU.
|
||||
* Has three parts; an overall description, access methods, and location of access.
|
||||
* Use the static methods to create an instance, or the blank initializer and set fields as required.
|
||||
*/
|
||||
class ImageUsage {
|
||||
public:
|
||||
enum class Type {
|
||||
DontCare, // Image is unused
|
||||
RenderTarget, // Color attachment is used
|
||||
DepthStencil, // Depth / Stencil buffer is used
|
||||
Multisample, // Resolves to a multisampled image
|
||||
Presentation, // Framebuffer for presentation
|
||||
LinearAccess, // Host-Readable
|
||||
InputAttachment, // Only what we write, is read.
|
||||
Sampled, // Image is sampled (ie. texture)
|
||||
Transfer // Image is used as an intermediate for transfer.
|
||||
};
|
||||
|
||||
enum class Access {
|
||||
DontCare, // Image is unused
|
||||
ReadOnly, // Read Only access.
|
||||
WriteOnly, // Write Only access.
|
||||
ReadWrite, // Read/Write access.
|
||||
};
|
||||
|
||||
enum class Location {
|
||||
DontCare, // Image is unused
|
||||
Host, // Image only exists on the host and will be transferred.
|
||||
VertexShader, // Image is only used in the VertexAll Shader.
|
||||
FragmentShader, // Image is only used in the Fragment Shader.
|
||||
ComputeShader, // Image is only used in a Compute Shader.
|
||||
Other, // Reserved.
|
||||
};
|
||||
|
||||
// Sampled in a fragment shader. Read-Only.
|
||||
static ImageUsage sampledFragment() { return { Type::Sampled, Access::ReadOnly, Location::FragmentShader }; };
|
||||
// Used as a render target (a render pass will output to this image). Read/Write.
|
||||
static ImageUsage renderTarget(int loc) { return { Type::RenderTarget, Access::ReadWrite, Location::Other, loc}; };
|
||||
// Resolves to a multisampled image. Write-Only.
|
||||
static ImageUsage multisample() { return { Type::Multisample, Access::WriteOnly, Location::Other }; };
|
||||
// A depth or stencil buffer. Access is given, but must not be DontCare.
|
||||
static ImageUsage depthStencil(Access acc) { return { Type::DepthStencil, acc, Location::Other}; };
|
||||
// Used to show to the user. Write-Only.
|
||||
static ImageUsage presentation() { return { Type::Presentation, Access::ReadOnly, Location::Other }; };
|
||||
// Input attachment for a fragment shader. Usually a texture. Read-Only.
|
||||
static ImageUsage input() { return { Type::InputAttachment, Access::ReadOnly, Location::FragmentShader }; };
|
||||
// Linearly accessed image. For a compute shader. Access is given, but must not be DontCare.
|
||||
static ImageUsage compute(Access acc) { return { Type::LinearAccess, acc, Location::ComputeShader }; };
|
||||
|
||||
explicit ImageUsage() : ImageUsage(Type::DontCare, Access::DontCare, Location::DontCare) {}
|
||||
|
||||
bool operator==(const ImageUsage& other) const {
|
||||
return type == other.type && access == other.access && location == other.location;
|
||||
}
|
||||
|
||||
VkImageUsageFlagBits getUsageFlags() const {
|
||||
switch (type) {
|
||||
case Type::DontCare: throw std::runtime_error("No usage for type DontCare");
|
||||
case Type::RenderTarget:
|
||||
case Type::Multisample:
|
||||
case Type::Presentation:
|
||||
return VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
case Type::DepthStencil:
|
||||
return VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
case Type::LinearAccess:
|
||||
return VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
case Type::Sampled:
|
||||
return VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
case Type::Transfer:
|
||||
switch (access) {
|
||||
case Access::DontCare: throw std::runtime_error("No access type specified for transfer usage.");
|
||||
case Access::ReadOnly: return VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
case Access::WriteOnly: return VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
case Access::ReadWrite: throw std::runtime_error("ReadWrite access type for Transfer usage is invalid.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static VkImageUsageFlags getFlagsForUsage(const std::vector<ImageUsage>& usages) {
|
||||
auto flags = 0;
|
||||
for (const auto& usage : usages) {
|
||||
if (usage.type != Type::DontCare)
|
||||
flags |= usage.getUsageFlags();
|
||||
}
|
||||
|
||||
return static_cast<VkImageUsageFlags>(flags);
|
||||
}
|
||||
|
||||
VkImageLayout getLayout() const {
|
||||
switch (type) {
|
||||
case Type::DontCare: return VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
case Type::RenderTarget:
|
||||
case Type::Multisample:
|
||||
return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
case Type::DepthStencil: return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
case Type::Presentation: return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
case Type::LinearAccess: return VK_IMAGE_LAYOUT_GENERAL;
|
||||
case Type::Sampled: return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
case Type::Transfer: return access == Access::ReadOnly ? VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
case Type::InputAttachment:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VkPipelineStageFlags getStage() const {
|
||||
switch (type) {
|
||||
case Type::DontCare: return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
case Type::RenderTarget:
|
||||
case Type::Multisample:
|
||||
case Type::Presentation:
|
||||
return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
case Type::DepthStencil:
|
||||
return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
case Type::LinearAccess:
|
||||
case Type::Sampled:
|
||||
switch (location) {
|
||||
case Location::Host: return VK_PIPELINE_STAGE_HOST_BIT;
|
||||
case Location::FragmentShader: return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
case Location::ComputeShader: return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
||||
|
||||
case Location::VertexShader:
|
||||
case Location::Other:
|
||||
throw std::runtime_error("Linear or sampled attachments must not be used in VertexAll or Other stages.");
|
||||
case Location::DontCare: throw std::runtime_error("Linear or sampled attachments must have an access.");
|
||||
}
|
||||
|
||||
case Type::Transfer:
|
||||
return VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
Access getAccess() const { return access; }
|
||||
|
||||
Type getType() const { return type; }
|
||||
|
||||
VkAccessFlags getAccessFlags() const {
|
||||
switch (type) {
|
||||
case Type::DontCare: return VK_ACCESS_NONE_KHR;
|
||||
case Type::RenderTarget: return getReadOrWrite(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
|
||||
case Type::DepthStencil: return getReadOrWrite(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT);
|
||||
case Type::Multisample: return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
case Type::Presentation: return 0;
|
||||
case Type::LinearAccess:
|
||||
case Type::Sampled:
|
||||
return location == Location::Host ? getReadOrWrite(VK_ACCESS_HOST_READ_BIT, VK_ACCESS_HOST_WRITE_BIT) : getReadOrWrite(VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_SHADER_WRITE_BIT);
|
||||
case Type::Transfer:
|
||||
return getReadOrWrite(VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
case Type::InputAttachment:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
ImageUsage(Type t, Access a, Location l, std::optional<int> att = std::nullopt)
|
||||
: type(t), access(a), location(l), attachment(att) {}
|
||||
|
||||
Type type;
|
||||
Access access;
|
||||
Location location;
|
||||
std::optional<int> attachment;
|
||||
|
||||
inline VkAccessFlags getReadOrWrite(VkAccessFlags read, VkAccessFlags write) const {
|
||||
VkAccessFlags flag = 0;
|
||||
if (access == Access::ReadOnly || access == Access::ReadWrite)
|
||||
flag |= read;
|
||||
if (access == Access::WriteOnly || access == Access::ReadWrite)
|
||||
flag |= write;
|
||||
return flag;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes how a single image will be used in each stage of a render pass.
|
||||
* Allows a single image to be written to during one pass, read in a second, and presented in the final, and tracked.
|
||||
* Helps figure out the optimizations that Vulkan can do to this image and the render passes that use it.
|
||||
*/
|
||||
class UsageTracker {
|
||||
public:
|
||||
|
||||
explicit UsageTracker(const ImageUsage& initial) : initialUsage(initial) {}
|
||||
explicit UsageTracker() = default;
|
||||
|
||||
UsageTracker(UsageTracker&&) noexcept = default;
|
||||
UsageTracker& operator=(UsageTracker&&) noexcept = default;
|
||||
|
||||
// Fluent API; chain calls in a builder pattern.
|
||||
#define fluent UsageTracker&
|
||||
|
||||
fluent add(int pass, const ImageUsage& usage) {
|
||||
usageAtSubpass.insert( { pass, usage} );
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
fluent add(int start, int end, const ImageUsage& usage) {
|
||||
for (int subpass = start; subpass <= end; ++subpass)
|
||||
add(subpass, usage);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
fluent addMultisample(int pass, std::string_view name) {
|
||||
multisamples.insert( { pass, std::string(name) } );
|
||||
return add(pass, ImageUsage::multisample());
|
||||
}
|
||||
|
||||
fluent setFinal(const ImageUsage& usage) {
|
||||
finalUsage = usage;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<ImageUsage> getUsages() const {
|
||||
size_t count = usageAtSubpass.size() + (finalUsage.has_value() ? 1 : 0) + 1;
|
||||
|
||||
std::vector<ImageUsage> usages;
|
||||
usages.reserve(count);
|
||||
usages.emplace_back(initialUsage);
|
||||
|
||||
for(const auto& pair : usageAtSubpass)
|
||||
usages.emplace_back(pair.second);
|
||||
|
||||
if (finalUsage.has_value())
|
||||
usages.emplace_back(finalUsage.value());
|
||||
|
||||
return usages;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::map<int, ImageUsage>& getUsageMap() const {
|
||||
return usageAtSubpass;
|
||||
}
|
||||
|
||||
ImageUsage& getInitialUsage() { return initialUsage; }
|
||||
std::optional<ImageUsage> getFinalUsage() { return finalUsage; }
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
std::map<int, ImageUsage> usageAtSubpass;
|
||||
ImageUsage initialUsage;
|
||||
std::optional<ImageUsage> finalUsage;
|
||||
std::map<int, std::string> multisamples;
|
||||
};
|
||||
|
||||
/**
|
||||
* A simple wrapper that allows tracking the current usage of multiple images.
|
||||
*/
|
||||
class MultiImageTracker {
|
||||
public:
|
||||
MultiImageTracker() = default;
|
||||
|
||||
MultiImageTracker(const MultiImageTracker&) = delete;
|
||||
MultiImageTracker& operator=(const MultiImageTracker&) = delete;
|
||||
|
||||
// Fluent API; chain calls in a builder pattern
|
||||
#undef fluent
|
||||
#define fluent MultiImageTracker&
|
||||
|
||||
fluent track(std::string&& name, const ImageUsage& usage) {
|
||||
images.insert( { std::move(name), usage } );
|
||||
return *this;
|
||||
}
|
||||
|
||||
fluent track(const std::string& name, const ImageUsage& usage) {
|
||||
return track(std::string(name), usage);
|
||||
}
|
||||
|
||||
fluent update(const std::string& name, const ImageUsage& usage) {
|
||||
auto iter = images.find(name);
|
||||
iter->second = usage;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isTracking(const std::string& image) const {
|
||||
return images.contains(image);
|
||||
}
|
||||
|
||||
[[nodiscard]] const ImageUsage& get(const std::string& image) const {
|
||||
return images.at(image);
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::string, ImageUsage> images;
|
||||
|
||||
};
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
namespace vlkx {
|
||||
struct Queue {
|
||||
VkQueue queue;
|
||||
int queueIndex;
|
||||
};
|
||||
}
|
|
@ -5489,7 +5489,6 @@ public:
|
|||
// Posts next part of an open string.
|
||||
void ContinueString(const char* pStr);
|
||||
// Posts next part of an open string. The number is converted to decimal characters.
|
||||
void ContinueString(uint32_t n);
|
||||
void ContinueString(uint64_t n);
|
||||
// Posts next part of an open string. Pointer value is converted to characters
|
||||
// using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00
|
||||
|
@ -5498,7 +5497,6 @@ public:
|
|||
void EndString(const char* pStr = VMA_NULL);
|
||||
|
||||
// Writes a number value.
|
||||
void WriteNumber(uint32_t n);
|
||||
void WriteNumber(uint64_t n);
|
||||
// Writes a boolean value - false or true.
|
||||
void WriteBool(bool b);
|
||||
|
@ -5654,12 +5652,6 @@ void VmaJsonWriter::ContinueString(const char* pStr)
|
|||
}
|
||||
}
|
||||
|
||||
void VmaJsonWriter::ContinueString(uint32_t n)
|
||||
{
|
||||
VMA_ASSERT(m_InsideString);
|
||||
m_SB.AddNumber(n);
|
||||
}
|
||||
|
||||
void VmaJsonWriter::ContinueString(uint64_t n)
|
||||
{
|
||||
VMA_ASSERT(m_InsideString);
|
||||
|
@ -5683,13 +5675,6 @@ void VmaJsonWriter::EndString(const char* pStr)
|
|||
m_InsideString = false;
|
||||
}
|
||||
|
||||
void VmaJsonWriter::WriteNumber(uint32_t n)
|
||||
{
|
||||
VMA_ASSERT(!m_InsideString);
|
||||
BeginValue(false);
|
||||
m_SB.AddNumber(n);
|
||||
}
|
||||
|
||||
void VmaJsonWriter::WriteNumber(uint64_t n)
|
||||
{
|
||||
VMA_ASSERT(!m_InsideString);
|
||||
|
|
|
@ -1,20 +1,129 @@
|
|||
#include <vlkx/render/Camera.h>
|
||||
|
||||
void Camera::init(float fov, float width, float height, float near, float far) {
|
||||
using namespace vlkx;
|
||||
|
||||
// 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);
|
||||
Camera& Camera::move(const glm::vec3 &delta) {
|
||||
position += delta;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Camera::setPosition(glm::vec3 newPosition) {
|
||||
position = newPosition;
|
||||
}
|
||||
Camera &Camera::setPos(const glm::vec3 &pos) {
|
||||
position = pos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Camera &Camera::up(const glm::vec3 &up) {
|
||||
upVector = glm::normalize(up);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Camera &Camera::forward(const glm::vec3 &forward) {
|
||||
frontVector = glm::normalize(forward);
|
||||
rightVector = glm::normalize(glm::cross(frontVector, upVector));
|
||||
return *this;
|
||||
}
|
||||
|
||||
glm::mat4 Camera::getViewMatrix() const {
|
||||
return glm::lookAt(position, position + frontVector, upVector);
|
||||
}
|
||||
|
||||
PerspectiveCamera &PerspectiveCamera::fieldOfView(float newFov) {
|
||||
fov = newFov;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PerspectiveCamera::RT PerspectiveCamera::getRT() const {
|
||||
const glm::vec3 upVec = glm::normalize(glm::cross(getRight(), getForward()));
|
||||
const float fovTan = glm::tan(glm::radians(fov));
|
||||
return { upVec * fovTan, getForward(), getRight() * fovTan * aspectRatio };
|
||||
}
|
||||
|
||||
glm::mat4 PerspectiveCamera::getProjMatrix() const {
|
||||
glm::mat4 proj = glm::perspective(glm::radians(fov), aspectRatio, nearPlane, farPlane);
|
||||
proj[1][1] = -proj[1][1];
|
||||
return proj;
|
||||
}
|
||||
|
||||
OrthographicCamera &OrthographicCamera::setWidth(float vWidth) {
|
||||
width = vWidth;
|
||||
return *this;
|
||||
}
|
||||
|
||||
glm::mat4 OrthographicCamera::getProjMatrix() const {
|
||||
const float height = width / aspectRatio;
|
||||
const auto halfSize = glm::vec2 { width, height } / 2.0f;
|
||||
return glm::ortho(-halfSize.x, halfSize.x, -halfSize.y, halfSize.y, nearPlane, farPlane);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void UserCamera<Type>::setInternal(std::function<void(Type *)> op) {
|
||||
op(camera.get());
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void UserCamera<Type>::move(double x, double y) {
|
||||
if (!isActive) return;
|
||||
|
||||
const auto offsetX = static_cast<float>(x * config.turnSpeed);
|
||||
const auto offsetY = static_cast<float>(y * config.turnSpeed);
|
||||
|
||||
pitch = glm::clamp(pitch - offsetY, glm::radians(-89.9f), glm::radians(89.9f));
|
||||
yaw = glm::mod(yaw - offsetX, glm::radians(360.0f));
|
||||
camera->forward( { glm::cos(pitch) * glm::cos(yaw), glm::sin(pitch), glm::cos(pitch) * glm::sin(yaw) });
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
bool UserCamera<Type>::scroll(double delta, double min, double max) {
|
||||
if (!isActive) return false;
|
||||
|
||||
if constexpr (std::is_same_v<Type, PerspectiveCamera>) {
|
||||
auto newFov = (float) glm::clamp(camera->getFieldOfView() + delta, min, max);
|
||||
if (newFov != camera->getFieldOfView()) {
|
||||
camera->fieldOfView(newFov);
|
||||
return true;
|
||||
}
|
||||
} else if constexpr (std::is_same_v<Type, OrthographicCamera>) {
|
||||
const auto newWidth = (float) glm::clamp(camera->getWidth() + delta, min, max);
|
||||
if (newWidth != camera->getWidth()) {
|
||||
camera->setWidth(newWidth);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
static_assert("Unhandled Camera Type");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void UserCamera<Type>::press(Camera::Input key, float time) {
|
||||
using Key = Camera::Input;
|
||||
if (!isActive) return;
|
||||
|
||||
if (!config.center.has_value()) {
|
||||
const float distance = time * config.moveSpeed;
|
||||
switch (key) {
|
||||
case Key::Up:
|
||||
camera->move(+camera->getForward() * distance); break;
|
||||
case Key::Down:
|
||||
camera->move(-camera->getForward() * distance); break;
|
||||
case Key::Left:
|
||||
camera->move(-camera->getRight() * distance); break;
|
||||
case Key::Right:
|
||||
camera->move(+camera->getRight() * distance); break;
|
||||
}
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void UserCamera<Type>::reset() {
|
||||
refForward = camera->getForward();
|
||||
refLeft = -camera->getRight();
|
||||
pitch = yaw = 0;
|
||||
}
|
||||
|
||||
template class vlkx::UserCamera<PerspectiveCamera>;
|
||||
template class vlkx::UserCamera<OrthographicCamera>;
|
|
@ -1,11 +1,11 @@
|
|||
#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 } },
|
||||
void Mesh::setTriData(std::vector<VertexAll>& vertices, std::vector<uint32_t>& indices) {
|
||||
std::vector<VertexAll> Vertices = {
|
||||
{ { 0.0f, -1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0, 1.0 } },
|
||||
{ { 1.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0, 0.0 } },
|
||||
{ { -1.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 1.0, 0.0 } },
|
||||
};
|
||||
|
||||
std::vector<uint32_t> Indices = {
|
||||
|
@ -18,13 +18,13 @@ void Mesh::setTriData(std::vector<Vertex>& vertices, std::vector<uint32_t>& indi
|
|||
indices = Indices;
|
||||
}
|
||||
|
||||
void Mesh::setQuadData(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices) {
|
||||
void Mesh::setQuadData(std::vector<VertexAll>& 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<VertexAll> Vertices = {
|
||||
{ { -1.0f, -1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0, 1.0 } },
|
||||
{ { -1.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0, 0.0 } },
|
||||
{ { 1.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 1.0, 0.0 } },
|
||||
{ { 1.0f, -1.0f, 0.0f },{ 0.0f, 0.0f, 1.0 },{ 1.0, 1.0 } }
|
||||
};
|
||||
|
||||
std::vector<uint32_t> Indices = {
|
||||
|
@ -37,38 +37,38 @@ void Mesh::setQuadData(std::vector<Vertex>& vertices, std::vector<uint32_t>& ind
|
|||
indices = Indices;
|
||||
}
|
||||
|
||||
void Mesh::setCubeData(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices) {
|
||||
std::vector<Vertex> Vertices = {
|
||||
void Mesh::setCubeData(std::vector<VertexAll>& vertices, std::vector<uint32_t>& indices) {
|
||||
std::vector<VertexAll> 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
|
||||
{ { -1.0f, -1.0f, 1.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0, 1.0 } }, // 0
|
||||
{ { -1.0f, 1.0f, 1.0f },{ 0.0f, 0.0f, 1.0 },{ 0.0, 0.0 } }, // 1
|
||||
{ { 1.0f, 1.0f, 1.0f },{ 0.0f, 0.0f, 1.0 },{ 1.0, 0.0 } }, // 2
|
||||
{ { 1.0f, -1.0f, 1.0f },{ 0.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
|
||||
{ { 1.0, -1.0, -1.0 },{ 0.0f, 0.0f, -1.0 },{ 0.0, 1.0 } }, // 4
|
||||
{ { 1.0f, 1.0, -1.0 },{ 0.0f, 0.0f, -1.0 },{ 0.0, 0.0 } }, // 5
|
||||
{ { -1.0, 1.0, -1.0 },{ 0.0f, 0.0f, -1.0 },{ 1.0, 0.0 } }, // 6
|
||||
{ { -1.0, -1.0, -1.0 },{ 0.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
|
||||
{ { -1.0, -1.0, -1.0 },{ -1.0f, 0.0f, 0.0 },{ 0.0, 1.0 } }, // 8
|
||||
{ { -1.0f, 1.0, -1.0 },{ -1.0f, 0.0f, 0.0 },{ 0.0, 0.0 } }, // 9
|
||||
{ { -1.0, 1.0, 1.0 },{ -1.0f, 0.0f, 0.0 },{ 1.0, 0.0 } }, // 10
|
||||
{ { -1.0, -1.0, 1.0 },{ -1.0f, 0.0f, 0.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
|
||||
{ { 1.0, -1.0, 1.0 },{ 1.0f, 0.0f, 0.0 },{ 0.0, 1.0 } }, // 12
|
||||
{ { 1.0f, 1.0, 1.0 },{ 1.0f, 0.0f, 0.0 },{ 0.0, 0.0 } }, // 13
|
||||
{ { 1.0, 1.0, -1.0 },{ 1.0f, 0.0f, 0.0 },{ 1.0, 0.0 } }, // 14
|
||||
{ { 1.0, -1.0, -1.0 },{ 1.0f, 0.0f, 0.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
|
||||
{ { -1.0f, 1.0f, 1.0f },{ 0.0f, 1.0f, 0.0 },{ 0.0, 1.0 } }, // 16
|
||||
{ { -1.0f, 1.0f, -1.0f },{ 0.0f, 1.0f, 0.0 },{ 0.0, 0.0 } }, // 17
|
||||
{ { 1.0f, 1.0f, -1.0f },{ 0.0f, 1.0f, 0.0 },{ 1.0, 0.0 } }, // 18
|
||||
{ { 1.0f, 1.0f, 1.0f },{ 0.0f, 1.0f, 0.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
|
||||
{ { -1.0f, -1.0, -1.0 },{ 0.0f, -1.0f, 0.0 },{ 0.0, 1.0 } }, // 20
|
||||
{ { -1.0, -1.0, 1.0 },{ 0.0f, -1.0f, 0.0 },{ 0.0, 0.0 } }, // 21
|
||||
{ { 1.0, -1.0, 1.0 },{ 0.0f, -1.0f, 0.0 },{ 1.0, 0.0 } }, // 22
|
||||
{ { 1.0, -1.0, -1.0 },{ 0.0f, -1.0f, 0.0 },{ 1.0, 1.0 } }, // 23
|
||||
};
|
||||
|
||||
std::vector<uint32_t> Indices = {
|
||||
|
@ -97,8 +97,8 @@ void Mesh::setCubeData(std::vector<Vertex>& vertices, std::vector<uint32_t>& ind
|
|||
}
|
||||
|
||||
|
||||
void Mesh::setSphereData(std::vector<Vertex>& vertices, std::vector<uint32_t>& indices) {
|
||||
std::vector<Vertex> Vertices;
|
||||
void Mesh::setSphereData(std::vector<VertexAll>& vertices, std::vector<uint32_t>& indices) {
|
||||
std::vector<VertexAll> Vertices;
|
||||
std::vector<uint32_t> Indices;
|
||||
|
||||
float latitudeBands = 20.0f;
|
||||
|
@ -116,7 +116,7 @@ void Mesh::setSphereData(std::vector<Vertex>& vertices, std::vector<uint32_t>& i
|
|||
float sinPhi = sin(phi);
|
||||
float cosPhi = cos(phi);
|
||||
|
||||
Vertex vs;
|
||||
VertexAll vs;
|
||||
|
||||
vs.texture.x = (longNumber / longitudeBands); // u
|
||||
vs.texture.y = (latNumber / latitudeBands); // v
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
#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
|
||||
VkSubpassDependency dependency = {};
|
||||
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dependency.dstSubpass = 0;
|
||||
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.srcAccessMask = 0;
|
||||
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
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.dependencyCount = 1;
|
||||
createInfo.pDependencies = &dependency;
|
||||
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);
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
#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();
|
||||
}
|
|
@ -0,0 +1,312 @@
|
|||
#include "vlkx/render/shader/Pipeline.h"
|
||||
#include "vlkx/vulkan/VulkanModule.h"
|
||||
#include "shadow/util/File.h"
|
||||
#include <numeric>
|
||||
|
||||
namespace vlkx {
|
||||
|
||||
struct ShaderStage {
|
||||
VkShaderStageFlagBits stage;
|
||||
ShaderModule::CountedShader module;
|
||||
};
|
||||
|
||||
VkPipelineViewportStateCreateInfo createViewport(const GraphicsPipelineBuilder::Viewport& port) {
|
||||
return {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
nullptr, 0, 1, &port.viewport, 1, &port.scissor
|
||||
};
|
||||
}
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo createBlend(const std::vector<VkPipelineColorBlendAttachmentState>& states) {
|
||||
return {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
nullptr, 0, VK_FALSE, VK_LOGIC_OP_CLEAR, static_cast<uint32_t>(states.size()), states.data(),
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
}
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo createVertexInput(const std::vector<VkVertexInputBindingDescription>& bindingDescs,
|
||||
const std::vector<VkVertexInputAttributeDescription>& attrDescs) {
|
||||
return {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
nullptr, 0,
|
||||
static_cast<uint32_t>(bindingDescs.size()), bindingDescs.data(),
|
||||
static_cast<uint32_t>(attrDescs.size()), attrDescs.data()
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<ShaderStage> createShader(const std::map<VkShaderStageFlagBits, std::string>& shaderMap) {
|
||||
std::vector<ShaderStage> stages;
|
||||
stages.reserve(shaderMap.size());
|
||||
for (const auto& pair : shaderMap) {
|
||||
stages.push_back({ pair.first, ShaderModule::CountedShader::get(pair.second, pair.second) });
|
||||
}
|
||||
|
||||
return stages;
|
||||
}
|
||||
|
||||
std::vector<VkPipelineShaderStageCreateInfo> createShaderStage(const std::vector<ShaderStage>& stages) {
|
||||
static constexpr char entryPoint[] = "main";
|
||||
std::vector<VkPipelineShaderStageCreateInfo> infos;
|
||||
infos.reserve(stages.size());
|
||||
for (const auto& stage : stages) {
|
||||
infos.push_back({
|
||||
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
nullptr, 0, stage.stage, **stage.module, entryPoint, nullptr
|
||||
});
|
||||
}
|
||||
|
||||
return infos;
|
||||
}
|
||||
|
||||
ShaderModule::ShaderModule(const std::string &path) {
|
||||
const shadowutil::FileData* file = shadowutil::loadFile(path);
|
||||
const VkShaderModuleCreateInfo module {
|
||||
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
nullptr, 0, file->size, reinterpret_cast<const uint32_t*>(file->data.data())
|
||||
};
|
||||
|
||||
if (vkCreateShaderModule(VulkanModule::getInstance()->getDevice()->logical, &module, nullptr, &shader) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to create shader module");
|
||||
}
|
||||
|
||||
PipelineBuilder::PipelineBuilder(std::optional<int> maxCache) {
|
||||
const VkPipelineCacheCreateInfo info {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
|
||||
nullptr, 0, static_cast<size_t>(maxCache.value_or(0)), nullptr
|
||||
};
|
||||
|
||||
if (vkCreatePipelineCache(VulkanModule::getInstance()->getDevice()->logical, &info, nullptr, &cache) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to create pipeline cache");
|
||||
}
|
||||
|
||||
void PipelineBuilder::setLayout(std::vector<VkDescriptorSetLayout>&& descs,
|
||||
std::vector<VkPushConstantRange>&& pushConstants) {
|
||||
std::vector<int> pushSizes (pushConstants.size());
|
||||
for (size_t i = 0; i < pushConstants.size(); i++)
|
||||
pushSizes[i] = pushConstants[i].size;
|
||||
|
||||
const auto totalSize = std::accumulate(pushSizes.begin(), pushSizes.end(), 0);
|
||||
if (totalSize > 128)
|
||||
throw std::runtime_error("Trying to set push constants of total size " + std::to_string(totalSize) + " into pipeline " + name);
|
||||
|
||||
descLayouts = std::move(descs);
|
||||
constants = std::move(pushConstants);
|
||||
layoutInfo.emplace(VkPipelineLayoutCreateInfo {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
nullptr, 0, static_cast<uint32_t>(descLayouts.size()), descLayouts.data(),
|
||||
static_cast<uint32_t>(constants.size()), constants.data()
|
||||
});
|
||||
}
|
||||
|
||||
GraphicsPipelineBuilder::GraphicsPipelineBuilder(std::optional<int> maxCache) : PipelineBuilder(maxCache) {
|
||||
assemblyInfo = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
nullptr, 0, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, VK_FALSE
|
||||
};
|
||||
|
||||
rasterizationInfo = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
nullptr, 0, VK_FALSE, VK_FALSE, VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
||||
VK_FALSE, 0, 0, 0, 1
|
||||
};
|
||||
|
||||
multisampleInfo = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
nullptr, 0, VK_SAMPLE_COUNT_1_BIT, VK_FALSE, 0, nullptr, VK_FALSE, VK_FALSE
|
||||
};
|
||||
|
||||
depthStencilInfo = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
nullptr, 0, VK_FALSE, VK_FALSE,
|
||||
VK_COMPARE_OP_LESS_OR_EQUAL, VK_FALSE, VK_FALSE,
|
||||
{}, {}, 0, 1
|
||||
};
|
||||
|
||||
dynamicStateInfo = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
nullptr, 0, 0, nullptr
|
||||
};
|
||||
}
|
||||
|
||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::name(std::string &&name) {
|
||||
setName(std::move(name));
|
||||
return *this;
|
||||
}
|
||||
|
||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::depthTest(bool enable, bool write) {
|
||||
depthStencilInfo.depthTestEnable = enable;
|
||||
depthStencilInfo.depthWriteEnable = write;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::stencilTest(bool enable) {
|
||||
depthStencilInfo.stencilTestEnable = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::multiSample(VkSampleCountFlagBits samples) {
|
||||
multisampleInfo.rasterizationSamples = samples;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::topology(VkPrimitiveTopology topology) {
|
||||
assemblyInfo.topology = topology;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::stencilOp(const VkStencilOpState &state,
|
||||
VkStencilFaceFlags flags) {
|
||||
if (flags & VK_STENCIL_FACE_FRONT_BIT)
|
||||
depthStencilInfo.front = state;
|
||||
if (flags & VK_STENCIL_FACE_BACK_BIT)
|
||||
depthStencilInfo.back = state;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::addVertex(uint32_t bindPoint,
|
||||
VkVertexInputBindingDescription &&desc,
|
||||
std::vector<VkVertexInputAttributeDescription> &&attrs) {
|
||||
desc.binding = bindPoint;
|
||||
for (auto& attr : attrs)
|
||||
attr.binding = bindPoint;
|
||||
bindingDescs.push_back(desc);
|
||||
|
||||
attrDescs.reserve(attrDescs.size() + attrs.size());
|
||||
std::move(attrs.begin(), attrs.end(), std::back_inserter(attrDescs));
|
||||
attrs.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::layout(std::vector<VkDescriptorSetLayout> &&descLayouts,
|
||||
std::vector<VkPushConstantRange> &&constants) {
|
||||
setLayout(std::move(descLayouts), std::move(constants));
|
||||
return *this;
|
||||
}
|
||||
|
||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::viewport(const vlkx::GraphicsPipelineBuilder::Viewport &port,
|
||||
bool flipY) {
|
||||
viewportMeta.emplace(port);
|
||||
|
||||
if (flipY) {
|
||||
VkViewport& view = viewportMeta.value().viewport;
|
||||
view.y += view.height;
|
||||
view.height *= -1;
|
||||
}
|
||||
|
||||
rasterizationInfo.frontFace = flipY ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::renderPass(const VkRenderPass &pass, uint32_t subpass) {
|
||||
passMeta.emplace(PassInfo { pass, subpass });
|
||||
return *this;
|
||||
}
|
||||
|
||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::colorBlend(
|
||||
std::vector<VkPipelineColorBlendAttachmentState> &&states) {
|
||||
blendStates = std::move(states);
|
||||
return *this;
|
||||
}
|
||||
|
||||
GraphicsPipelineBuilder& GraphicsPipelineBuilder::shader(VkShaderStageFlagBits stage, std::string &&file) {
|
||||
shaders[stage] = std::move(file);
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<Pipeline> GraphicsPipelineBuilder::build() const {
|
||||
if (!hasLayout())
|
||||
throw std::runtime_error("Pipeline " + getName() + " has no layout set");
|
||||
if (!viewportMeta.has_value())
|
||||
throw std::runtime_error("Pipeline " + getName() + " has no viewport set");
|
||||
if (!passMeta.has_value())
|
||||
throw std::runtime_error("Pipeline " + getName() + " has no render pass set");
|
||||
if (blendStates.empty())
|
||||
throw std::runtime_error("Pipeline " + getName() + " has no color blend states.");
|
||||
if (shaders.empty())
|
||||
throw std::runtime_error("Pipeline " + getName() + " has no shaders bound");
|
||||
|
||||
const auto viewportState = createViewport(viewportMeta.value());
|
||||
const auto blendState = createBlend(blendStates);
|
||||
const auto vertexState = createVertexInput(bindingDescs, attrDescs);
|
||||
|
||||
const auto shaderStages = createShader(shaders);
|
||||
const auto shaderStageInfo = createShaderStage(shaderStages);
|
||||
VkPipelineLayout pipelineLayout;
|
||||
if (vkCreatePipelineLayout(VulkanModule::getInstance()->getDevice()->logical, &getLayout(), nullptr, &pipelineLayout) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to create layout for pipeline " + getName());
|
||||
|
||||
const VkGraphicsPipelineCreateInfo createInfo {
|
||||
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, nullptr, 0,
|
||||
static_cast<uint32_t>(shaderStageInfo.size()), shaderStageInfo.data(),
|
||||
&vertexState, &assemblyInfo, nullptr, &viewportState,
|
||||
&rasterizationInfo, &multisampleInfo, &depthStencilInfo, &blendState, &dynamicStateInfo,
|
||||
pipelineLayout, passMeta->pass, passMeta->subpass, VK_NULL_HANDLE, 0
|
||||
};
|
||||
|
||||
VkPipeline pipeline;
|
||||
if (vkCreateGraphicsPipelines(VulkanModule::getInstance()->getDevice()->logical, VK_NULL_HANDLE, 1, &createInfo, nullptr, &pipeline) != VK_SUCCESS)
|
||||
throw std::runtime_error("Failed to create pipeline " + getName());
|
||||
|
||||
return std::unique_ptr<Pipeline> {
|
||||
new Pipeline(getName(), pipeline, pipelineLayout, VK_PIPELINE_BIND_POINT_GRAPHICS)
|
||||
};
|
||||
}
|
||||
|
||||
ComputePipelineBuilder& ComputePipelineBuilder::name(std::string &&name) {
|
||||
setName(std::move(name));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComputePipelineBuilder& ComputePipelineBuilder::layout(std::vector<VkDescriptorSetLayout> &&descLayouts,
|
||||
std::vector<VkPushConstantRange> &&pushConstants) {
|
||||
setLayout(std::move(descLayouts), std::move(pushConstants));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComputePipelineBuilder& ComputePipelineBuilder::shader(std::string &&path) {
|
||||
shaderPath.emplace(std::move(path));
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<Pipeline> ComputePipelineBuilder::build() const {
|
||||
if (!hasLayout())
|
||||
throw std::runtime_error("Pipeline " + getName() + " has no layout set");
|
||||
if (!shaderPath.has_value())
|
||||
throw std::runtime_error("Pipeline " + getName() + " has no shader set");
|
||||
|
||||
const auto shaderStages = createShader({{VK_SHADER_STAGE_COMPUTE_BIT, shaderPath.value()}});
|
||||
const auto shaderStageInfo = createShaderStage(shaderStages);
|
||||
if (shaderStageInfo.size() != 1)
|
||||
throw std::runtime_error("Compute pipeline " + getName() + " must have exactly one shader bound");
|
||||
|
||||
VkPipelineLayout layout;
|
||||
if (vkCreatePipelineLayout(VulkanModule::getInstance()->getDevice()->logical, &getLayout(), nullptr,
|
||||
&layout) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to create layout for compute pipeline " + getName());
|
||||
|
||||
const VkComputePipelineCreateInfo createInfo{
|
||||
VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
|
||||
nullptr, 0,
|
||||
shaderStageInfo[0], layout, VK_NULL_HANDLE, 0
|
||||
};
|
||||
|
||||
VkPipeline pipeline;
|
||||
if (vkCreateComputePipelines(VulkanModule::getInstance()->getDevice()->logical, VK_NULL_HANDLE, 1, &createInfo,
|
||||
nullptr, &pipeline) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to create compute pipeline " + getName());
|
||||
|
||||
return std::unique_ptr<Pipeline>{
|
||||
new Pipeline{getName(), pipeline, layout, VK_PIPELINE_BIND_POINT_COMPUTE}
|
||||
};
|
||||
}
|
||||
|
||||
void Pipeline::bind(const VkCommandBuffer &buffer) const {
|
||||
vkCmdBindPipeline(buffer, bindPoint, pipeline);
|
||||
}
|
||||
|
||||
Pipeline::~Pipeline() {
|
||||
vkDestroyPipeline(VulkanModule::getInstance()->getDevice()->logical, pipeline, nullptr);
|
||||
vkDestroyPipelineLayout(VulkanModule::getInstance()->getDevice()->logical, layout, nullptr);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,394 @@
|
|||
#include "vlkx/render/render_pass/GPUPass.h"
|
||||
|
||||
namespace vlkx {
|
||||
|
||||
inline bool needsSynchronization(const ImageUsage& prev, const ImageUsage& curr) {
|
||||
if (curr == prev && curr.getAccess() == ImageUsage::Access::ReadOnly)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void addUsageToSubpass(const ImageUsage& usage, RenderPassBuilder::SubpassDependency::SubpassMeta* pass) {
|
||||
pass->stage |= usage.getStage();
|
||||
pass->access |= usage.getAccessFlags();
|
||||
}
|
||||
|
||||
void CommonPass::addUsage(std::string &&name, UsageTracker &&tracker) {
|
||||
for (const auto& pair : tracker.getUsageMap())
|
||||
validate(pair.first, name, false);
|
||||
|
||||
tracker.add(getVirtualInitial(), tracker.getInitialUsage());
|
||||
if (tracker.getFinalUsage().has_value())
|
||||
tracker.add(getVirtualFinal(), tracker.getFinalUsage().value());
|
||||
|
||||
usageHistory.emplace(std::move(name), std::move(tracker));
|
||||
}
|
||||
|
||||
VkImageLayout CommonPass::getInitialLayout(const std::string &name) const {
|
||||
return getHistory(name).getUsageMap().begin()->second.getLayout();
|
||||
}
|
||||
|
||||
VkImageLayout CommonPass::getFinalLayout(const std::string &name) const {
|
||||
return getHistory(name).getUsageMap().rbegin()->second.getLayout();
|
||||
}
|
||||
|
||||
VkImageLayout CommonPass::getSubpassLayout(const std::string &name, int subpass) const {
|
||||
validate(subpass, name, false);
|
||||
return getUsage(name, subpass)->getLayout();
|
||||
}
|
||||
|
||||
void CommonPass::update(const std::string &name, MultiImageTracker &tracker) const {
|
||||
tracker.update(name, getHistory(name).getUsageMap().rbegin()->second);
|
||||
}
|
||||
|
||||
const UsageTracker& CommonPass::getHistory(const std::string &name) const {
|
||||
return usageHistory.at(name);
|
||||
}
|
||||
|
||||
const ImageUsage* CommonPass::getUsage(const std::string &name, int pass) const {
|
||||
validate(pass, name, true);
|
||||
const UsageTracker& history = getHistory(name);
|
||||
const auto iter = history.getUsageMap().find(pass);
|
||||
return iter != history.getUsageMap().end() ? &iter->second : nullptr;
|
||||
}
|
||||
|
||||
std::optional<CommonPass::Usages> CommonPass::checkForSync(const std::string &name, int pass) const {
|
||||
validate(pass, name, true);
|
||||
const UsageTracker& history = getHistory(name);
|
||||
const auto currIter = history.getUsageMap().find(pass);
|
||||
if (currIter == history.getUsageMap().end())
|
||||
return std::nullopt;
|
||||
const auto prevIter = std::prev(currIter);
|
||||
|
||||
const ImageUsage& prevUsage = prevIter->second;
|
||||
const ImageUsage& currUsage = currIter->second;
|
||||
|
||||
if (!needsSynchronization(prevUsage, currUsage))
|
||||
return std::nullopt;
|
||||
|
||||
const int prevSubpass = prevIter->first;
|
||||
return CommonPass::Usages { prevSubpass, &prevUsage, &currUsage };
|
||||
}
|
||||
|
||||
void CommonPass::validate(int pass, const std::string &image, bool includeVirtual) const {
|
||||
if (includeVirtual) {
|
||||
if (!(pass >= getVirtualInitial() && pass <= getVirtualFinal()))
|
||||
throw std::runtime_error("Subpass out of range.");
|
||||
} else {
|
||||
if (!(pass >= 0 && pass < numPasses))
|
||||
throw std::runtime_error("nv Subpass out of range.");
|
||||
}
|
||||
}
|
||||
|
||||
int GraphicsPass::add(const std::string &name, UsageTracker &&history, std::function<int(int)> &&getter,
|
||||
const std::optional<RenderPassBuilder::Attachment::OpsType> ops) {
|
||||
verifyHistory(name, history);
|
||||
|
||||
const std::optional<int> needsGetter = getFirstRenderTarget(history);
|
||||
if (needsGetter.has_value()) {
|
||||
if (getter == nullptr)
|
||||
throw std::runtime_error("Image " + name + " is used as a render target without a location getter.");
|
||||
} else {
|
||||
getter = nullptr;
|
||||
}
|
||||
|
||||
const int attachmentLocation = static_cast<int>(metas.size());
|
||||
metas.insert(
|
||||
{
|
||||
name,
|
||||
AttachmentMeta {
|
||||
attachmentLocation,
|
||||
std::move(getter),
|
||||
getOps(name, history, ops),
|
||||
{}
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
addUsage(std::string(name), std::move(history));
|
||||
return attachmentLocation;
|
||||
}
|
||||
|
||||
GraphicsPass& GraphicsPass::addMultisample(const std::string &source, const std::string &dest, int pass) {
|
||||
validate(pass, source, false);
|
||||
|
||||
const auto source_iter = usageHistory.find(source);
|
||||
if (source_iter == usageHistory.end())
|
||||
throw std::runtime_error("Usage history not found for source image " + source);
|
||||
|
||||
const UsageTracker& source_history = source_iter->second;
|
||||
if (!verifyImageUsage(source_history, pass, ImageUsage::Type::RenderTarget))
|
||||
throw std::runtime_error("Usage type for source image " + source + " must be render target.");
|
||||
|
||||
const auto dest_iter = usageHistory.find(dest);
|
||||
if (dest_iter == usageHistory.end())
|
||||
throw std::runtime_error("Usage history not found for destination image " + dest);
|
||||
|
||||
const UsageTracker& dest_history = dest_iter->second;
|
||||
if (!verifyImageUsage(dest_history, pass, ImageUsage::Type::Multisample))
|
||||
throw std::runtime_error("Usage type for destination image " + dest + " must be multisample");
|
||||
|
||||
auto& targetMap = metas[source].multisample;
|
||||
const bool inserted = targetMap.insert( { pass, dest }).second;
|
||||
|
||||
if (!inserted)
|
||||
throw std::runtime_error("Image " + source + " is already bound to a multisample.");
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void GraphicsPass::setAttachments() {
|
||||
for (const auto& pair : usageHistory) {
|
||||
const std::string& name = pair.first;
|
||||
const AttachmentMeta& meta = metas[name];
|
||||
builder->setAttachment(meta.index, { meta.ops, getInitialLayout(name), getFinalLayout(name) } );
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsPass::setSubpasses() {
|
||||
for (int pass = 0; pass < numPasses; ++pass) {
|
||||
std::vector<RenderPassBuilder::ColorAttachmentMeta> colors;
|
||||
std::vector<RenderPassBuilder::ColorAttachmentMeta> multisamples;
|
||||
VkAttachmentReference dsRef { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_UNDEFINED };
|
||||
|
||||
// Verify all images used, the long way around.
|
||||
for (const auto& pair : usageHistory) {
|
||||
const std::string& name = pair.first;
|
||||
const ImageUsage* history = getUsage(name, pass);
|
||||
if (history == nullptr)
|
||||
continue;
|
||||
|
||||
const AttachmentMeta& meta = metas[name];
|
||||
const VkAttachmentReference ref { static_cast<uint32_t>(meta.index), history->getLayout() };
|
||||
|
||||
switch (history->getType()) {
|
||||
// If we're looking at a render target, we need to figure out where it gets its' details from.
|
||||
case ImageUsage::Type::RenderTarget: {
|
||||
const int location = meta.getter(pass);
|
||||
const auto iter = meta.multisample.find(pass);
|
||||
if (iter != meta.multisample.end()) {
|
||||
const std::string& target = iter->second;
|
||||
const ImageUsage* targetUsage = getUsage(target, pass);
|
||||
if (targetUsage == nullptr)
|
||||
throw std::runtime_error("Expected target image to have a usage");
|
||||
multisamples.push_back( { location, metas[target].index, targetUsage->getLayout() });
|
||||
}
|
||||
|
||||
colors.push_back( { location, static_cast<int>(ref.attachment), ref.layout });
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ImageUsage::Type::DepthStencil:
|
||||
if (dsRef.attachment != VK_ATTACHMENT_UNUSED)
|
||||
throw std::runtime_error("Depth stencil used multiple times");
|
||||
dsRef = ref;
|
||||
break;
|
||||
|
||||
case ImageUsage::Type::Multisample:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Unreachable?");
|
||||
}
|
||||
}
|
||||
|
||||
auto colorRefs = RenderPassBuilder::parseColorReferences(colors);
|
||||
auto multisampleRefs = RenderPassBuilder::parseMutisampling(colorRefs.size(), multisamples);
|
||||
builder->setSubpass(pass, std::move(colorRefs), std::move(multisampleRefs), dsRef);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsPass::setDependencies() {
|
||||
if (getVirtualFinal() != numPasses)
|
||||
throw std::runtime_error("Virtual subpass mismatch");
|
||||
for (int pass = 0; pass <= numPasses; ++pass) {
|
||||
std::map<int, RenderPassBuilder::SubpassDependency> deps;
|
||||
|
||||
for (const auto& pair : usageHistory) {
|
||||
const auto usage = checkForSync(pair.first, pass);
|
||||
if (!usage.has_value()) continue;
|
||||
|
||||
const ImageUsage& prev = usage.value().lastUsage;
|
||||
const ImageUsage& curr = usage.value().currentUsage;
|
||||
const int sourcePass = usage.value().lastSubpass;
|
||||
|
||||
auto iter = deps.find(sourcePass);
|
||||
if (iter == deps.end()) {
|
||||
const RenderPassBuilder::SubpassDependency defaultDep {
|
||||
{ checkSubpass(sourcePass), VK_PIPELINE_STAGE_NONE_KHR, VK_ACCESS_NONE_KHR },
|
||||
{ checkSubpass(pass), VK_PIPELINE_STAGE_NONE_KHR, VK_ACCESS_NONE_KHR },
|
||||
0
|
||||
};
|
||||
|
||||
iter = deps.insert( { sourcePass, defaultDep } ).first;
|
||||
}
|
||||
|
||||
addUsageToSubpass(prev, &iter->second.source);
|
||||
addUsageToSubpass(curr, &iter->second.destination);
|
||||
}
|
||||
|
||||
for (const auto& pair : deps) {
|
||||
const RenderPassBuilder::SubpassDependency& dep = pair.second;
|
||||
builder->addDependency(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<RenderPassBuilder> GraphicsPass::build(int framebuffers) {
|
||||
builder = std::make_unique<RenderPassBuilder>();
|
||||
builder->setFramebufferCount(framebuffers);
|
||||
|
||||
setAttachments();
|
||||
setSubpasses();
|
||||
setDependencies();
|
||||
return std::move(builder);
|
||||
}
|
||||
|
||||
std::optional<int> GraphicsPass::getFirstRenderTarget(const UsageTracker &history) const {
|
||||
for (const auto& pair : history.getUsageMap()) {
|
||||
const int pass = pair.first;
|
||||
if (isVirtual(pass)) continue;
|
||||
|
||||
if (pair.second.getType() == ImageUsage::Type::RenderTarget) return pass;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
RenderPassBuilder::Attachment::OpsType GraphicsPass::getOps(const std::string &name, const UsageTracker &history,
|
||||
const std::optional<RenderPassBuilder::Attachment::OpsType> &userOps) const {
|
||||
const ImageUsage::Type type = getUsageType(name, history);
|
||||
|
||||
if (!userOps.has_value()) {
|
||||
switch (type) {
|
||||
case ImageUsage::Type::RenderTarget: return getDefaultOps();
|
||||
case ImageUsage::Type::DepthStencil: return getStencilOps();
|
||||
default:
|
||||
throw std::runtime_error("Unreachable?");
|
||||
}
|
||||
}
|
||||
|
||||
const RenderPassBuilder::Attachment::OpsType& ops = userOps.value();
|
||||
switch (type) {
|
||||
case ImageUsage::Type::RenderTarget:
|
||||
case ImageUsage::Type::DepthStencil:
|
||||
return ops;
|
||||
default:
|
||||
throw std::runtime_error("Unreachable?");
|
||||
}
|
||||
}
|
||||
|
||||
ImageUsage::Type GraphicsPass::getUsageType(const std::string &name, const UsageTracker &history) const {
|
||||
ImageUsage::Type prev = ImageUsage::Type::DontCare;
|
||||
|
||||
for (const auto& pair : history.getUsageMap()) {
|
||||
if (isVirtual(pair.first)) continue;
|
||||
|
||||
ImageUsage::Type type = pair.second.getType();
|
||||
if (type == ImageUsage::Type::Multisample)
|
||||
type = ImageUsage::Type::RenderTarget;
|
||||
|
||||
if (prev == ImageUsage::Type::DontCare) {
|
||||
prev = type;
|
||||
} else if (type != prev) {
|
||||
throw std::runtime_error("Inconsistent usage type specified for " + name);
|
||||
}
|
||||
}
|
||||
|
||||
if (prev == ImageUsage::Type::DontCare)
|
||||
throw std::runtime_error("Image " + name + " has no usages.");
|
||||
|
||||
return prev;
|
||||
}
|
||||
|
||||
bool GraphicsPass::verifyImageUsage(const UsageTracker &history, int subpass, ImageUsage::Type type) const {
|
||||
const auto iter = history.getUsageMap().find(subpass);
|
||||
return iter != history.getUsageMap().end() && iter->second.getType() == type;
|
||||
}
|
||||
|
||||
void GraphicsPass::verifyHistory(const std::string &image, const UsageTracker &history) const {
|
||||
for (const auto& pair : history.getUsageMap()) {
|
||||
const ImageUsage::Type type = pair.second.getType();
|
||||
if (type != ImageUsage::Type::RenderTarget && type != ImageUsage::Type::DepthStencil && type != ImageUsage::Type::Multisample)
|
||||
throw std::runtime_error("Invalid usage of " + image + " at subpass " + std::to_string(pair.first));
|
||||
}
|
||||
}
|
||||
|
||||
ComputePass &ComputePass::add(std::string &&name, UsageTracker &&history) {
|
||||
verify(name, history);
|
||||
addUsage(std::move(name), std::move(history));
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ComputePass::execute(const VkCommandBuffer &commands, uint32_t queueFamily,
|
||||
const std::map<std::string, const VkImage*> &images,
|
||||
const std::vector<std::function<void()>>& computeOps) const {
|
||||
|
||||
if (computeOps.size() != numPasses)
|
||||
throw std::runtime_error("Compute shader mismatches ops and passes.");
|
||||
|
||||
if (getVirtualFinal() != numPasses)
|
||||
throw std::runtime_error("Compute shader attempting to run too many subpasses");
|
||||
|
||||
for (int pass = 0; pass < numPasses; ++pass) {
|
||||
for (const auto& pair : usageHistory) {
|
||||
const std::string& image = pair.first;
|
||||
const auto usage = checkForSync(image, pass);
|
||||
if (!usage.has_value()) continue;
|
||||
|
||||
const auto iter = images.find(image);
|
||||
if (iter == images.end())
|
||||
throw std::runtime_error("Image " + image + " not provided");
|
||||
|
||||
barrier(commands, queueFamily, *iter->second, usage.value().lastUsage, usage.value().currentUsage);
|
||||
}
|
||||
|
||||
if (pass < numPasses)
|
||||
computeOps[pass]();
|
||||
}
|
||||
}
|
||||
|
||||
void ComputePass::barrier(const VkCommandBuffer &commands, uint32_t queueFamily, const VkImage &image,
|
||||
const ImageUsage &prev, const ImageUsage ¤t) const {
|
||||
const VkImageMemoryBarrier barrier {
|
||||
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
nullptr,
|
||||
prev.getAccessFlags(),
|
||||
current.getAccessFlags(),
|
||||
prev.getLayout(),
|
||||
current.getLayout(),
|
||||
queueFamily,
|
||||
queueFamily,
|
||||
image,
|
||||
{
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1
|
||||
}
|
||||
};
|
||||
|
||||
vkCmdPipelineBarrier(
|
||||
commands,
|
||||
prev.getStage(),
|
||||
current.getStage(),
|
||||
0,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
1,
|
||||
&barrier
|
||||
);
|
||||
}
|
||||
|
||||
void ComputePass::verify(const std::string &name, const UsageTracker &history) const {
|
||||
for (const auto& pair : history.getUsageMap()) {
|
||||
const ImageUsage::Type type = pair.second.getType();
|
||||
if (type != ImageUsage::Type::LinearAccess && type != ImageUsage::Type::Sampled && type != ImageUsage::Type::Transfer)
|
||||
throw std::runtime_error("Compute shader using an attachment that is not guranteed to be readable.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,268 @@
|
|||
#include "vlkx/render/render_pass/GenericRenderPass.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "vlkx/vulkan/VulkanModule.h"
|
||||
#include <limits>
|
||||
|
||||
namespace vlkx {
|
||||
|
||||
/**
|
||||
* Creates the necessary Clear Value struct to erase the given attachment.
|
||||
* @param attachment the attachment metadata
|
||||
* @return the Clear Value that will erase the attachment's data
|
||||
*/
|
||||
VkClearValue createClearFor(const RenderPassBuilder::Attachment& attachment) {
|
||||
|
||||
VkClearValue clear {};
|
||||
if (std::holds_alternative<RenderPassBuilder::Attachment::ColorOps>(attachment.ops))
|
||||
clear.color = { { 0, 0, 0, 0 } };
|
||||
else {
|
||||
clear.depthStencil = { 1.0, 0 };
|
||||
}
|
||||
|
||||
return clear;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a RenderPassBuilder attachment to a VkAttachmentDescription.
|
||||
* Format will be UNDEFINED.
|
||||
* Sample count will be 1.
|
||||
*/
|
||||
VkAttachmentDescription buildAttachment(const RenderPassBuilder::Attachment& attachment) {
|
||||
VkAttachmentDescription descriptor {
|
||||
{},
|
||||
VK_FORMAT_UNDEFINED,
|
||||
VK_SAMPLE_COUNT_1_BIT,
|
||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
attachment.layoutInitial,
|
||||
attachment.layoutFinal
|
||||
};
|
||||
|
||||
if (const auto& ops = std::get_if<RenderPassBuilder::Attachment::ColorOps>(&attachment.ops); ops != nullptr) {
|
||||
descriptor.loadOp = ops->LOAD;
|
||||
descriptor.storeOp = ops->STORE;
|
||||
} else if (const auto& ops = std::get_if<RenderPassBuilder::Attachment::StencilDepthOps>(&attachment.ops); ops != nullptr) {
|
||||
descriptor.loadOp = ops->DEPTH_LOAD;
|
||||
descriptor.storeOp = ops->DEPTH_STORE;
|
||||
descriptor.stencilLoadOp = ops->STENCIL_LOAD;
|
||||
descriptor.stencilStoreOp = ops->STENCIL_STORE;
|
||||
}
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
std::vector<VkSubpassDescription> buildSubpassDescriptors(const std::vector<RenderPassBuilder::SubpassAttachments>& attachments) {
|
||||
std::vector<VkSubpassDescription> descriptors;
|
||||
descriptors.reserve(attachments.size());
|
||||
|
||||
for (const auto& attachment : attachments) {
|
||||
descriptors.emplace_back(VkSubpassDescription {
|
||||
{},
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
0,
|
||||
nullptr,
|
||||
static_cast<uint32_t>(attachment.colorReferences.size()),
|
||||
attachment.colorReferences.data(),
|
||||
attachment.multisampleReferences.empty() ? nullptr : attachment.multisampleReferences.data(),
|
||||
attachment.stencilDepthReference.has_value() ? &attachment.stencilDepthReference.value() : nullptr,
|
||||
0,
|
||||
nullptr
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
VkSubpassDependency buildSubpassDependency(const RenderPassBuilder::SubpassDependency& dep) {
|
||||
return VkSubpassDependency {
|
||||
dep.source.index,
|
||||
dep.destination.index,
|
||||
dep.source.stage,
|
||||
dep.destination.stage,
|
||||
dep.source.access,
|
||||
dep.destination.access,
|
||||
dep.flags
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<int> countColorAttachments(const std::vector<RenderPassBuilder::SubpassAttachments>& attachments) {
|
||||
std::vector<int> count;
|
||||
count.reserve(attachments.size());
|
||||
|
||||
for (const auto& attachment : attachments)
|
||||
count.emplace_back(attachment.colorReferences.size());
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
std::vector<VkFramebuffer> createFramebuffers(const VkRenderPass& renderPass, const std::vector<std::function<const Image&(int idx)>> getters, int count, const VkExtent2D& extent) {
|
||||
std::vector<VkImageView> views(getters.size());
|
||||
|
||||
VkFramebufferCreateInfo framebufferCreate {
|
||||
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
nullptr,
|
||||
{},
|
||||
renderPass,
|
||||
static_cast<uint32_t>(views.size()),
|
||||
views.data(),
|
||||
extent.width,
|
||||
extent.height,
|
||||
1
|
||||
};
|
||||
|
||||
std::vector<VkFramebuffer> framebuffers(count);
|
||||
for (int i = 0; i < framebuffers.size(); ++i) {
|
||||
for (int image = 0; image < getters.size(); image++)
|
||||
views[image] = getters[image](i).getView();
|
||||
|
||||
vkCreateFramebuffer(VulkanModule::getInstance()->getDevice()->logical, &framebufferCreate, nullptr, &framebuffers[i]);
|
||||
}
|
||||
|
||||
return framebuffers;
|
||||
}
|
||||
|
||||
std::vector<VkAttachmentReference> RenderPassBuilder::parseColorReferences(std::vector<ColorAttachmentMeta> meta) {
|
||||
if (meta.empty())
|
||||
return {};
|
||||
|
||||
// Search for the highest location index'd attachment reference.
|
||||
// Shaders can define negative locations, so we have to start as low as we can go,
|
||||
// and work our way up until we find the highest.
|
||||
// If we start at 0, we risk missing an all-negative location set.
|
||||
int max = std::numeric_limits<int>::min();
|
||||
for (const auto& attachment : meta)
|
||||
max = std::max(max, attachment.location);
|
||||
|
||||
std::vector<VkAttachmentReference> references(max + 1, { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_UNDEFINED });
|
||||
for (const auto& attachment : meta)
|
||||
references[attachment.location] = { static_cast<uint32_t>(attachment.descriptionIdx), attachment.layout };
|
||||
|
||||
return references;
|
||||
}
|
||||
|
||||
std::vector<VkAttachmentReference> RenderPassBuilder::parseMutisampling(int colorReferencesCount, std::vector<ColorAttachmentMeta> meta) {
|
||||
std::vector<VkAttachmentReference> references(colorReferencesCount, { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_UNDEFINED });
|
||||
for (const auto& attachment : meta)
|
||||
references[attachment.location] = { static_cast<uint32_t>(attachment.descriptionIdx), attachment.layout };
|
||||
|
||||
return references;
|
||||
}
|
||||
|
||||
fluent RenderPassBuilder::setFramebufferCount(int count) {
|
||||
framebufferCount = count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
fluent RenderPassBuilder::setAttachment(int idx, const Attachment &attachment) {
|
||||
if (idx > clearValues.size())
|
||||
clearValues.resize(idx + 1);
|
||||
clearValues.at(idx) = createClearFor(attachment);
|
||||
|
||||
if (idx > attachmentDescriptors.size())
|
||||
attachmentDescriptors.resize(idx + 1);
|
||||
attachmentDescriptors.at(idx) = buildAttachment(attachment);
|
||||
|
||||
if (attachmentDescriptors.size() > attachmentGetters.size())
|
||||
attachmentGetters.resize(attachmentDescriptors.size());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
fluent RenderPassBuilder::updateAttachmentBacking(int idx,
|
||||
std::function<const Image &(int)> &&getBacking) {
|
||||
const Image& img = getBacking(idx);
|
||||
attachmentDescriptors[idx].format = img.getFormat();
|
||||
attachmentDescriptors[idx].samples = img.getSamples();
|
||||
attachmentGetters.at(idx) = std::move(getBacking);
|
||||
return *this;
|
||||
}
|
||||
|
||||
fluent RenderPassBuilder::setSubpass(int idx, std::vector<VkAttachmentReference> &&color,
|
||||
std::vector<VkAttachmentReference> &&multisample,
|
||||
VkAttachmentReference &depthStencil) {
|
||||
if (multisample.empty())
|
||||
if (multisample.size() != color.size())
|
||||
throw std::runtime_error("Constructing a subpass with mismatched color and multisample attachments");
|
||||
|
||||
SubpassAttachments attachments {
|
||||
std::move(color), std::move(multisample), depthStencil
|
||||
};
|
||||
|
||||
subpassAttachments.emplace_back(attachments);
|
||||
return *this;
|
||||
}
|
||||
|
||||
fluent RenderPassBuilder::addDependency(const SubpassDependency &dep) {
|
||||
subpassDependencies.emplace_back(buildSubpassDependency(dep));
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<RenderPass> RenderPassBuilder::build() const {
|
||||
if (framebufferCount == 0)
|
||||
throw std::runtime_error("No framebuffers in render pass");
|
||||
for (int i = 0; i < attachmentGetters.size(); i++)
|
||||
if (attachmentGetters[i] == nullptr)
|
||||
throw std::runtime_error("Image " + std::to_string(i) + " is not set in render pass");
|
||||
|
||||
const auto descriptors = buildSubpassDescriptors(subpassAttachments);
|
||||
const VkRenderPassCreateInfo createInfo {
|
||||
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
nullptr,
|
||||
{},
|
||||
static_cast<uint32_t>(attachmentDescriptors.size()),
|
||||
attachmentDescriptors.data(),
|
||||
static_cast<uint32_t>(descriptors.size()),
|
||||
descriptors.data(),
|
||||
static_cast<uint32_t>(subpassDependencies.size()),
|
||||
subpassDependencies.data()
|
||||
};
|
||||
|
||||
VkRenderPass pass;
|
||||
if (vkCreateRenderPass(VulkanModule::getInstance()->getDevice()->logical, &createInfo, nullptr, &pass) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to create render pass");
|
||||
|
||||
const auto framebufferSize = attachmentGetters[0](0).getExtent();
|
||||
|
||||
return std::make_unique<RenderPass> (
|
||||
static_cast<int>(descriptors.size()), pass, clearValues, framebufferSize, createFramebuffers(pass, attachmentGetters, framebufferCount.value(), framebufferSize),
|
||||
countColorAttachments(subpassAttachments)
|
||||
);
|
||||
}
|
||||
|
||||
void RenderPass::execute(const VkCommandBuffer &commands, int imageIndex,
|
||||
std::vector<std::function<void(const VkCommandBuffer &)>> ops) const {
|
||||
const VkRenderPassBeginInfo begin {
|
||||
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
nullptr,
|
||||
renderPass,
|
||||
framebuffers[imageIndex],
|
||||
{
|
||||
{0, 0},
|
||||
extent
|
||||
},
|
||||
static_cast<uint32_t>(clearValues.size()),
|
||||
clearValues.data()
|
||||
};
|
||||
|
||||
vkCmdBeginRenderPass(commands, &begin, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
for (int i = 0; i < ops.size(); i++) {
|
||||
if (i != 0)
|
||||
vkCmdNextSubpass(commands, VK_SUBPASS_CONTENTS_INLINE);
|
||||
ops[i](commands);
|
||||
}
|
||||
|
||||
vkCmdEndRenderPass(commands);
|
||||
}
|
||||
|
||||
RenderPass::~RenderPass() {
|
||||
for (const auto& fb : framebuffers)
|
||||
vkDestroyFramebuffer(VulkanModule::getInstance()->getDevice()->logical, fb, nullptr);
|
||||
vkDestroyRenderPass(VulkanModule::getInstance()->getDevice()->logical, renderPass, nullptr);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
#include "vlkx/render/render_pass/ScreenRenderPass.h"
|
||||
#include "vlkx/vulkan/abstraction/Image.h"
|
||||
#include "vlkx/render/render_pass/GPUPass.h"
|
||||
#include "vlkx/vulkan/VulkanModule.h"
|
||||
|
||||
namespace vlkx {
|
||||
|
||||
void checkSubpass(int pass, int high) { if (pass < 1 || pass > high) throw std::runtime_error("Subpass index too high"); }
|
||||
|
||||
void addAttachment(const AttachmentConfig& config, GraphicsPass& pass, MultiImageTracker& tracker, GraphicsPass::LocationGetter&& getter, const std::function<void(UsageTracker&)>& populateHistory) {
|
||||
const std::string& name = config.name;
|
||||
if (!tracker.isTracking(name))
|
||||
throw std::runtime_error("Attempting to add image " + name + " that is not tracked");
|
||||
|
||||
UsageTracker history { tracker.get(name) };
|
||||
populateHistory(history);
|
||||
|
||||
if (config.finalUsage.has_value())
|
||||
history.setFinal(config.finalUsage.value());
|
||||
|
||||
config.index = pass.add(name, std::move(history), std::move(getter), config.loadStoreOps);
|
||||
pass.update(name, tracker);
|
||||
}
|
||||
|
||||
RendererConfig::RendererConfig(int passCount, std::vector<std::unique_ptr<vlkx::Image>>& destinations, bool toScreen, std::optional<int> firstTransparent, std::optional<int> firstOverlay) : renderImages(destinations) {
|
||||
if (passCount < 1)
|
||||
throw std::runtime_error("Creating a RendererConfig with less than 1 subpass.");
|
||||
|
||||
if (firstTransparent.has_value())
|
||||
checkSubpass(firstTransparent.value(), passCount);
|
||||
if (firstOverlay.has_value())
|
||||
checkSubpass(firstOverlay.value(), passCount);
|
||||
|
||||
if (firstOverlay.has_value())
|
||||
numOverlayPasses = passCount - firstOverlay.value();
|
||||
if (firstTransparent.has_value()) {
|
||||
numOpaquePasses = firstTransparent.value();
|
||||
numTransparentPasses = passCount - numOpaquePasses - numOverlayPasses;
|
||||
} else {
|
||||
numOpaquePasses = passCount - numOverlayPasses;
|
||||
}
|
||||
|
||||
rendersToScreen = toScreen;
|
||||
}
|
||||
|
||||
std::unique_ptr<RenderPassBuilder> SimpleRenderPass::createBuilder(int framebuffers,
|
||||
const vlkx::RendererConfig &config,
|
||||
const vlkx::AttachmentConfig &color,
|
||||
const vlkx::AttachmentConfig *multisample,
|
||||
const vlkx::AttachmentConfig *depthStencil,
|
||||
vlkx::MultiImageTracker &tracker) {
|
||||
const int passes = config.passes();
|
||||
const int firstPass = 0;
|
||||
const int lastPass = passes - 1;
|
||||
|
||||
const auto getLocation = [](int pass) { return 0; };
|
||||
bool usesMultisample = multisample != nullptr;
|
||||
bool usesDepth = depthStencil != nullptr;
|
||||
|
||||
const bool passesUsingDepth = config.depthPasses() > 0;
|
||||
|
||||
if (usesDepth) {
|
||||
if (passesUsingDepth == 0)
|
||||
throw std::runtime_error("Depth stencil defined, but never used.");
|
||||
} else
|
||||
if (passesUsingDepth > 0)
|
||||
throw std::runtime_error("Depth stencil used, but never defined.");
|
||||
|
||||
GraphicsPass graphicsPass(passes);
|
||||
|
||||
addAttachment(color, graphicsPass, tracker, getLocation, [&](UsageTracker& history) {
|
||||
if (usesMultisample)
|
||||
history.add(lastPass, ImageUsage::multisample());
|
||||
else
|
||||
history.add(firstPass, lastPass, ImageUsage::renderTarget(0));
|
||||
});
|
||||
|
||||
if (usesMultisample) {
|
||||
addAttachment(*multisample, graphicsPass, tracker, getLocation, [&](UsageTracker& history) {
|
||||
history.add(firstPass, lastPass, ImageUsage::renderTarget(0));
|
||||
});
|
||||
graphicsPass.addMultisample(multisample->name, color.name, lastPass);
|
||||
}
|
||||
|
||||
if (usesDepth)
|
||||
addAttachment(*depthStencil, graphicsPass, tracker, getLocation, [&](UsageTracker& history) {
|
||||
if (config.numOpaquePasses > 0) {
|
||||
const int lastOpaque = config.numOpaquePasses - 1;
|
||||
history.add(firstPass, lastOpaque, ImageUsage::depthStencil(ImageUsage::Access::ReadWrite));
|
||||
}
|
||||
|
||||
if (config.numTransparentPasses > 0) {
|
||||
const int firstTransparent = config.numOpaquePasses;
|
||||
const int lastTransparent = firstTransparent + config.numTransparentPasses - 1;
|
||||
history.add(firstTransparent, lastTransparent, ImageUsage::depthStencil(ImageUsage::Access::ReadOnly));
|
||||
}
|
||||
});
|
||||
|
||||
return graphicsPass.build(framebuffers);
|
||||
|
||||
}
|
||||
|
||||
void ScreenRenderPassManager::initializeRenderPass() {
|
||||
if (config.usesDepth()) {
|
||||
depthStencilImage = MultisampleImage::createDepthStencil(VulkanModule::getInstance()->getSwapchain()->extent, std::nullopt);
|
||||
}
|
||||
|
||||
if (passBuilder == nullptr)
|
||||
preparePassBuilder();
|
||||
|
||||
passBuilder->updateAttachmentBacking(destinationInfo.index.value(), [this](int index) -> const Image& {
|
||||
return *config.renderImages[index];
|
||||
});
|
||||
|
||||
if (depthStencilImage != nullptr)
|
||||
passBuilder->updateAttachmentBacking(depthStencilInfo.index.value(), [this](int index) -> const Image& {
|
||||
return *depthStencilImage;
|
||||
});
|
||||
|
||||
pass = passBuilder->build();
|
||||
}
|
||||
|
||||
void ScreenRenderPassManager::preparePassBuilder() {
|
||||
const bool usesDepth = depthStencilImage != nullptr;
|
||||
const bool usesMultisampling = false;
|
||||
|
||||
MultiImageTracker tracker;
|
||||
destinationInfo.add(tracker, *config.renderImages[0]);
|
||||
if (usesDepth)
|
||||
depthStencilInfo.add(tracker, *depthStencilImage);
|
||||
if (usesMultisampling)
|
||||
// TODO
|
||||
(void) 0;
|
||||
|
||||
auto colorConfig = destinationInfo.makeConfig();
|
||||
if (config.rendersToScreen)
|
||||
colorConfig.setUsage(ImageUsage::presentation());
|
||||
else
|
||||
colorConfig.setUsage(ImageUsage::sampledFragment());
|
||||
const auto multisampleConfig = multisampleInfo.makeConfig();
|
||||
const auto depthConfig = depthStencilInfo.makeConfig();
|
||||
|
||||
passBuilder = SimpleRenderPass::createBuilder(config.renderImages.size(), config, colorConfig, usesMultisampling ? &multisampleConfig : nullptr, usesDepth ? &depthConfig : nullptr, tracker);
|
||||
}
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
#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;
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
#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, false);
|
||||
|
||||
// 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);
|
||||
}
|
|
@ -1,200 +0,0 @@
|
|||
#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);
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
#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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
#include "temp/model/Builder.h"
|
||||
|
||||
namespace vlkxtemp {
|
||||
using namespace vlkx;
|
||||
using Geo::VertexAll;
|
||||
using VertexData = PerVertexBuffer::NoShareMeta;
|
||||
|
||||
std::unique_ptr<SamplableImage> createTex(const ModelBuilder::TextureSource& source) {
|
||||
const auto usages = { ImageUsage::sampledFragment() };
|
||||
return std::make_unique<RefCountedTexture>(source, usages, ImageSampler::Config {});
|
||||
}
|
||||
|
||||
void fillTextureMeta(const ModelBuilder::BindingPoints& binding, const ModelBuilder::TexturePerMesh& textures, const ModelBuilder::TexturePerMesh& sharedTextures, Descriptor::Meta* meta, Descriptor::ImageInfos* infos) {
|
||||
*meta = {Image::getSampleType(), VK_SHADER_STAGE_FRAGMENT_BIT, {} };
|
||||
auto& texBindings = meta->bindings;
|
||||
|
||||
for (size_t idx = 0; idx < (size_t) ModelBuilder::TextureType::Count; idx++) {
|
||||
const auto type = (ModelBuilder::TextureType) idx;
|
||||
const size_t numTextures = textures[idx].size() + sharedTextures[idx].size();
|
||||
if (numTextures != 0) {
|
||||
const auto iter = binding.find(type);
|
||||
if (iter == binding.end())
|
||||
throw std::runtime_error("Binding point of texture type " + std::to_string(idx) + " is not set");
|
||||
|
||||
texBindings.push_back({ iter->second, static_cast<uint32_t>(numTextures) });
|
||||
|
||||
auto& metaMap = (*infos)[iter->second];
|
||||
metaMap.reserve(numTextures);
|
||||
for (const auto& texture : textures[idx])
|
||||
metaMap.push_back(texture->getInfoForSampling());
|
||||
for (const auto& texture : sharedTextures[idx])
|
||||
metaMap.push_back(texture->getInfoForSampling());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<VkPushConstantRange> createRanges(const ModelBuilder::ModelPushConstant& constants) {
|
||||
std::vector<VkPushConstantRange> ranges;
|
||||
ranges.reserve(constants.constants.size());
|
||||
for (const auto& meta : constants.constants)
|
||||
ranges.push_back( { constants.stage, meta.offset, meta.constants->getSize() });
|
||||
return ranges;
|
||||
}
|
||||
|
||||
VkVertexInputBindingDescription getBinding(uint32_t stride, bool instancing) {
|
||||
return VkVertexInputBindingDescription{ 0, stride,instancing ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX, };
|
||||
}
|
||||
|
||||
void setVertexInput(const PerVertexBuffer& buffer, const std::vector<PerInstanceVertexBuffer*>& instanceBuffers, GraphicsPipelineBuilder* builder) {
|
||||
uint32_t start = 0;
|
||||
auto attributes = buffer.getAttrs(start);
|
||||
start += attributes.size();
|
||||
|
||||
builder->addVertex(0, Geo::VertexAll::getBindingDesc(), std::move(attributes));
|
||||
|
||||
for (size_t i = 0; i < instanceBuffers.size(); i++) {
|
||||
if (instanceBuffers[i] == nullptr)
|
||||
throw std::runtime_error("PerInstanceVertexBuffer not specified");
|
||||
auto instanceAttrs = instanceBuffers[i]->getAttrs(start);
|
||||
start += instanceAttrs.size();
|
||||
|
||||
auto instanceBinding = getBinding(instanceBuffers[i]->getSize(), true);
|
||||
builder->addVertex(i + 1, std::move(instanceBinding), std::move(instanceAttrs));
|
||||
}
|
||||
}
|
||||
|
||||
void ModelBuilder::SingleMeshModel::load(ModelBuilder* builder) const {
|
||||
const Wavefront obj(objFile, objIndexBase);
|
||||
VertexData vertices {
|
||||
{{
|
||||
PerVertexBuffer::VertexDataMeta { obj.indices },
|
||||
PerVertexBuffer::VertexDataMeta { obj.vertices }
|
||||
}}
|
||||
};
|
||||
|
||||
builder->vertexBuffer = std::make_unique<StaticPerVertexBuffer>(std::move(vertices), Geo::VertexAll::getAttributeDesc());
|
||||
|
||||
auto& meshTexs = builder->textures;
|
||||
meshTexs.push_back({});
|
||||
for (const auto& pair : textureSources) {
|
||||
const auto type = (size_t)pair.first;
|
||||
const auto& sources = pair.second;
|
||||
|
||||
meshTexs.back()[type].reserve(sources.size());
|
||||
for (const auto& source : sources)
|
||||
meshTexs.back()[type].push_back({createTex(source)});
|
||||
}
|
||||
}
|
||||
|
||||
void ModelBuilder::MultiMeshModel::load(ModelBuilder* builder) const {
|
||||
const ModelLoader loader(models, textures);
|
||||
std::vector<VertexData::PerMesh> data;
|
||||
data.reserve(loader.getMeshes().size());
|
||||
|
||||
for (const auto& mesh : loader.getMeshes())
|
||||
data.push_back({ PerVertexBuffer::VertexDataMeta { mesh.indices }, PerVertexBuffer::VertexDataMeta { mesh.vertices } });
|
||||
|
||||
builder->vertexBuffer = std::make_unique<StaticPerVertexBuffer>(VertexData { std::move(data)}, Geo::VertexAll::getAttributeDesc());
|
||||
|
||||
const auto usages = { ImageUsage::sampledFragment() };
|
||||
auto& meshTexs = builder->textures;
|
||||
meshTexs.reserve(loader.getMeshes().size());
|
||||
for (const auto& mesh : loader.getMeshes()) {
|
||||
meshTexs.push_back({});
|
||||
for (const auto& tex : mesh.textures)
|
||||
meshTexs.back()[(size_t) tex.type].push_back(std::make_unique<RefCountedTexture>(tex.path, usages, ImageSampler::Config {}));
|
||||
}
|
||||
}
|
||||
|
||||
ModelBuilder::ModelBuilder(std::string &&name, int frames, float aspect, const ModelResource &resource)
|
||||
: frames(frames), aspectRatio(aspect), uniformBufferMeta(frames), pipelineBuilder(std::make_unique<GraphicsPipelineBuilder>()) {
|
||||
pipelineBuilder->name(std::move(name));
|
||||
resource.load(this);
|
||||
}
|
||||
|
||||
ModelBuilder& ModelBuilder::texture(TextureType type, const TextureSource &source) {
|
||||
sharedTextures[(size_t) type].push_back(createTex(source));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ModelBuilder& ModelBuilder::bindTextures(TextureType type, uint32_t point) {
|
||||
bindPoints[type] = point;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ModelBuilder& ModelBuilder::instanceBuffer(vlkx::PerInstanceVertexBuffer* buffer) {
|
||||
instanceBuffers.push_back(buffer);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ModelBuilder& ModelBuilder::uniform(VkShaderStageFlags stage, std::vector<vlkx::Descriptor::Meta::Binding> &&bindings) {
|
||||
uniformMeta.push_back({ UniformBuffer::getDescriptorType(), stage, std::move(bindings) });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ModelBuilder& ModelBuilder::uniformBuffer(uint32_t point, const vlkx::UniformBuffer &buffer) {
|
||||
for (size_t frame = 0; frame < frames; frame++) {
|
||||
const int chunk = buffer.isSingle() ? 0 : frame;
|
||||
uniformBufferMeta[frame][point].push_back( buffer.getDescriptorInfo(chunk) );
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ModelBuilder& ModelBuilder::pushStage(VkShaderStageFlags stage) {
|
||||
if (!pushConstants.has_value())
|
||||
pushConstants.emplace();
|
||||
|
||||
pushConstants->stage = stage;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ModelBuilder& ModelBuilder::pushConstant(const vlkx::PushConstant* constant, uint32_t offset) {
|
||||
if (!pushConstants.has_value())
|
||||
pushConstants.emplace();
|
||||
|
||||
pushConstants.value().constants.push_back( ModelPushConstant::Meta { constant, offset });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ModelBuilder& ModelBuilder::shader(VkShaderStageFlagBits stage, std::string &&file) {
|
||||
pipelineBuilder->shader(stage, std::move(file));
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<ModelBuilder::Descriptors> ModelBuilder::createDescs() const {
|
||||
std::vector<Descriptors> descs(frames);
|
||||
auto infos = uniformMeta;
|
||||
infos.resize(infos.size() + 1);
|
||||
|
||||
for (size_t frame = 0; frame < frames; frame++) {
|
||||
descs[frame].reserve(textures.size());
|
||||
|
||||
for (const auto& tex : textures) {
|
||||
Descriptor::ImageInfos image;
|
||||
fillTextureMeta(bindPoints, tex, sharedTextures, &infos.back(), &image);
|
||||
|
||||
descs[frame].push_back(std::make_unique<StaticDescriptor>(infos));
|
||||
descs[frame].back()->buffers(UniformBuffer::getDescriptorType(), uniformBufferMeta[frame]);
|
||||
descs[frame].back()->images(Image::getSampleType(), image);
|
||||
}
|
||||
}
|
||||
|
||||
return descs;
|
||||
}
|
||||
|
||||
std::unique_ptr<Model> ModelBuilder::build() {
|
||||
if (pushConstants.has_value())
|
||||
if (pushConstants->constants.empty())
|
||||
throw std::runtime_error("Model sets push constant present but no data.");
|
||||
|
||||
auto descs = createDescs();
|
||||
pipelineBuilder->layout(
|
||||
{ descs[0][0]->getLayout() },
|
||||
pushConstants.has_value() ? createRanges(pushConstants.value()) : std::vector<VkPushConstantRange> {}
|
||||
);
|
||||
|
||||
setVertexInput(*vertexBuffer, instanceBuffers, pipelineBuilder.get());
|
||||
|
||||
uniformMeta.clear();
|
||||
uniformBufferMeta.clear();
|
||||
|
||||
return std::unique_ptr<Model> {
|
||||
new Model {
|
||||
aspectRatio, std::move(vertexBuffer), std::move(instanceBuffers), std::move(pushConstants),
|
||||
std::move(sharedTextures), std::move(textures), std::move(descs), std::move(pipelineBuilder)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void Model::update(bool opaque, const VkExtent2D &frame, VkSampleCountFlagBits samples,
|
||||
const vlkx::RenderPass &pass, uint32_t subpass, bool flipY) {
|
||||
|
||||
pipeline = (*pipelineBuilder)
|
||||
.depthTest(true, opaque)
|
||||
.multiSample(samples)
|
||||
.viewport({ { 0, 0, static_cast<float>(frame.width), static_cast<float>(frame.height), 0, 1 }, { { 0, 0 }, frame } })
|
||||
.renderPass(*pass, subpass)
|
||||
.colorBlend(std::vector<VkPipelineColorBlendAttachmentState>(pass.getAttachsInSubpass(subpass), vlkx::Pipeline::getAlphaBlendState(!opaque)))
|
||||
.build();
|
||||
}
|
||||
|
||||
void Model::draw(const VkCommandBuffer &commands, int frame, uint32_t instances) const {
|
||||
pipeline->bind(commands);
|
||||
|
||||
for (size_t i = 0; i < perInstanceBuffers.size(); i++)
|
||||
perInstanceBuffers[i]->bind(commands, i + 1, 0);
|
||||
|
||||
if (pushConstants.has_value())
|
||||
for (const auto& meta : pushConstants->constants)
|
||||
meta.constants->upload(commands, pipeline->getLayout(), frame, meta.offset, pushConstants->stage);
|
||||
|
||||
for (size_t mesh = 0; mesh < textures.size(); mesh++) {
|
||||
descriptors[frame][mesh]->bind(commands, pipeline->getLayout(), pipeline->getBind());
|
||||
vertexBuffer->draw(commands, 0, mesh, instances);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
#include "temp/model/Loader.h"
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
namespace vlkxtemp {
|
||||
|
||||
std::vector<std::string> split (std::string_view s, char delim) {
|
||||
std::vector<std::string> result;
|
||||
std::stringstream ss ((std::string(s)));
|
||||
std::string item;
|
||||
|
||||
while (std::getline (ss, item, delim)) {
|
||||
result.push_back (item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Wavefront::Wavefront(std::string_view path, size_t index_base) {
|
||||
std::ifstream file(std::string(path), std::ios::binary);
|
||||
|
||||
std::vector<glm::vec3> positions;
|
||||
std::vector<glm::vec3> normals;
|
||||
std::vector<glm::vec2> tex_coords;
|
||||
std::map<std::string, uint32_t> loaded_vertices;
|
||||
|
||||
const auto parse_line = [&](std::string_view line) {
|
||||
const size_t non_space = line.find_first_not_of(' ');
|
||||
if (non_space == std::string::npos || line[0] == '#')
|
||||
return;
|
||||
|
||||
switch (line[non_space]) {
|
||||
case 'v': {
|
||||
switch (line[non_space + 1]) {
|
||||
case ' ': {
|
||||
const auto nums = split(line.substr(non_space + 2), ' ');
|
||||
positions.emplace_back(stof(nums[0]), stof(nums[1]), stof(nums[2]));
|
||||
break;
|
||||
}
|
||||
case 'n': {
|
||||
// Normal.
|
||||
const auto nums = split(line.substr(non_space + 3), ' ');
|
||||
normals.emplace_back(glm::vec3{stof(nums[0]), stof(nums[1]), stof(nums[2])});
|
||||
break;
|
||||
}
|
||||
case 't': {
|
||||
// Texture coordinates.
|
||||
const auto nums = split(line.substr(non_space + 3), ' ');
|
||||
tex_coords.emplace_back(glm::vec2{stof(nums[0]), stof(nums[1])});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("Unexpected symbol " + std::to_string(line[non_space + 1]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'f': {
|
||||
for (const auto& seg : split(line.substr(non_space + 2), ' ')) {
|
||||
const auto iter = loaded_vertices.find(seg);
|
||||
if (iter != loaded_vertices.end()) {
|
||||
indices.push_back(iter->second);
|
||||
} else {
|
||||
indices.push_back(vertices.size());
|
||||
loaded_vertices[seg] = vertices.size();
|
||||
const auto idxs = split(seg, '/');
|
||||
vertices.push_back(Geo::VertexAll {
|
||||
positions.at(stoi(idxs[0]) - index_base),
|
||||
normals.at(stoi(idxs[2]) - index_base),
|
||||
tex_coords.at(stoi(idxs[1]) - index_base),
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("Unexpected symbol in OBJ file: " + std::to_string(line[non_space]));
|
||||
}
|
||||
};
|
||||
|
||||
std::string line;
|
||||
int line_num = 1;
|
||||
try {
|
||||
for (; std::getline(file, line); ++line_num)
|
||||
parse_line(line);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Failed to parse obj file, error on line " + std::to_string(line_num) + ": " + line + "; " + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
ModelLoader::ModelLoader(const std::string &model, const std::string &textures) {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
#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);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
#include <vlkx/vulkan/SwapChain.h>
|
||||
#include <vlkx/vulkan/VulkanManager.h>
|
||||
#include <vlkx/vulkan/VulkanModule.h>
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
SwapChain::SwapChain() {}
|
||||
|
@ -45,7 +45,7 @@ VkExtent2D SwapChain::chooseExtent(const VkSurfaceCapabilitiesKHR& capabilities)
|
|||
}
|
||||
|
||||
void SwapChain::create(VkSurfaceKHR surface) {
|
||||
SwapChainMeta info = VulkanManager::getInstance()->getDevice()->swapChain;
|
||||
SwapChainMeta info = VulkanModule::getInstance()->getDevice()->swapChain;
|
||||
|
||||
VkSurfaceFormatKHR chosenFormat = chooseFormat(info.formats);
|
||||
VkPresentModeKHR chosenMode = chooseMode(info.modes);
|
||||
|
@ -66,7 +66,7 @@ void SwapChain::create(VkSurfaceKHR surface) {
|
|||
createInfo.imageArrayLayers = 1; // 2 for VR
|
||||
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
|
||||
QueueFamilies queues = VulkanManager::getInstance()->getDevice()->getQueues();
|
||||
QueueFamilies queues = VulkanModule::getInstance()->getDevice()->getQueues();
|
||||
uint32_t indices[] = { static_cast<uint32_t>(queues.graphics), static_cast<uint32_t>(queues.presentation) };
|
||||
|
||||
if (queues.graphics != queues.presentation) {
|
||||
|
@ -86,19 +86,32 @@ void SwapChain::create(VkSurfaceKHR surface) {
|
|||
createInfo.oldSwapchain = VK_NULL_HANDLE;
|
||||
|
||||
// Create the swap-chain
|
||||
if (vkCreateSwapchainKHR(VulkanManager::getInstance()->getDevice()->logical, &createInfo, nullptr, &swapChain))
|
||||
if (vkCreateSwapchainKHR(VulkanModule::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 members
|
||||
format = chosenFormat.format;
|
||||
extent = chosenExtent;
|
||||
|
||||
// Fetch our images from the swapchain
|
||||
|
||||
uint32_t swapchainImgCount = 0;
|
||||
vkGetSwapchainImagesKHR(VulkanModule::getInstance()->getDevice()->logical, swapChain, &swapchainImgCount, nullptr);
|
||||
|
||||
std::vector<VkImage> swapchainImgs(swapchainImgCount);
|
||||
vkGetSwapchainImagesKHR(VulkanModule::getInstance()->getDevice()->logical, swapChain, &swapchainImgCount, swapchainImgs.data());
|
||||
|
||||
images.resize(0);
|
||||
images.reserve(imageCount);
|
||||
for (const auto& img : swapchainImgs) {
|
||||
images.emplace_back(std::make_unique<vlkx::SwapchainImage>(img, extent, format));
|
||||
}
|
||||
|
||||
// Set gloabls
|
||||
format = chosenFormat.format;
|
||||
extent = chosenExtent;
|
||||
}
|
||||
|
||||
void SwapChain::destroy() {
|
||||
vkDestroySwapchainKHR(VulkanManager::getInstance()->getDevice()->logical, swapChain, nullptr);
|
||||
vkDestroySwapchainKHR(VulkanModule::getInstance()->getDevice()->logical, swapChain, nullptr);
|
||||
/*for (auto & image : images)
|
||||
image.reset();
|
||||
multisampleImg.reset(); */
|
||||
}
|
123
projs/shadow/shadow-engine/shadow-renderer/src/vulkan/Tools.cpp
Normal file
123
projs/shadow/shadow-engine/shadow-renderer/src/vulkan/Tools.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
#include <vlkx/vulkan/Tools.h>
|
||||
#include <string>
|
||||
#include "vlkx/vulkan/abstraction/Commands.h"
|
||||
|
||||
API VmaAllocator VkTools::allocator;
|
||||
|
||||
VkTools::ManagedImage VkTools::createImage(VkFormat format, VkImageUsageFlags flags, VkExtent3D extent) {
|
||||
// Set up image metadata
|
||||
VkImageCreateInfo info = {};
|
||||
info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
info.imageType = VK_IMAGE_TYPE_3D;
|
||||
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(allocator, &info, &allocateInfo, &image.image, &image.allocation, nullptr);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
VkSampler VkTools::createSampler(VkFilter filters, VkSamplerAddressMode mode, uint32_t mipping, VkDevice dev) {
|
||||
VkSamplerCreateInfo info = {
|
||||
VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
nullptr, {}, filters, filters,
|
||||
VK_SAMPLER_MIPMAP_MODE_LINEAR, mode, mode, mode,
|
||||
0, VK_TRUE, 16, VK_FALSE,
|
||||
VK_COMPARE_OP_ALWAYS, 0, static_cast<float>(mipping),
|
||||
VK_BORDER_COLOR_INT_OPAQUE_BLACK, VK_FALSE
|
||||
};
|
||||
|
||||
VkSampler sampler;
|
||||
vkCreateSampler(dev, &info, nullptr, &sampler);
|
||||
|
||||
return sampler;
|
||||
}
|
||||
|
||||
VkImageView VkTools::createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags, uint32_t mipping, uint32_t layers, VkDevice device) {
|
||||
// Raw information about the image
|
||||
VkImageViewCreateInfo viewInfo = {};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = image;
|
||||
viewInfo.viewType = layers == 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_CUBE;
|
||||
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 = mipping;
|
||||
viewInfo.subresourceRange.baseArrayLayer = 0;
|
||||
viewInfo.subresourceRange.layerCount = layers;
|
||||
|
||||
VkImageView imageView;
|
||||
if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS)
|
||||
throw std::runtime_error("Failed to create texture image view.");
|
||||
|
||||
return imageView;
|
||||
}
|
||||
|
||||
VkTools::ManagedBuffer VkTools::createGPUBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkDevice logicalDevice, VkPhysicalDevice physicalDevice, bool hostVisible) {
|
||||
// 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 = hostVisible ? VMA_MEMORY_USAGE_CPU_ONLY : VMA_MEMORY_USAGE_GPU_ONLY;
|
||||
vmaInfo.requiredFlags = properties;
|
||||
|
||||
// Create the buffer.
|
||||
if (VkResult status = vmaCreateBuffer(allocator, &bufferInfo, &vmaInfo, &buffer.buffer, &buffer.allocation, nullptr); status != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to create GPU buffer: " + std::to_string(status));
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
void VkTools::immediateExecute(const std::function<void(const VkCommandBuffer&)>& execute, VulkanDevice* dev) {
|
||||
vlkx::ImmediateCommand cmd({ dev->graphicsQueue, dev->queueData.graphics });
|
||||
cmd.run(execute);
|
||||
}
|
||||
|
||||
void VkTools::copyGPUBuffer(VkBuffer source, VkBuffer dest, VkDeviceSize length, VulkanDevice* dev) {
|
||||
immediateExecute([&](const VkCommandBuffer& commands) {
|
||||
// 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);
|
||||
}, dev);
|
||||
}
|
|
@ -41,6 +41,10 @@ std::vector<const char*> ValidationAndExtension::getRequiredExtensions(SDL_Windo
|
|||
SDL_Vulkan_GetInstanceExtensions(window, &count, nullptr);
|
||||
|
||||
std::vector<const char*> extensions = {
|
||||
#ifdef __APPLE__
|
||||
"VK_KHR_portability_enumeration",
|
||||
#endif
|
||||
"VK_KHR_get_physical_device_properties2"
|
||||
};
|
||||
|
||||
size_t additional_extension_count = extensions.size();
|
||||
|
@ -57,17 +61,16 @@ std::vector<const char*> ValidationAndExtension::getRequiredExtensions(SDL_Windo
|
|||
}
|
||||
|
||||
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
|
||||
VkDebugReportFlagsEXT flags,
|
||||
VkDebugReportObjectTypeEXT objectType,
|
||||
uint64_t object,
|
||||
size_t location,
|
||||
int32_t messageCode,
|
||||
const char* pLayerPrefix,
|
||||
const char* pMessage,
|
||||
void* pUserData
|
||||
) {
|
||||
VkDebugReportFlagsEXT flags,
|
||||
VkDebugReportObjectTypeEXT objExt,
|
||||
uint64_t obj,
|
||||
size_t location,
|
||||
int32_t code,
|
||||
const char* layer,
|
||||
const char* message,
|
||||
void* user) {
|
||||
|
||||
std::cerr << "Validation from layer " << pLayerPrefix << ": " << pMessage << std::endl;
|
||||
std::cerr << "Validation from layer " << layer << ": " << message << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ void VulkanDevice::choosePhysicalDevice(VkInstance* vulkan, VkSurfaceKHR surface
|
|||
for (const auto& device : physicals) {
|
||||
VkPhysicalDeviceProperties props;
|
||||
vkGetPhysicalDeviceProperties(device, &props);
|
||||
limits = props.limits;
|
||||
|
||||
bool dedicated = props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;
|
||||
spdlog::debug(std::string("Device: ") + props.deviceName + ", discrete: " + (dedicated ? "yes" : "no"));
|
||||
|
@ -62,7 +63,7 @@ bool VulkanDevice::isSuitable(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
|||
|
||||
QueueFamilies VulkanDevice::checkQueues(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||
QueueFamilies families;
|
||||
// Enumerate queueues
|
||||
// Enumerate queues
|
||||
uint32_t queueCount = 0;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueCount, nullptr);
|
||||
|
||||
|
|
|
@ -1,232 +0,0 @@
|
|||
#define VMA_IMPLEMENTATION
|
||||
|
||||
#include <vulkan/vk_mem_alloc.h>
|
||||
|
||||
#define VKTOOLS_IMPLEMENTATION
|
||||
|
||||
#include <vlkx/vulkan/Tools.h>
|
||||
|
||||
#include <vlkx\vulkan\VulkanManager.h>
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/sinks/stdout_color_sinks.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();
|
||||
|
||||
spdlog::info("Initializing Infinity Drive rendering engine");
|
||||
spdlog::default_logger()->set_level(spdlog::level::debug);
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
spdlog::info("Infinity Drive initialization finished.");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,348 @@
|
|||
#define VMA_IMPLEMENTATION
|
||||
|
||||
#include <vulkan/vk_mem_alloc.h>
|
||||
#include <vlkx/vulkan/Tools.h>
|
||||
|
||||
#include <vlkx\vulkan\VulkanModule.h>
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
#include "core/ShadowApplication.h"
|
||||
#include "core/SDL2Module.h"
|
||||
#include "vlkx/render/render_pass/ScreenRenderPass.h"
|
||||
#include <vlkx/vulkan/SwapChain.h>
|
||||
|
||||
#define CATCH(x) \
|
||||
try { x } catch (std::exception& e) { spdlog::error(e.what()); exit(0); }
|
||||
|
||||
SHObject_Base_Impl(VulkanModule)
|
||||
|
||||
std::unique_ptr<vlkx::ScreenRenderPassManager> renderPass;
|
||||
bool renderingGeometry;
|
||||
std::unique_ptr<vlkx::ScreenRenderPassManager> editorPass;
|
||||
|
||||
std::unique_ptr<vlkx::RenderCommand> editorRenderCommands;
|
||||
|
||||
const std::unique_ptr<vlkx::ScreenRenderPassManager>& VulkanModule::getRenderPass() {
|
||||
return renderPass;
|
||||
}
|
||||
|
||||
VulkanModule::VulkanModule() { instance = this; }
|
||||
|
||||
VulkanModule::~VulkanModule() = default;
|
||||
|
||||
VulkanModule* VulkanModule::instance = nullptr;
|
||||
|
||||
VulkanModule* VulkanModule::getInstance() {
|
||||
return VulkanModule::instance != nullptr ? VulkanModule::instance
|
||||
: (VulkanModule::instance = new VulkanModule());
|
||||
}
|
||||
|
||||
void VulkanModule::EnableEditor() {
|
||||
editorEnabled = true;
|
||||
}
|
||||
|
||||
VkExtent2D VulkanModule::GetRenderExtent() {
|
||||
if (editorEnabled) {
|
||||
if (renderingGeometry)
|
||||
return editorContentFrames[0]->getExtent();
|
||||
else
|
||||
return swapchain->extent;
|
||||
} else
|
||||
return swapchain->extent;
|
||||
}
|
||||
|
||||
void VulkanModule::Recreate() {
|
||||
vkDeviceWaitIdle(device->logical);
|
||||
|
||||
device->swapChain = device->checkSwapchain(device->physical, surface);
|
||||
|
||||
if (device->swapChain.capabilities.currentExtent.width == 0 && device->swapChain.capabilities.currentExtent.height == 0) {
|
||||
[]() {
|
||||
SDL_Event event;
|
||||
while (true) {
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (event.type == SDL_WINDOWEVENT && (event.window.event == SDL_WINDOWEVENT_MAXIMIZED || event.window.event == SDL_WINDOWEVENT_SHOWN || event.window.event == SDL_WINDOWEVENT_RESIZED || event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED || event.window.event == SDL_WINDOWEVENT_RESTORED))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}();
|
||||
}
|
||||
|
||||
device->swapChain = device->checkSwapchain(device->physical, surface);
|
||||
|
||||
swapchain->destroy();
|
||||
swapchain->create(surface);
|
||||
|
||||
renderPass->initializeRenderPass();
|
||||
editorPass->initializeRenderPass();
|
||||
}
|
||||
|
||||
void VulkanModule::PreInit() {
|
||||
spdlog::info("Vulkan Renderer Module loading..");
|
||||
|
||||
|
||||
auto shApp = ShadowEngine::ShadowApplication::Get();
|
||||
|
||||
ShadowEngine::ModuleManager &moduleManager = shApp.GetModuleManager();
|
||||
|
||||
auto sdl2module = moduleManager.GetModuleByType<ShadowEngine::SDL2Module>();
|
||||
|
||||
CATCH(initVulkan(sdl2module->_window->sdlWindowPtr);)
|
||||
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
|
||||
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
|
||||
|
||||
// Setup Dear ImGui style
|
||||
ImGui::StyleColorsDark();
|
||||
VkDescriptorPool imGuiPool;
|
||||
VkDescriptorPoolSize pool_sizes[] =
|
||||
{
|
||||
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
|
||||
};
|
||||
|
||||
VkDescriptorPoolCreateInfo pool_info = {};
|
||||
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
||||
pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes);
|
||||
pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);
|
||||
pool_info.pPoolSizes = pool_sizes;
|
||||
vkCreateDescriptorPool(getDevice()->logical, &pool_info, VK_NULL_HANDLE, &imGuiPool);
|
||||
|
||||
// Setup Platform/Renderer backends
|
||||
ImGui_ImplSDL2_InitForVulkan(wnd);
|
||||
ImGui_ImplVulkan_InitInfo init_info = {};
|
||||
init_info.Instance = getVulkan();
|
||||
init_info.PhysicalDevice = getDevice()->physical;
|
||||
init_info.Device = getDevice()->logical;
|
||||
init_info.QueueFamily = getDevice()->queueData.graphics;
|
||||
init_info.Queue = getDevice()->graphicsQueue;
|
||||
init_info.PipelineCache = VK_NULL_HANDLE;
|
||||
init_info.DescriptorPool = imGuiPool;
|
||||
init_info.Subpass = 1;
|
||||
init_info.MinImageCount = getSwapchain()->images.size();
|
||||
init_info.ImageCount = getSwapchain()->images.size();
|
||||
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
init_info.Allocator = VK_NULL_HANDLE;
|
||||
init_info.CheckVkResultFn = nullptr;
|
||||
|
||||
|
||||
if (editorEnabled) {
|
||||
editorContentFrames.resize(1);
|
||||
for (size_t i = 0; i < 1; i++) {
|
||||
vlkx::TextureImage::Meta meta {
|
||||
{nullptr}, {vlkx::ImageUsage::renderTarget(0)}, VK_FORMAT_R8G8B8A8_SRGB, 640, 480, 4
|
||||
};
|
||||
editorContentFrames[i] = std::make_unique<vlkx::TextureImage>(0, vlkx::ImageSampler::Config {}, meta);
|
||||
}
|
||||
|
||||
editorRenderCommands = std::make_unique<vlkx::RenderCommand>(editorContentFrames.size());
|
||||
}
|
||||
|
||||
renderPass = std::make_unique<vlkx::ScreenRenderPassManager>(vlkx::RendererConfig { editorEnabled ? 1 : 2, editorEnabled ? editorContentFrames : swapchain->images, !editorEnabled } );
|
||||
renderPass->initializeRenderPass();
|
||||
editorPass = std::make_unique<vlkx::ScreenRenderPassManager>(vlkx::RendererConfig { 2, swapchain->images, true } );
|
||||
editorPass->initializeRenderPass();
|
||||
|
||||
ImGui_ImplVulkan_Init(&init_info, **(editorEnabled ? editorPass : renderPass)->getPass());
|
||||
|
||||
VkTools::immediateExecute([](const VkCommandBuffer& commands) { ImGui_ImplVulkan_CreateFontsTexture(commands); }, getDevice());
|
||||
|
||||
if (editorEnabled) {
|
||||
editorRenderPlanes.resize(editorContentFrames.size());
|
||||
for (size_t i = 0; i < editorContentFrames.size(); i++) {
|
||||
editorRenderPlanes[i] = ImGui_ImplVulkan_AddTexture(VkTools::createSampler(VK_FILTER_LINEAR, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, 0, device->logical), editorContentFrames[i]->getView(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
}
|
||||
|
||||
renderingGeometry = false;
|
||||
}
|
||||
|
||||
void VulkanModule::Init() {
|
||||
}
|
||||
|
||||
void VulkanModule::BeginRenderPass(const std::unique_ptr<vlkx::RenderCommand>& commands) {
|
||||
const auto update = !editorEnabled ? [](const int frame) { ShadowEngine::ModuleManager::instance->Update(frame); } : nullptr;
|
||||
|
||||
const auto res = commands->execute(commands->getFrame(), swapchain->swapChain, update,
|
||||
[this](const VkCommandBuffer& buffer, int frame) {
|
||||
(editorEnabled ? editorPass : renderPass)->getPass()->execute(buffer, frame, {
|
||||
// Render our model
|
||||
[&](const VkCommandBuffer &commands) {
|
||||
if (!editorEnabled) {
|
||||
renderingGeometry = true;
|
||||
ShadowEngine::ModuleManager::instance->Render(const_cast<VkCommandBuffer &>(commands), frame);
|
||||
ShadowEngine::ModuleManager::instance->LateRender(const_cast<VkCommandBuffer &>(commands), frame);
|
||||
renderingGeometry = false;
|
||||
}
|
||||
},
|
||||
// Render ImGUI
|
||||
[&](const VkCommandBuffer &commands) {
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
if (editorEnabled)
|
||||
ImGui::Image(editorRenderPlanes[0], { 640, 480 });
|
||||
|
||||
ShadowEngine::ModuleManager::instance->OverlayRender();
|
||||
|
||||
ImGui::Render();
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
(void) io;
|
||||
|
||||
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), commands);
|
||||
|
||||
// Update and Render additional Platform Windows
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if (res.has_value())
|
||||
ShadowEngine::ModuleManager::getInstance()->Recreate();
|
||||
}
|
||||
|
||||
void VulkanModule::PreRender() {
|
||||
if (editorEnabled) {
|
||||
editorRenderCommands->executeSimple(editorRenderCommands->getFrame(), [](const int frame) { ShadowEngine::ModuleManager::instance->Update(frame); },
|
||||
[&](const VkCommandBuffer& buffer, int frame) {
|
||||
renderPass->getPass()->execute(buffer, frame, {
|
||||
[&](const VkCommandBuffer& commands) {
|
||||
renderingGeometry = true;
|
||||
ShadowEngine::ModuleManager::instance->Render(const_cast<VkCommandBuffer &>(commands), frame);
|
||||
ShadowEngine::ModuleManager::instance->LateRender(const_cast<VkCommandBuffer &>(commands), frame);
|
||||
renderingGeometry = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void VulkanModule::OverlayRender() {}
|
||||
void VulkanModule::AfterFrameEnd() {}
|
||||
void VulkanModule::Render(VkCommandBuffer& commands, int frame) {}
|
||||
void VulkanModule::Update(int frame) {}
|
||||
void VulkanModule::Event(SDL_Event *e) { (void)e; }
|
||||
|
||||
void VulkanModule::LateRender(VkCommandBuffer& commands, int frame) {
|
||||
}
|
||||
|
||||
void VulkanModule::Destroy() {
|
||||
ImGui_ImplVulkan_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
void VulkanModule::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;
|
||||
#ifdef __APPLE__
|
||||
VkFlags instanceFlag = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||
#else
|
||||
VkFlags instanceFlag = 0;
|
||||
#endif
|
||||
instanceInfo.flags = instanceFlag;
|
||||
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 VulkanModule::initVulkan(SDL_Window* window) {
|
||||
wnd = window;
|
||||
validators = new ValidationAndExtension();
|
||||
|
||||
spdlog::info("Initializing Infinity Drive rendering engine");
|
||||
spdlog::default_logger()->set_level(spdlog::level::debug);
|
||||
|
||||
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::allocator = allocator;
|
||||
|
||||
this->swapchain = new SwapChain();
|
||||
this->swapchain->create(surface);
|
||||
spdlog::info("Infinity Drive initialization finished.");
|
||||
}
|
||||
|
||||
void VulkanModule::cleanup() {
|
||||
// Wait for the GPU to not be busy
|
||||
vkDeviceWaitIdle(VulkanModule::getInstance()->getDevice()->logical);
|
||||
|
||||
swapchain->destroy();
|
||||
|
||||
// Destroy the Vulkan Device
|
||||
VulkanModule::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 swapchain;
|
||||
delete device;
|
||||
delete validators;
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
#include "vlkx/vulkan/abstraction/Buffer.h"
|
||||
#include "vlkx/vulkan/Tools.h"
|
||||
#include "vlkx/vulkan/VulkanModule.h"
|
||||
#include <memory>
|
||||
|
||||
namespace vlkx {
|
||||
|
||||
void executeBulkCopy(VkTools::ManagedBuffer buffer, const std::vector<vlkx::Buffer::CopyMeta>& meta) {
|
||||
void* dst;
|
||||
vmaMapMemory(VulkanModule::getInstance()->getAllocator(), buffer.allocation, &dst);
|
||||
// GPU memory accessible through dst pointer
|
||||
|
||||
for (const auto& info : meta) {
|
||||
memcpy(static_cast<char*>(dst) + info.start, info.data, info.length);
|
||||
}
|
||||
|
||||
// Unmap GPU memory
|
||||
vmaUnmapMemory(VulkanModule::getInstance()->getAllocator(), buffer.allocation);
|
||||
}
|
||||
|
||||
StagingBuffer::StagingBuffer(const vlkx::Buffer::BulkCopyMeta ©Meta) : dataSize(copyMeta.length) {
|
||||
setBuffer(VkTools::createGPUBuffer(dataSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VulkanModule::getInstance()->getDevice()->logical, VulkanModule::getInstance()->getDevice()->physical));
|
||||
|
||||
executeBulkCopy(get(), copyMeta.metas);
|
||||
}
|
||||
|
||||
void StagingBuffer::copy(const VkBuffer &target) const {
|
||||
VkTools::copyGPUBuffer(get().buffer, target, dataSize, VulkanModule::getInstance()->getDevice());
|
||||
}
|
||||
|
||||
std::vector<VkVertexInputAttributeDescription> VertexBuffer::getAttrs(uint32_t start) const {
|
||||
std::vector<VkVertexInputAttributeDescription> descs;
|
||||
descs.reserve(attributes.size());
|
||||
|
||||
for (const auto& attr : attributes) {
|
||||
descs.push_back(VkVertexInputAttributeDescription {
|
||||
start++, 0, attr.format, attr.offset
|
||||
});
|
||||
}
|
||||
|
||||
return descs;
|
||||
}
|
||||
|
||||
void VertexBuffer::draw(const VkCommandBuffer &commands, uint32_t verts, uint32_t instances) {
|
||||
vkCmdDraw(commands, verts, instances, 0, 0);
|
||||
}
|
||||
|
||||
void VertexBuffer::create(VkDeviceSize totalSize, bool dynamic, bool indexes) {
|
||||
VkBufferUsageFlags usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
|
||||
VkMemoryPropertyFlags props;
|
||||
|
||||
if (dynamic) {
|
||||
props = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
} else {
|
||||
usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
||||
props = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
}
|
||||
|
||||
if (indexes)
|
||||
usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
|
||||
|
||||
setBuffer(VkTools::createGPUBuffer(totalSize, usage, props, VulkanModule::getInstance()->getDevice()->logical, VulkanModule::getInstance()->getDevice()->physical, dynamic));
|
||||
}
|
||||
|
||||
DynamicBuffer::DynamicBuffer(size_t size, bool hasIndices, vlkx::VertexBuffer *buffer) : hasIndices(hasIndices), vertexBuffer(buffer) {
|
||||
resize(size);
|
||||
}
|
||||
|
||||
void DynamicBuffer::resize(size_t pSize) {
|
||||
if (pSize <= bufferSize()) return;
|
||||
|
||||
if (pSize > 0) {
|
||||
// todo: release the buffer & device memory
|
||||
}
|
||||
|
||||
size = pSize;
|
||||
vertexBuffer->create(size, true, hasIndices);
|
||||
}
|
||||
|
||||
Buffer::BulkCopyMeta PerVertexBuffer::NoIndexBufferMeta::prepareCopy(vlkx::PerVertexBuffer *buffer) const {
|
||||
auto& meshInfos = buffer->meshDataInfo.emplace<MeshDataNoIndex>().info;
|
||||
meshInfos.reserve(perMeshVertices.size());
|
||||
|
||||
std::vector<Buffer::CopyMeta> copyMetas;
|
||||
copyMetas.reserve(perMeshVertices.size());
|
||||
|
||||
VkDeviceSize offset = 0;
|
||||
for (const auto& verts : perMeshVertices) {
|
||||
meshInfos.push_back(MeshDataNoIndex::Info { static_cast<uint32_t>(verts.unitsPerMesh), offset });
|
||||
copyMetas.push_back(Buffer::CopyMeta { verts.data, verts.sizePerMesh, offset });
|
||||
offset += verts.sizePerMesh;
|
||||
}
|
||||
|
||||
return Buffer::BulkCopyMeta { offset, std::move(copyMetas) };
|
||||
}
|
||||
|
||||
Buffer::BulkCopyMeta PerVertexBuffer::SharedIndexMeta::prepareCopy(vlkx::PerVertexBuffer *buffer) const {
|
||||
auto& meshInfos = buffer->meshDataInfo.emplace<MeshDataIndex>().info;
|
||||
meshInfos.reserve(meshes);
|
||||
|
||||
VkDeviceSize offset = sharedIndices.sizePerMesh;
|
||||
for (int i = 0; i < meshes; ++i) {
|
||||
meshInfos.push_back(MeshDataIndex::Info { static_cast<uint32_t>(sharedIndices.unitsPerMesh), 0, offset });
|
||||
offset += perMeshVertex.sizePerMesh;
|
||||
}
|
||||
|
||||
return Buffer::BulkCopyMeta { offset, { { sharedIndices.data, sharedIndices.sizePerMesh, 0 }, { perMeshVertex.data, perMeshVertex.sizePerMesh * meshes, sharedIndices.sizePerMesh } } };
|
||||
}
|
||||
|
||||
|
||||
Buffer::BulkCopyMeta PerVertexBuffer::NoShareMeta::prepareCopy(vlkx::PerVertexBuffer *buffer) const {
|
||||
auto& meshInfos = buffer->meshDataInfo.emplace<MeshDataIndex>().info;
|
||||
meshInfos.reserve(perMeshMeta.size());
|
||||
|
||||
std::vector<Buffer::CopyMeta> copyMetas;
|
||||
copyMetas.reserve(perMeshMeta.size() * 2);
|
||||
|
||||
VkDeviceSize offset = 0;
|
||||
for (const auto& meta : perMeshMeta) {
|
||||
const size_t indicesSize = meta.indices.sizePerMesh;
|
||||
const size_t verticesSize = meta.vertices.sizePerMesh;
|
||||
const VkDeviceSize verticesOffset = offset + indicesSize;
|
||||
|
||||
meshInfos.push_back(MeshDataIndex::Info { static_cast<uint32_t>(meta.indices.unitsPerMesh), offset, verticesOffset });
|
||||
copyMetas.push_back(Buffer::CopyMeta { meta.indices.data, indicesSize, offset });
|
||||
copyMetas.push_back(Buffer::CopyMeta { meta.vertices.data, verticesSize, verticesOffset });
|
||||
|
||||
offset += indicesSize + verticesSize;
|
||||
}
|
||||
|
||||
return Buffer::BulkCopyMeta { offset, std::move(copyMetas) };
|
||||
}
|
||||
|
||||
void PerVertexBuffer::draw(const VkCommandBuffer &commands, uint32_t bind, int index, uint32_t instances) const {
|
||||
if (const auto* meshNoIndex = std::get_if<MeshDataNoIndex>(&meshDataInfo); meshNoIndex != nullptr) {
|
||||
const auto& meshInfo = meshNoIndex->info[index];
|
||||
vkCmdBindVertexBuffers(commands, bind, 1, &getBuffer(), &meshInfo.vertexStart);
|
||||
vkCmdDraw(commands, meshInfo.vertexCount, instances, 0, 0);
|
||||
} else if (const auto* meshIndex = std::get_if<MeshDataIndex>(&meshDataInfo); meshIndex != nullptr) {
|
||||
const auto& meshInfo = meshIndex->info[index];
|
||||
vkCmdBindIndexBuffer(commands, getBuffer(), meshInfo.indexStart, VK_INDEX_TYPE_UINT32);
|
||||
vkCmdBindVertexBuffers(commands, bind, 1, &getBuffer(), &meshInfo.vertexStart);
|
||||
vkCmdDrawIndexed(commands, meshInfo.indexCount, instances, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
StaticPerVertexBuffer::StaticPerVertexBuffer(const vlkx::PerVertexBuffer::BufferDataMeta &info,
|
||||
std::vector<VkVertexInputAttributeDescription> &&attrs) : PerVertexBuffer(std::move(attrs)) {
|
||||
const BulkCopyMeta copy = info.prepareCopy(this);
|
||||
create(copy.length, false, info.hasIndices());
|
||||
const StagingBuffer staging(copy);
|
||||
staging.copy(getBuffer());
|
||||
}
|
||||
|
||||
void DynamicPerVertexBuffer::copyToDevice(const vlkx::PerVertexBuffer::BufferDataMeta &meta) {
|
||||
const BulkCopyMeta copy = meta.prepareCopy(this);
|
||||
resize(copy.length);
|
||||
executeBulkCopy(get(), copy.metas);
|
||||
}
|
||||
|
||||
void PerInstanceVertexBuffer::bind(const VkCommandBuffer &commands, uint32_t bindPoint, int offset) const {
|
||||
const VkDeviceSize size = sizePerInstance * offset;
|
||||
vkCmdBindVertexBuffers(commands, bindPoint, 1, &getBuffer(), &size);
|
||||
}
|
||||
|
||||
StaticPerInstanceBuffer::StaticPerInstanceBuffer(uint32_t size, const void *data, uint32_t instances,
|
||||
std::vector<VkVertexInputAttributeDescription> &&attrs) : PerInstanceVertexBuffer(size, std::move(attrs)) {
|
||||
const uint32_t totalSize = size * instances;
|
||||
create(totalSize, false, false);
|
||||
|
||||
const BulkCopyMeta copy { totalSize, { {data, totalSize, 0} } };
|
||||
const StagingBuffer staging(copy);
|
||||
staging.copy(getBuffer());
|
||||
}
|
||||
|
||||
void DynamicPerInstanceBuffer::copyToDevice(const void *data, uint32_t instances) {
|
||||
const uint32_t totalSize = getSize() * instances;
|
||||
const BulkCopyMeta copy { totalSize, { { data, totalSize, 0 } } };
|
||||
resize(totalSize);
|
||||
executeBulkCopy(get(), copy.metas);
|
||||
}
|
||||
|
||||
UniformBuffer::UniformBuffer(size_t chunkSize, int chunks) : DataBuffer(), chunkSize(chunkSize), numChunks(chunks) {
|
||||
const VkDeviceSize alignment = VulkanModule::getInstance()->getDevice()->limits.minUniformBufferOffsetAlignment;
|
||||
chunkLength = (chunkSize + alignment - 1) / alignment * alignment;
|
||||
|
||||
data = new char[chunkSize * numChunks];
|
||||
setBuffer(VkTools::createGPUBuffer(chunkLength * numChunks, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VulkanModule::getInstance()->getDevice()->logical, VulkanModule::getInstance()->getDevice()->physical, true));
|
||||
}
|
||||
|
||||
void UniformBuffer::upload(int index) const {
|
||||
checkIndex(index);
|
||||
const VkDeviceSize srcOffset = chunkSize * index;
|
||||
const VkDeviceSize dstOffset = chunkLength * index;
|
||||
|
||||
// TODO: dstoffset?
|
||||
executeBulkCopy(get(), { { data + srcOffset, chunkSize, 0 } } );
|
||||
}
|
||||
|
||||
void UniformBuffer::upload(int index, VkDeviceSize dataSize, VkDeviceSize start) const {
|
||||
checkIndex(index);
|
||||
const VkDeviceSize srcOffset = chunkSize * index + start;
|
||||
const VkDeviceSize dstOffset = chunkLength * index + start;
|
||||
|
||||
// TODO: dstoffset?
|
||||
executeBulkCopy(get(), { { data + srcOffset, dataSize, 0 } } );
|
||||
}
|
||||
|
||||
VkDescriptorBufferInfo UniformBuffer::getDescriptorInfo(int index) const {
|
||||
checkIndex(index);
|
||||
return VkDescriptorBufferInfo { getBuffer(), chunkLength * index, chunkSize};
|
||||
}
|
||||
|
||||
void UniformBuffer::checkIndex(int index) const {
|
||||
if (index > numChunks)
|
||||
throw std::runtime_error("Attempting to access uniform chunk " + std::to_string(index) + " out of range.");
|
||||
|
||||
}
|
||||
|
||||
PushConstant::PushConstant(size_t size, int numFrames) : sizePerFrame(static_cast<uint32_t>(size)), numFrames(numFrames) {
|
||||
if (size > 128)
|
||||
throw std::runtime_error("Attempting to push constant of size " + std::to_string(size) + ", max ix 128.");
|
||||
|
||||
data = new char[size * numFrames];
|
||||
}
|
||||
|
||||
void PushConstant::upload(const VkCommandBuffer &commands, const VkPipelineLayout &pipelineLayout, int frame,
|
||||
uint32_t offset, VkShaderStageFlags stage) const {
|
||||
checkIndex(frame);
|
||||
void* data = getData<void>(frame);
|
||||
vkCmdPushConstants(commands, pipelineLayout, stage, offset, sizePerFrame, data);
|
||||
}
|
||||
|
||||
void PushConstant::checkIndex(int index) const {
|
||||
if (index > numFrames)
|
||||
throw std::runtime_error("Attempting to access push constant for frame " + std::to_string(index) + " out of range.");
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
#include "vlkx/vulkan/abstraction/Commands.h"
|
||||
#include "vlkx/vulkan/VulkanModule.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
vlkx::CommandBuffer::CommandBuffer() {
|
||||
dev = VulkanModule::getInstance()->getDevice();
|
||||
}
|
||||
|
||||
VkCommandPool createPool(vlkx::Queue queue, bool shortLived) {
|
||||
VkCommandPool pool;
|
||||
VkCommandPoolCreateInfo poolCreateInfo = {};
|
||||
poolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
poolCreateInfo.queueFamilyIndex = queue.queueIndex;
|
||||
poolCreateInfo.flags = shortLived ? VK_COMMAND_POOL_CREATE_TRANSIENT_BIT : VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||
|
||||
// Create the pool
|
||||
if (vkCreateCommandPool(VulkanModule::getInstance()->getDevice()->logical, &poolCreateInfo, nullptr, &pool) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to allocate a temporary command pool");
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
std::vector<VkCommandBuffer> allocateBuffers(const VkCommandPool& pool, uint32_t amount) {
|
||||
const VkCommandBufferAllocateInfo allocate {
|
||||
VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
nullptr, pool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, amount
|
||||
};
|
||||
|
||||
std::vector<VkCommandBuffer> buffers(amount);
|
||||
if (vkAllocateCommandBuffers(VulkanModule::getInstance()->getDevice()->logical, &allocate, buffers.data()) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to allocate command buffers");
|
||||
|
||||
return buffers;
|
||||
}
|
||||
|
||||
void recordCommands(const VkCommandBuffer& commands, VkCommandBufferUsageFlags flags, const vlkx::CommandBuffer::Command& record) {
|
||||
const VkCommandBufferBeginInfo begin {
|
||||
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr, flags, nullptr
|
||||
};
|
||||
|
||||
if (vkBeginCommandBuffer(commands, &begin) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to begin listening on command buffer");
|
||||
if (record != nullptr)
|
||||
record(commands);
|
||||
if (vkEndCommandBuffer(commands) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to end listening on command buffer");
|
||||
}
|
||||
|
||||
std::optional<VkResult> parseResult(VkResult result) {
|
||||
switch(result) {
|
||||
case VK_ERROR_OUT_OF_DATE_KHR: return result;
|
||||
case VK_SUCCESS:
|
||||
case VK_SUBOPTIMAL_KHR:
|
||||
return std::nullopt;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Command buffer returned unknown result " + std::to_string(result));
|
||||
}
|
||||
}
|
||||
|
||||
vlkx::ImmediateCommand::ImmediateCommand(Queue queue) : queue(queue) {
|
||||
const auto pool = createPool(queue, true);
|
||||
setPool(pool);
|
||||
commands = allocateBuffers(pool, 1)[0];
|
||||
}
|
||||
|
||||
void vlkx::ImmediateCommand::run(const vlkx::CommandBuffer::Command &cmd) {
|
||||
recordCommands(commands, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, cmd);
|
||||
const VkSubmitInfo submit {
|
||||
VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
nullptr, 0, nullptr, nullptr, 1,
|
||||
&commands, 0, nullptr
|
||||
};
|
||||
|
||||
vkQueueSubmit(queue.queue, 1, &submit, VK_NULL_HANDLE);
|
||||
vkQueueWaitIdle(queue.queue);
|
||||
}
|
||||
|
||||
vlkx::RenderCommand::RenderCommand(int frames) {
|
||||
VulkanDevice* dev = VulkanModule::getInstance()->getDevice();
|
||||
|
||||
// Create semaphores for render events
|
||||
VkSemaphoreCreateInfo semaphoreInfo = {};
|
||||
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
vkCreateSemaphore(dev->logical, &semaphoreInfo, nullptr, &newImageSem);
|
||||
vkCreateSemaphore(dev->logical, &semaphoreInfo, nullptr, &renderDoneSem);
|
||||
|
||||
// Create fences for the frames
|
||||
inFlight.resize(frames);
|
||||
VkFenceCreateInfo fenceInfo = {};
|
||||
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||||
|
||||
for (size_t i = 0; i < frames; i++) {
|
||||
if (vkCreateFence(dev->logical, &fenceInfo, nullptr, &inFlight[i]) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to create fence for a frame");
|
||||
}
|
||||
|
||||
const auto pool = createPool({ dev->graphicsQueue, dev->queueData.graphics }, false);
|
||||
setPool(pool);
|
||||
commands = allocateBuffers(pool, static_cast<uint32_t>(frames));
|
||||
}
|
||||
|
||||
std::optional<VkResult> vlkx::RenderCommand::execute(int frame, const VkSwapchainKHR &swapchain,
|
||||
const vlkx::RenderCommand::Update &update,
|
||||
const vlkx::RenderCommand::Command &cmd) {
|
||||
|
||||
const VkDevice& logical = VulkanModule::getInstance()->getDevice()->logical;
|
||||
|
||||
vkWaitForFences(logical, 1, &inFlight[imageIndex], VK_TRUE,
|
||||
std::numeric_limits<uint64_t>::max()
|
||||
);
|
||||
|
||||
if (update != nullptr)
|
||||
update(frame);
|
||||
|
||||
uint32_t nextFrame;
|
||||
// Prepare for a new frame to start
|
||||
const auto result = parseResult(vkAcquireNextImageKHR(logical, swapchain,
|
||||
std::numeric_limits<uint64_t>::max(), newImageSem, VK_NULL_HANDLE, &nextFrame
|
||||
));
|
||||
|
||||
if (result.has_value())
|
||||
return result;
|
||||
|
||||
recordCommands(commands[imageIndex], VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, [&cmd, nextFrame](const VkCommandBuffer& buffer) {
|
||||
cmd(buffer, nextFrame);
|
||||
});
|
||||
|
||||
constexpr VkPipelineStageFlags stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
const VkSubmitInfo submit {
|
||||
VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
nullptr, 1, &newImageSem, &stage, 1, &commands[imageIndex], 1, &renderDoneSem
|
||||
};
|
||||
|
||||
vkResetFences(logical, 1, &inFlight[imageIndex]);
|
||||
|
||||
if (VkResult res = vkQueueSubmit(VulkanModule::getInstance()->getDevice()->graphicsQueue, 1, &submit, inFlight[imageIndex]); res != VK_SUCCESS)
|
||||
throw std::runtime_error("Failed to submit commands: " + std::to_string(res));
|
||||
|
||||
const VkPresentInfoKHR present {
|
||||
VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
nullptr, 1, &renderDoneSem, 1, &swapchain, &nextFrame, nullptr
|
||||
};
|
||||
|
||||
return parseResult(vkQueuePresentKHR(VulkanModule::getInstance()->getDevice()->presentationQueue, &present));
|
||||
}
|
||||
|
||||
|
||||
std::optional<VkResult> vlkx::RenderCommand::executeSimple(int frame,
|
||||
const vlkx::RenderCommand::Update &update,
|
||||
const vlkx::RenderCommand::Command &cmd) {
|
||||
if (update != nullptr)
|
||||
update(frame);
|
||||
|
||||
const VkDevice& logical = VulkanModule::getInstance()->getDevice()->logical;
|
||||
|
||||
vkWaitForFences(logical, 1, &inFlight[imageIndex], VK_TRUE,
|
||||
std::numeric_limits<uint64_t>::max()
|
||||
);
|
||||
|
||||
recordCommands(commands[imageIndex], VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, [&cmd](const VkCommandBuffer& buffer) {
|
||||
cmd(buffer, 0);
|
||||
});
|
||||
|
||||
vkResetFences(logical, 1, &inFlight[imageIndex]);
|
||||
|
||||
constexpr VkPipelineStageFlags stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
const VkSubmitInfo submit {
|
||||
VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
nullptr, 0, VK_NULL_HANDLE, &stage, 1, &commands[imageIndex], 0, VK_NULL_HANDLE
|
||||
};
|
||||
|
||||
return std::make_optional(vkQueueSubmit(VulkanModule::getInstance()->getDevice()->graphicsQueue, 1, &submit, inFlight[imageIndex]));
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
#include "vlkx/vulkan/abstraction/Descriptor.h"
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
using namespace vlkx;
|
||||
// Returns 'pointer', assuming 'ExpectedType' and 'ActualType' are the same.
|
||||
template <typename ExpectedType, typename ActualType>
|
||||
inline const ExpectedType* getPtr(const ActualType* pointer, std::true_type) {
|
||||
return pointer;
|
||||
}
|
||||
|
||||
// Returns nullptr, assuming 'ExpectedType' and 'ActualType' are different.
|
||||
template <typename ExpectedType, typename ActualType>
|
||||
inline const ExpectedType* getPtr(const ActualType* pointer, std::false_type) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename ExpectedType, typename ValueType>
|
||||
const ExpectedType* getPointer(const std::vector<ValueType>& container) {
|
||||
return getPtr<ExpectedType, ValueType>(container.data(), std::is_same<ExpectedType, ValueType>());
|
||||
}
|
||||
|
||||
|
||||
VkDescriptorPool createPool(std::vector<Descriptor::Meta> metas) {
|
||||
std::map<VkDescriptorType, uint32_t> sizes;
|
||||
for (const auto& meta : metas) {
|
||||
uint32_t length = 0;
|
||||
for (const auto& binding : meta.bindings)
|
||||
length += binding.length;
|
||||
sizes[meta.type] += length;
|
||||
}
|
||||
|
||||
std::vector<VkDescriptorPoolSize> poolSizes;
|
||||
for (const auto& pair : sizes)
|
||||
poolSizes.push_back({ pair.first, pair.second });
|
||||
|
||||
const VkDescriptorPoolCreateInfo create {
|
||||
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
nullptr, 0,
|
||||
1, static_cast<uint32_t>(poolSizes.size()), poolSizes.data()
|
||||
};
|
||||
|
||||
VkDescriptorPool pool;
|
||||
if (vkCreateDescriptorPool(VulkanModule::getInstance()->getDevice()->logical, &create, nullptr, &pool) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to create Descriptor Pool");
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout createLayout(std::vector<Descriptor::Meta> metas, bool dynamic) {
|
||||
size_t bindings = 0;
|
||||
for (const auto& meta : metas)
|
||||
bindings += meta.bindings.size();
|
||||
|
||||
std::vector<VkDescriptorSetLayoutBinding> layoutBindings;
|
||||
layoutBindings.reserve(bindings);
|
||||
for (const auto& meta : metas)
|
||||
for (const auto& binding : meta.bindings)
|
||||
layoutBindings.push_back({ binding.bindPoint, meta.type, binding.length, meta.stage, nullptr });
|
||||
|
||||
const VkDescriptorSetLayoutCreateInfo create {
|
||||
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
nullptr, static_cast<VkDescriptorSetLayoutCreateFlags>(dynamic ? VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR : 0),
|
||||
static_cast<uint32_t>(layoutBindings.size()), layoutBindings.data()
|
||||
};
|
||||
|
||||
VkDescriptorSetLayout layout;
|
||||
if (vkCreateDescriptorSetLayout(VulkanModule::getInstance()->getDevice()->logical, &create, nullptr, &layout) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to create Descriptor Set Layout");
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
VkDescriptorSet allocateSet(const VkDescriptorPool& pool, const VkDescriptorSetLayout& layout) {
|
||||
const VkDescriptorSetAllocateInfo allocate {
|
||||
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
nullptr, pool, 1, &layout
|
||||
};
|
||||
|
||||
VkDescriptorSet set;
|
||||
if (vkAllocateDescriptorSets(VulkanModule::getInstance()->getDevice()->logical, &allocate, &set) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to allocate descriptor set");
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
std::vector<VkWriteDescriptorSet> createWrites(const VkDescriptorSet& set, VkDescriptorType type, const std::map<uint32_t, std::vector<Type>>& map) {
|
||||
|
||||
std::vector<VkWriteDescriptorSet> sets;
|
||||
sets.reserve(map.size());
|
||||
|
||||
for (const auto& pair : map) {
|
||||
const auto& info = pair.second;
|
||||
sets.push_back(VkWriteDescriptorSet {
|
||||
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr, set, pair.first, 0,
|
||||
static_cast<uint32_t>(info.size()), type,
|
||||
getPointer<VkDescriptorImageInfo>(info),
|
||||
getPointer<VkDescriptorBufferInfo>(info),
|
||||
getPointer<VkBufferView>(info)
|
||||
});
|
||||
}
|
||||
|
||||
return sets;
|
||||
}
|
||||
|
||||
StaticDescriptor::StaticDescriptor(std::vector<Meta> metas) : Descriptor() {
|
||||
pool = createPool(metas);
|
||||
const auto layout = createLayout(metas, false);
|
||||
setLayout(layout);
|
||||
set = allocateSet(pool, layout);
|
||||
}
|
||||
|
||||
const StaticDescriptor& StaticDescriptor::buffers(VkDescriptorType type, const BufferInfos &infos) const {
|
||||
return updateSet(createWrites(set, type, infos));
|
||||
}
|
||||
|
||||
const StaticDescriptor& StaticDescriptor::images(VkDescriptorType type, const ImageInfos &infos) const {
|
||||
return updateSet(createWrites(set, type, infos));
|
||||
}
|
||||
|
||||
const StaticDescriptor& StaticDescriptor::updateSet(const std::vector<VkWriteDescriptorSet> &write) const {
|
||||
vkUpdateDescriptorSets(VulkanModule::getInstance()->getDevice()->logical, write.size(), write.data(), 0, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void StaticDescriptor::bind(const VkCommandBuffer &commands, const VkPipelineLayout &layout,
|
||||
VkPipelineBindPoint bindPoint) const {
|
||||
vkCmdBindDescriptorSets(commands, bindPoint, layout, 0, 1, &set, 0, nullptr);
|
||||
}
|
||||
|
|
@ -0,0 +1,439 @@
|
|||
#include "vlkx/vulkan/abstraction/Image.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <utility>
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
#include "vlkx/vulkan/VulkanModule.h"
|
||||
#include "shadow/util/File.h"
|
||||
|
||||
namespace vlkx {
|
||||
struct ImageConfig {
|
||||
explicit ImageConfig(bool readable = false) {
|
||||
if (readable) {
|
||||
tiling = VK_IMAGE_TILING_LINEAR;
|
||||
layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||||
} else {
|
||||
tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t mipping = 1;
|
||||
uint32_t layers = 1;
|
||||
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
VkImageTiling tiling;
|
||||
VkImageLayout layout;
|
||||
};
|
||||
|
||||
struct ImageData {
|
||||
ImageDescriptor::Dimension dimensions;
|
||||
const char* data;
|
||||
};
|
||||
|
||||
ImageData loadImage(const std::string& path, int wantedChannels) {
|
||||
shadowutil::FileData* data = shadowutil::loadFile(path);
|
||||
int width, height, channels;
|
||||
|
||||
stbi_uc* stbData = stbi_load_from_memory(reinterpret_cast<const stbi_uc *>(data->data.data()), data->size, &width, &height, &channels, wantedChannels);
|
||||
if (stbData == nullptr)
|
||||
throw std::runtime_error("Unable to read image file " + std::string(path));
|
||||
|
||||
switch (channels) {
|
||||
case 1:
|
||||
case 4:
|
||||
break;
|
||||
|
||||
case 3: {
|
||||
stbi_image_free(stbData);
|
||||
stbData = stbi_load_from_memory(reinterpret_cast<const stbi_uc *>(data->data.data()), data->size, &width, &height, &channels, STBI_rgb_alpha);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Trying to load image with unsupported number of channels: " + std::to_string(channels));
|
||||
}
|
||||
|
||||
return {{ static_cast<uint32_t>(width), static_cast<uint32_t>(height), static_cast<uint32_t>(channels) },reinterpret_cast<const char *>(stbData) };
|
||||
}
|
||||
|
||||
std::optional<VkFormat> findFormatWith(const std::vector<VkFormat>& formats, VkFormatFeatureFlags feature) {
|
||||
for (const auto format : formats) {
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(VulkanModule::getInstance()->getDevice()->physical, format, &props);
|
||||
if ((props.optimalTilingFeatures & feature) == feature) return format;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
VkFormat findFormatForChannels(int channels, const std::vector<ImageUsage>& usages, bool highPrecision = false) {
|
||||
switch (channels) {
|
||||
case 1: {
|
||||
VkFormat best, alternative;
|
||||
if (highPrecision) {
|
||||
best = VK_FORMAT_R16_SFLOAT;
|
||||
alternative = VK_FORMAT_R16G16B16A16_SFLOAT;
|
||||
} else {
|
||||
best = VK_FORMAT_R8_UNORM;
|
||||
alternative = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
}
|
||||
|
||||
if (findFormatWith({ best }, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT).has_value())
|
||||
return best;
|
||||
else
|
||||
return alternative;
|
||||
}
|
||||
|
||||
case 4:
|
||||
if (highPrecision)
|
||||
return VK_FORMAT_R16G16B16A16_SFLOAT;
|
||||
else
|
||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
default:
|
||||
throw std::runtime_error("Attempting to find format for invalid channels (RGB images have 4 channels!)");
|
||||
}
|
||||
}
|
||||
|
||||
VkFormat findFormatForDepthStencil() {
|
||||
const auto format = findFormatWith({ VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
|
||||
if (!format.has_value())
|
||||
throw std::runtime_error("Unable to find a format for a depth stencil image.");
|
||||
return format.value();
|
||||
}
|
||||
|
||||
VkSampleCountFlagBits getMaxSamples(VkSampleCountFlags samples) {
|
||||
for (const auto count : { VK_SAMPLE_COUNT_64_BIT, VK_SAMPLE_COUNT_32_BIT, VK_SAMPLE_COUNT_16_BIT, VK_SAMPLE_COUNT_8_BIT, VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_1_BIT }) {
|
||||
if (samples & count)
|
||||
return count;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Multisampling isn't supported?");
|
||||
}
|
||||
|
||||
Buffer::Buffer() {
|
||||
}
|
||||
|
||||
ImageDescriptor Image::loadCubeFromDisk(const std::string& directory, const std::array<std::string, 6> &files,
|
||||
bool flipY) {
|
||||
stbi_set_flip_vertically_on_load(flipY);
|
||||
auto firstImage = loadImage(directory + "/" + files[0], STBI_default);
|
||||
const ImageDescriptor::Dimension& dim = firstImage.dimensions;
|
||||
char* data = new char[dim.getSize() * 6]; // TODO: Figure out how to make this delete
|
||||
memcpy(data, firstImage.data, dim.getSize());
|
||||
for (size_t i = 1; i < 6; i++) {
|
||||
auto image = loadImage(directory + "/" + files[1], STBI_default);
|
||||
if (!(image.dimensions.width == dim.width && image.dimensions.height == dim.height && image.dimensions.channels == dim.channels))
|
||||
throw std::runtime_error("Image " + std::to_string(i) + "(" + directory + "/" + files[i] + ") has different dimensions from the first image.");
|
||||
|
||||
memcpy(data + i * dim.getSize(), image.data, dim.getSize());
|
||||
}
|
||||
|
||||
stbi_set_flip_vertically_on_load(false);
|
||||
return { ImageDescriptor::Type::Cubemap, firstImage.dimensions, data };
|
||||
}
|
||||
|
||||
ImageDescriptor Image::loadSingleFromDisk(std::string path, bool flipY) {
|
||||
stbi_set_flip_vertically_on_load(flipY);
|
||||
auto image = loadImage(std::move(path), STBI_default);
|
||||
stbi_set_flip_vertically_on_load(false);
|
||||
|
||||
return { ImageDescriptor::Type::Single, image.dimensions, image.data };
|
||||
}
|
||||
|
||||
TextureImage::Meta createTextureMeta(const ImageDescriptor& image, const std::vector<ImageUsage>& usages) {
|
||||
return TextureImage::Meta {
|
||||
image.getData(), usages,
|
||||
findFormatForChannels(image.getChannels(), usages),
|
||||
image.getWidth(), image.getHeight(), image.getChannels(),
|
||||
};
|
||||
}
|
||||
|
||||
VkTools::ManagedImage createImage(const ImageConfig& config, VkImageCreateFlags flags, VkFormat format, const VkExtent3D& extent, VkImageUsageFlags usage) {
|
||||
const auto device = VulkanModule::getInstance()->getDevice();
|
||||
|
||||
uint32_t graphicsQueue = (uint32_t) device->getQueues().graphics;
|
||||
VkImageCreateInfo info {
|
||||
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, nullptr,
|
||||
flags, VK_IMAGE_TYPE_2D, format,
|
||||
extent, config.mipping, config.layers, config.samples, config.tiling,
|
||||
usage, VK_SHARING_MODE_EXCLUSIVE,
|
||||
1, &graphicsQueue, config.layout
|
||||
};
|
||||
|
||||
// Prepare the managed image
|
||||
VkTools::ManagedImage image {};
|
||||
|
||||
// Set up image allocation
|
||||
VmaAllocationCreateInfo allocateInfo = {};
|
||||
allocateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
||||
|
||||
// Allocate + create the image
|
||||
vmaCreateImage(VulkanModule::getInstance()->getAllocator(), &info, &allocateInfo, &image.image, &image.allocation, nullptr);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void waitForBarrier(const VkCommandBuffer& commands, const VkImageMemoryBarrier& barrier, const std::array<VkPipelineStageFlags, 2>& stages) {
|
||||
vkCmdPipelineBarrier(commands, stages[0], stages[1], 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
}
|
||||
|
||||
void transitionImage(const VkImage& image, const ImageConfig& config, VkImageAspectFlags aspect, const std::array<VkImageLayout, 2>& layouts, const std::array<VkAccessFlags, 2>& access, const std::array<VkPipelineStageFlags, 2>& stages) {
|
||||
VkTools::immediateExecute([&](const VkCommandBuffer& commands) {
|
||||
waitForBarrier(commands,
|
||||
{
|
||||
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
nullptr,
|
||||
access[0], access[1],
|
||||
layouts[0], layouts[1],
|
||||
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
|
||||
image,
|
||||
{ aspect, 0, config.mipping, 0, config.layers }
|
||||
}, stages);
|
||||
}, VulkanModule::getInstance()->getDevice());
|
||||
}
|
||||
|
||||
inline VkOffset3D convertExtent(const VkExtent2D& extent) {
|
||||
return VkOffset3D { static_cast<int32_t>(extent.width), static_cast<int32_t>(extent.height), 1 };
|
||||
}
|
||||
|
||||
inline VkExtent3D expandExtent(const VkExtent2D& extent) {
|
||||
return VkExtent3D { extent.width, extent.height, 1 };
|
||||
}
|
||||
|
||||
std::vector<VkExtent2D> getExtentForMipmaps(const VkExtent3D& extent) {
|
||||
const int largest = std::max(extent.width, extent.height);
|
||||
const int mipping = std::floor(std::log2(largest));
|
||||
std::vector<VkExtent2D> extents(mipping);
|
||||
VkExtent2D imageExt { extent.width, extent.height };
|
||||
for (size_t level = 0; level < mipping; ++level) {
|
||||
imageExt.width = imageExt.width > 1 ? imageExt.width / 2 : 1;
|
||||
imageExt.height = imageExt.height > 1 ? imageExt.height / 2 : 1;
|
||||
extents[level] = imageExt;
|
||||
}
|
||||
|
||||
return extents;
|
||||
}
|
||||
|
||||
void generateMipmaps(const VkImage& image, VkFormat format, const VkExtent3D& extent, const std::vector<VkExtent2D>& mipExtents) {
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(VulkanModule::getInstance()->getDevice()->physical, format, &props);
|
||||
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT))
|
||||
throw std::runtime_error("Attempting to create Mipmaps for an image format that does not support blitting");
|
||||
|
||||
uint32_t destLevel = 1;
|
||||
VkExtent2D previousExt { extent.width, extent.height };
|
||||
|
||||
VkTools::immediateExecute([&](const VkCommandBuffer& commands) {
|
||||
// Blit the new images into place
|
||||
for (const auto &ext : mipExtents) {
|
||||
const uint32_t sourceLevel = destLevel - 1;
|
||||
VkImageMemoryBarrier barrier {
|
||||
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
nullptr, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
0, 0, image, { VK_IMAGE_ASPECT_COLOR_BIT, sourceLevel, 1, 0, 1 }
|
||||
};
|
||||
|
||||
waitForBarrier(commands, barrier, {VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT});
|
||||
|
||||
const VkImageBlit blit {
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, sourceLevel, 0, 1},
|
||||
{{0, 0, 0}, convertExtent(previousExt)},
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, destLevel, 0, 1},
|
||||
{{0, 0, 0}, convertExtent(ext)}
|
||||
};
|
||||
vkCmdBlitImage(commands, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR);
|
||||
|
||||
++destLevel;
|
||||
previousExt = ext;
|
||||
}
|
||||
}, VulkanModule::getInstance()->getDevice());
|
||||
|
||||
VkTools::immediateExecute([&](const VkCommandBuffer& commands) {
|
||||
// Convert all images to shader read only so we can sample them
|
||||
for (uint32_t level = 0; level < mipExtents.size() + 1; ++level) {
|
||||
VkImageMemoryBarrier barrier {
|
||||
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
nullptr, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
|
||||
level == mipExtents.size() ? VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
0, 0, image, { VK_IMAGE_ASPECT_COLOR_BIT, level, 1, 0, 1 }
|
||||
};
|
||||
|
||||
waitForBarrier(commands, barrier, { VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT });
|
||||
}
|
||||
}, VulkanModule::getInstance()->getDevice());
|
||||
}
|
||||
|
||||
Image::Image(const VkExtent2D &ext, VkFormat form) : extent(ext), format(form) {
|
||||
dev = VulkanModule::getInstance()->getDevice();
|
||||
}
|
||||
|
||||
void ImageStagingBuffer::copy(const VkImage &target, const VkExtent3D &extent, uint32_t layers) const {
|
||||
VkTools::immediateExecute([&](const VkCommandBuffer& commands) {
|
||||
const VkBufferImageCopy copyData { 0, 0, 0, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, layers }, { 0, 0, 0 }, extent };
|
||||
vkCmdCopyBufferToImage(commands, getBuffer(), target, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Data);
|
||||
}, VulkanModule::getInstance()->getDevice());
|
||||
}
|
||||
|
||||
ImageSampler::ImageSampler(int mipLevels, const vlkx::ImageSampler::Config &config)
|
||||
: sampler(VkTools::createSampler(config.filter, config.mode, mipLevels, VulkanModule::getInstance()->getDevice()->logical)) {
|
||||
dev = VulkanModule::getInstance()->getDevice();
|
||||
}
|
||||
|
||||
Buffer::BulkCopyMeta TextureImage::Meta::getCopyMeta() const {
|
||||
const VkDeviceSize singleSize = width * height * channels;
|
||||
const VkDeviceSize totalSize = singleSize * data.size();
|
||||
std::vector<Buffer::CopyMeta> copy(data.size());
|
||||
|
||||
// If we're making a framebuffer, we have no data to copy in.
|
||||
if (data[0] == nullptr)
|
||||
return { totalSize, {} };
|
||||
|
||||
for (size_t i = 0; i < copy.size(); ++i) {
|
||||
copy[i] = { data[i], singleSize, singleSize * i };
|
||||
}
|
||||
|
||||
return { totalSize, std::move(copy) };
|
||||
}
|
||||
|
||||
TextureImage::TextureImage(bool mipmapping, const ImageSampler::Config &samplerConfig, const vlkx::TextureImage::Meta &meta)
|
||||
: Image(meta.getExtent(), meta.format), buffer(mipmapping, meta), sampler(buffer.getMipping(), samplerConfig) {
|
||||
|
||||
setView(VkTools::createImageView(buffer.getImage(), format, VK_IMAGE_ASPECT_COLOR_BIT, buffer.getMipping(), meta.data.size(), VulkanModule::getInstance()->getDevice()->logical));
|
||||
}
|
||||
|
||||
TextureImage::TextureImage(bool mipmapping, const ImageDescriptor &image, const std::vector<ImageUsage>& usages, const ImageSampler::Config &config)
|
||||
: TextureImage(mipmapping, config, createTextureMeta(image, usages))
|
||||
{}
|
||||
|
||||
TextureImage::TextureBuffer::TextureBuffer(bool mipmaps, const vlkx::TextureImage::Meta &meta) : ImageBuffer() {
|
||||
const VkExtent3D extent = expandExtent(meta.getExtent());
|
||||
const auto layers = meta.data.size();
|
||||
if (layers != 1 && layers != 6)
|
||||
throw std::runtime_error("Attempting to allocate a texture buffer for an invalid number of textures; only single textures and cubemap textures are supported.");
|
||||
|
||||
ImageConfig config;
|
||||
config.layers = meta.data.size();
|
||||
|
||||
std::vector<VkExtent2D> mipExtents;
|
||||
if (mipmaps) {
|
||||
mipExtents = getExtentForMipmaps(extent);
|
||||
mipLevels = mipExtents.size() + 1;
|
||||
}
|
||||
|
||||
config.mipping = mipLevels;
|
||||
|
||||
VkImageCreateFlags createFlags {};
|
||||
if (layers == 6)
|
||||
createFlags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
||||
|
||||
auto usage = ImageUsage::getFlagsForUsage(meta.usages);
|
||||
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
if (mipmaps) usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
|
||||
setImage(createImage(config, createFlags, meta.format, extent, usage));
|
||||
|
||||
transitionImage(getImage(), config, VK_IMAGE_ASPECT_COLOR_BIT, { VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL }, { 0, VK_ACCESS_TRANSFER_WRITE_BIT }, { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT });
|
||||
|
||||
const ImageStagingBuffer staging(meta.getCopyMeta());
|
||||
staging.copy(getImage(), extent, config.layers);
|
||||
|
||||
if (mipmaps) {
|
||||
generateMipmaps(getImage(), meta.format, extent, mipExtents);
|
||||
} else {
|
||||
transitionImage(getImage(), config, VK_IMAGE_ASPECT_COLOR_BIT, { VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, { VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT }, { VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT });
|
||||
}
|
||||
}
|
||||
|
||||
RefCountedTexture::ReferenceCounter RefCountedTexture::get(const vlkx::RefCountedTexture::ImageLocation &location, const std::vector<ImageUsage>& usages, const ImageSampler::Config &config) {
|
||||
bool mips;
|
||||
const std::string* ident;
|
||||
std::unique_ptr<ImageDescriptor> image;
|
||||
|
||||
if (const auto* singleTex = std::get_if<std::string>(&location); singleTex != nullptr) {
|
||||
mips = true;
|
||||
ident = singleTex;
|
||||
image = std::make_unique<ImageDescriptor>(Image::loadSingleFromDisk(*singleTex, false));
|
||||
} else if (const auto* cubeTex = std::get_if<CubemapLocation>(&location); cubeTex != nullptr) {
|
||||
mips = false;
|
||||
ident = &cubeTex->directory;
|
||||
image = std::make_unique<ImageDescriptor>(Image::loadCubeFromDisk(cubeTex->directory, cubeTex->files, false));
|
||||
}
|
||||
|
||||
return ReferenceCounter::get(*ident, mips, config, createTextureMeta(*image, usages));
|
||||
}
|
||||
|
||||
DepthStencilImage::DepthStencilImage(const VkExtent2D &extent) : Image(extent, findFormatForDepthStencil()), buffer(extent, format) {
|
||||
setView(VkTools::createImageView(getImage(), format, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, 1, 1, VulkanModule::getInstance()->getDevice()->logical));
|
||||
}
|
||||
|
||||
DepthStencilImage::DepthStencilBuffer::DepthStencilBuffer(const VkExtent2D &extent, VkFormat format) : ImageBuffer() {
|
||||
setImage(createImage(ImageConfig {}, 0, format, expandExtent(extent), VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT));
|
||||
}
|
||||
|
||||
SwapchainImage::SwapchainImage(const VkImage &image, const VkExtent2D &extent, VkFormat format) : Image(extent, format), image(image) {
|
||||
setView(VkTools::createImageView(image, format, VK_IMAGE_ASPECT_COLOR_BIT, 1, 1, VulkanModule::getInstance()->getDevice()->logical));
|
||||
managed = { image, nullptr };
|
||||
}
|
||||
|
||||
std::unique_ptr<Image> MultisampleImage::createColor(const vlkx::Image &targetImage, vlkx::MultisampleImage::Mode mode) {
|
||||
return std::unique_ptr<Image>(new MultisampleImage(targetImage.getExtent(), targetImage.getFormat(), mode, MultisampleBuffer::Type::Color));
|
||||
}
|
||||
|
||||
std::unique_ptr<Image> MultisampleImage::createDepthStencilMS(const VkExtent2D &extent, vlkx::MultisampleImage::Mode mode) {
|
||||
return std::unique_ptr<Image>(new MultisampleImage(extent, findFormatForDepthStencil(), mode, MultisampleBuffer::Type::DepthStencil));
|
||||
}
|
||||
|
||||
std::unique_ptr<Image> MultisampleImage::createDepthStencil(VkExtent2D &extent, std::optional<Mode> mode) {
|
||||
if (mode.has_value())
|
||||
return createDepthStencilMS(extent, mode.value());
|
||||
else
|
||||
return std::make_unique<DepthStencilImage>(extent);
|
||||
}
|
||||
|
||||
MultisampleImage::MultisampleImage(const VkExtent2D &extent, VkFormat format, vlkx::MultisampleImage::Mode mode,MultisampleBuffer::Type type)
|
||||
: Image(extent, format), samples(chooseSamples(mode)), buffer(type, extent, format, samples) {
|
||||
|
||||
VkImageAspectFlags aspect;
|
||||
switch (type) {
|
||||
case MultisampleBuffer::Type::Color: aspect = VK_IMAGE_ASPECT_COLOR_BIT; break;
|
||||
case MultisampleBuffer::Type::DepthStencil: aspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; break;
|
||||
}
|
||||
setView(VkTools::createImageView(buffer.getImage(), format, aspect, 1, 1, VulkanModule::getInstance()->getDevice()->logical));
|
||||
}
|
||||
|
||||
VkSampleCountFlagBits MultisampleImage::chooseSamples(vlkx::MultisampleImage::Mode mode) {
|
||||
VkPhysicalDeviceLimits limits;
|
||||
VkPhysicalDeviceProperties props;
|
||||
vkGetPhysicalDeviceProperties(VulkanModule::getInstance()->getDevice()->physical, &props);
|
||||
limits = props.limits;
|
||||
|
||||
const VkSampleCountFlags sampleFlags = std::min({ limits.framebufferColorSampleCounts, limits.framebufferDepthSampleCounts, limits.framebufferStencilSampleCounts });
|
||||
const VkSampleCountFlagBits maxSamples = getMaxSamples(sampleFlags);
|
||||
switch (mode) {
|
||||
case Mode::MostEfficient: return std::min(VK_SAMPLE_COUNT_4_BIT, maxSamples);
|
||||
case Mode::Highest: return maxSamples;
|
||||
}
|
||||
}
|
||||
|
||||
MultisampleImage::MultisampleBuffer::MultisampleBuffer(vlkx::MultisampleImage::MultisampleBuffer::Type type, const VkExtent2D &extent, VkFormat format, VkSampleCountFlagBits samples)
|
||||
: ImageBuffer() {
|
||||
|
||||
VkImageUsageFlags usageFlags;
|
||||
switch (type) {
|
||||
case Type::Color: usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; break;
|
||||
case Type::DepthStencil: usageFlags = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; break;
|
||||
}
|
||||
|
||||
ImageConfig config;
|
||||
config.samples = samples;
|
||||
|
||||
setImage(createImage(config, 0, format, expandExtent(extent), usageFlags));
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
namespace shadowutil {
|
||||
|
||||
struct FileData {
|
||||
size_t size;
|
||||
std::vector<char> data;
|
||||
};
|
||||
|
||||
// A testing stub; this should be deleted and wired into the asset system once that becomes ready.
|
||||
FileData* loadFile(std::string path);
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
namespace shadowutil {
|
||||
|
||||
// An object wrapper that behaves like smart pointers.
|
||||
// References to this object are counted, and the object will be destructed when there are no references.
|
||||
// This allows for automatic, safe and leak-free handling of all kinds of resources.
|
||||
// The AutoRelease behaviour can be adjusted.
|
||||
template <typename ObjectType>
|
||||
class RefCounter {
|
||||
public:
|
||||
|
||||
// Preserves ObjectType instances in the current scope.
|
||||
// Use like lock_guard; RAII allows precise scoping rules.
|
||||
class AutoRelease {
|
||||
public:
|
||||
explicit AutoRelease() { RefCounter<ObjectType>::registerAutoRelease(); }
|
||||
|
||||
AutoRelease(const AutoRelease&) = delete;
|
||||
AutoRelease& operator=(const AutoRelease&) = delete;
|
||||
|
||||
~AutoRelease() { RefCounter<ObjectType>::unregisterAutoRelease(); }
|
||||
|
||||
// AutoRelease can only exist on the stack.
|
||||
void* operator new(size_t) = delete;
|
||||
void* operator new[](size_t) = delete;
|
||||
};
|
||||
|
||||
// Use to access a ref counted object.
|
||||
// If exists, the object will be passed and reference counter increased.
|
||||
// Otherwise, args will be used to create.
|
||||
template<typename... Args>
|
||||
static RefCounter get(const std::string& identifier, Args&&... args) {
|
||||
auto iter = getMap().find(identifier);
|
||||
if (iter == getMap().end()) {
|
||||
const auto inserted = getMap().insert(
|
||||
{
|
||||
identifier,
|
||||
typename ObjectPool::CountedObject {
|
||||
std::make_unique<ObjectType>(std::forward<Args>(args)...), 0
|
||||
}
|
||||
}
|
||||
);
|
||||
iter = inserted.first;
|
||||
}
|
||||
|
||||
auto& object = iter->second;
|
||||
++object.references;
|
||||
return RefCounter { identifier, object.obj.get() };
|
||||
}
|
||||
|
||||
RefCounter(RefCounter&& other) noexcept {
|
||||
identifier = std::move(other.identifier);
|
||||
objectPtr = other.objectPtr;
|
||||
other.identifier.clear();
|
||||
}
|
||||
|
||||
RefCounter& operator=(RefCounter&& other) noexcept {
|
||||
std::swap(identifier, other.identifier);
|
||||
std::swap(objectPtr, other.objectPtr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// When we reach 0 references and there's no auto release system set up, destroy the object.
|
||||
~RefCounter() {
|
||||
if (identifier.empty()) return;
|
||||
|
||||
const auto iter = getMap().find(identifier);
|
||||
if (--iter->second.references == 0 && objectPool.activePools == 0)
|
||||
getMap().erase(iter);
|
||||
}
|
||||
|
||||
// Smart pointer emulation overloads.
|
||||
const ObjectType* operator->() const { return objectPtr; }
|
||||
const ObjectType& operator*() const { return *objectPtr; }
|
||||
|
||||
static bool hasAutoRelease() { return objectPool.activePools != 0; }
|
||||
|
||||
private:
|
||||
// The object pool that handles managing and counting objects.
|
||||
struct ObjectPool {
|
||||
struct CountedObject {
|
||||
std::unique_ptr<ObjectType> obj;
|
||||
size_t references;
|
||||
};
|
||||
|
||||
using RefCountMap = std::map<std::string, CountedObject>;
|
||||
|
||||
RefCountMap refCountMap;
|
||||
size_t activePools = 0;
|
||||
};
|
||||
|
||||
RefCounter(std::string identifier, const ObjectType* obj) : identifier(std::move(identifier)), objectPtr(obj) {}
|
||||
|
||||
static void unregisterAutoRelease() {
|
||||
if (--objectPool.activePools == 0) {
|
||||
using CountedObject = typename ObjectPool::CountedObject;
|
||||
static const auto removeZeroRef = [](const std::pair<const std::string, CountedObject> &pair) {
|
||||
return pair.second.references == 0;
|
||||
};
|
||||
std::erase_if(getMap(), removeZeroRef);
|
||||
}
|
||||
}
|
||||
|
||||
static void registerAutoRelease() { ++objectPool.activePools; }
|
||||
|
||||
static typename ObjectPool::RefCountMap& getMap() { return objectPool.refCountMap; }
|
||||
|
||||
static ObjectPool objectPool;
|
||||
|
||||
std::string identifier;
|
||||
const ObjectType* objectPtr;
|
||||
};
|
||||
|
||||
template <typename ObjectType>
|
||||
typename RefCounter<ObjectType>::ObjectPool RefCounter<ObjectType>::objectPool {};
|
||||
}
|
7897
projs/shadow/shadow-engine/shadow-utility/inc/stb_image.h
Normal file
7897
projs/shadow/shadow-engine/shadow-utility/inc/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
26
projs/shadow/shadow-engine/shadow-utility/src/File.cpp
Normal file
26
projs/shadow/shadow-engine/shadow-utility/src/File.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include <shadow/util/File.h>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
namespace shadowutil {
|
||||
FileData* loadFile(std::string path) {
|
||||
// Verify the file
|
||||
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
||||
|
||||
if (!file.is_open())
|
||||
throw std::runtime_error("Unable to open specified file: " + std::string(path));
|
||||
|
||||
auto* data = new FileData {};
|
||||
|
||||
// Read the file's size (opened with At The End, so just tellg the size)
|
||||
size_t size = file.tellg();
|
||||
data->data.resize(size);
|
||||
data->size = size;
|
||||
// Go to the front of the file
|
||||
file.seekg(0);
|
||||
// Read the file into the buffer
|
||||
file.read(data->data.data(), size);
|
||||
file.close();
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
#include "string-helpers.h"
|
||||
#include "../inc/string-helpers.h"
|
||||
|
||||
std::vector<std::string> explode(const std::string& s, const char& c)
|
||||
{
|
||||
|
|
|
@ -8,7 +8,6 @@ add_executable(shadow-runtime ${SOURCES})
|
|||
target_include_directories(shadow-runtime PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
target_link_libraries(shadow-runtime PRIVATE SDL2::SDL2main PUBLIC shadow-engine)
|
||||
|
||||
|
||||
add_custom_command(TARGET shadow-runtime POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:shadow-runtime> $<TARGET_FILE_DIR:shadow-runtime>
|
||||
COMMAND_EXPAND_LISTS
|
||||
|
|
|
@ -13,13 +13,17 @@ public:
|
|||
|
||||
void Init() override;
|
||||
|
||||
void Update() override;
|
||||
void Update(int frame) override;
|
||||
|
||||
void PreRender() override;
|
||||
|
||||
void Render() override;
|
||||
void Recreate() override;
|
||||
|
||||
void LateRender() override;
|
||||
void OverlayRender() override;
|
||||
|
||||
void Render(VkCommandBuffer& commands, int frame) override;
|
||||
|
||||
void LateRender(VkCommandBuffer& commands, int frame) override;
|
||||
|
||||
void AfterFrameEnd() override;
|
||||
|
||||
|
|
|
@ -1,171 +1,92 @@
|
|||
#include <GameModule.h>
|
||||
#include "imgui.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "vlkx/render/geometry/SingleRenderer.h"
|
||||
#include "imgui_impl_vulkan.h"
|
||||
#include "core/ShadowApplication.h"
|
||||
#include "core/SDL2Module.h"
|
||||
#include "imgui_impl_sdl.h"
|
||||
#include "core/Time.h"
|
||||
|
||||
#include "vlkx/render/Camera.h"
|
||||
#include "vlkx/vulkan/abstraction/Buffer.h"
|
||||
#include "vlkx/render/render_pass/ScreenRenderPass.h"
|
||||
#include "temp/model/Builder.h"
|
||||
#include "core/ModuleManager.h"
|
||||
|
||||
#define CATCH(x) \
|
||||
try { x } catch (std::exception& e) { spdlog::error(e.what()); exit(0); }
|
||||
|
||||
// Create the renderer
|
||||
SingleRenderer object;
|
||||
|
||||
// Create the camera
|
||||
Camera camera;
|
||||
|
||||
SHObject_Base_Impl(GameModule)
|
||||
|
||||
void GameModule::PreInit() { spdlog::info("Game Module loading.."); }
|
||||
struct Transformation {
|
||||
alignas(sizeof(glm::mat4)) glm::mat4 proj_view_model;
|
||||
};
|
||||
|
||||
std::unique_ptr<vlkx::PushConstant> trans_constant_;
|
||||
std::unique_ptr<vlkxtemp::Model> cube_model_;
|
||||
float aspectRatio;
|
||||
|
||||
void GameModule::PreInit() { }
|
||||
|
||||
void GameModule::Init() {
|
||||
spdlog::info("Game Module loading level..");
|
||||
trans_constant_ = std::make_unique<vlkx::PushConstant>(
|
||||
sizeof(Transformation), 2);
|
||||
|
||||
auto shApp = ShadowEngine::ShadowApplication::Get();
|
||||
auto extent = ShadowEngine::ModuleManager::getInstance()->renderer->GetRenderExtent();
|
||||
aspectRatio = (float) extent.width / extent.height;
|
||||
|
||||
ShadowEngine::ModuleManager &moduleManager = shApp.GetModuleManager();
|
||||
/* Model */
|
||||
cube_model_ = vlkxtemp::ModelBuilder {
|
||||
"Walrus", 2, aspectRatio,
|
||||
vlkxtemp::ModelBuilder::SingleMeshModel {"resources/walrus/walrus.obj", 1,
|
||||
{{ vlkxtemp::ModelBuilder::TextureType::Diffuse, { { "resources/walrus/texture.png" } } } }
|
||||
}}
|
||||
.bindTextures(vlkxtemp::ModelBuilder::TextureType::Diffuse, 1)
|
||||
.pushStage(VK_SHADER_STAGE_VERTEX_BIT)
|
||||
.pushConstant(trans_constant_.get(), 0)
|
||||
.shader(VK_SHADER_STAGE_VERTEX_BIT, "resources/walrus/cube.vert.spv")
|
||||
.shader(VK_SHADER_STAGE_FRAGMENT_BIT, "resources/walrus/cube.frag.spv")
|
||||
.build();
|
||||
|
||||
auto sdl2module = moduleManager.GetModuleByType<ShadowEngine::SDL2Module>();
|
||||
|
||||
CATCH(VulkanManager::getInstance()->initVulkan(sdl2module->_window->sdlWindowPtr);)
|
||||
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
|
||||
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
|
||||
|
||||
// Setup Dear ImGui style
|
||||
ImGui::StyleColorsDark();
|
||||
VkDescriptorPool imGuiPool;
|
||||
VulkanManager* vk = VulkanManager::getInstance();
|
||||
VkDescriptorPoolSize pool_sizes[] =
|
||||
{
|
||||
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
|
||||
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
|
||||
};
|
||||
|
||||
VkDescriptorPoolCreateInfo pool_info = {};
|
||||
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
||||
pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes);
|
||||
pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);
|
||||
pool_info.pPoolSizes = pool_sizes;
|
||||
vkCreateDescriptorPool(vk->getDevice()->logical, &pool_info, VK_NULL_HANDLE, &imGuiPool);
|
||||
|
||||
// Setup Platform/Renderer backends
|
||||
ImGui_ImplSDL2_InitForVulkan(sdl2module->_window->sdlWindowPtr);
|
||||
ImGui_ImplVulkan_InitInfo init_info = {};
|
||||
init_info.Instance = vk->getVulkan();
|
||||
init_info.PhysicalDevice = vk->getDevice()->physical;
|
||||
init_info.Device = vk->getDevice()->logical;
|
||||
init_info.QueueFamily = vk->getDevice()->queueData.graphics;
|
||||
init_info.Queue = vk->getDevice()->graphicsQueue;
|
||||
init_info.PipelineCache = VK_NULL_HANDLE;
|
||||
init_info.DescriptorPool = imGuiPool;
|
||||
init_info.Subpass = 0;
|
||||
init_info.MinImageCount = vk->getSwapchain()->images.size();
|
||||
init_info.ImageCount = vk->getSwapchain()->images.size();
|
||||
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
init_info.Allocator = VK_NULL_HANDLE;
|
||||
init_info.CheckVkResultFn = nullptr;
|
||||
ImGui_ImplVulkan_Init(&init_info, vk->getRenderPass()->pass);
|
||||
|
||||
// Upload Fonts
|
||||
{
|
||||
// Prepare to create a temporary command pool.
|
||||
VkCommandPool pool;
|
||||
VkCommandPoolCreateInfo poolCreateInfo = {};
|
||||
poolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
poolCreateInfo.queueFamilyIndex = vk->getDevice()->queueData.graphics;
|
||||
poolCreateInfo.flags = 0;
|
||||
|
||||
// Create the pool
|
||||
if (vkCreateCommandPool(vk->getDevice()->logical, &poolCreateInfo, nullptr, &pool) != VK_SUCCESS)
|
||||
throw std::runtime_error("Unable to allocate a temporary command pool");
|
||||
|
||||
VkCommandBuffer buffer = VkTools::createTempCommandBuffer(pool, vk->getDevice()->logical);
|
||||
|
||||
ImGui_ImplVulkan_CreateFontsTexture(buffer);
|
||||
|
||||
VkSubmitInfo end_info = {};
|
||||
end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
end_info.commandBufferCount = 1;
|
||||
end_info.pCommandBuffers = &buffer;
|
||||
|
||||
VkTools::executeAndDeleteTempBuffer(buffer, pool, vk->getDevice()->graphicsQueue, vk->getDevice()->logical);
|
||||
|
||||
ImGui_ImplVulkan_DestroyFontUploadObjects();
|
||||
}
|
||||
|
||||
CATCH(object.createSingleRenderer(Geo::MeshType::Cube, glm::vec3(-1, 0, -1), glm::vec3(0.5));)
|
||||
camera.init(45, 1280, 720, 0.1, 10000);
|
||||
camera.setPosition(glm::vec3(0, 0, 4));
|
||||
Recreate();
|
||||
}
|
||||
|
||||
void GameModule::Update() {
|
||||
object.setRotation(glm::rotate(object.getRotation(), (float) 0.5, glm::vec3(1, 0, 0)));
|
||||
void GameModule::Recreate() {
|
||||
auto extent = ShadowEngine::ModuleManager::getInstance()->renderer->GetRenderExtent();
|
||||
cube_model_->update(true, extent, VK_SAMPLE_COUNT_1_BIT, *VulkanModule::getInstance()->getRenderPass()->getPass(), 0);
|
||||
}
|
||||
|
||||
void GameModule::PreRender() {
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
void GameModule::Update(int frame) {
|
||||
const float elapsed_time = Time::timeSinceStart;
|
||||
const glm::mat4 model = glm::rotate(glm::mat4{1.0f},
|
||||
(elapsed_time / 1000 / 2) * glm::radians(90.0f),
|
||||
glm::vec3{1.0f, 1.0f, 0.0f});
|
||||
const glm::mat4 view = glm::lookAt(glm::vec3{3.0f}, glm::vec3{0.0f},
|
||||
glm::vec3{0.0f, 0.0f, 1.0f});
|
||||
const glm::mat4 proj = glm::perspective(
|
||||
glm::radians(45.0f), aspectRatio,
|
||||
0.1f, 100.0f);
|
||||
*trans_constant_->getData<Transformation>(frame) = {proj * view * model};
|
||||
}
|
||||
|
||||
void GameModule::Render() {
|
||||
void GameModule::Render(VkCommandBuffer& commands, int frame) {
|
||||
cube_model_->draw(commands, frame, 1);
|
||||
}
|
||||
|
||||
void GameModule::OverlayRender() {
|
||||
bool active = true;
|
||||
ImGui::Begin("Game module window", &active, ImGuiWindowFlags_MenuBar);
|
||||
|
||||
ImGui::Text("Such teext from curle's branch");
|
||||
|
||||
if (ImGui::Begin("Game module window", &active, ImGuiWindowFlags_MenuBar))
|
||||
ImGui::Text("Such text from curle's branch");
|
||||
ImGui::End();
|
||||
|
||||
CATCH(object.updateUniforms(camera);)
|
||||
CATCH(object.draw();)
|
||||
|
||||
bool showDemo = true;
|
||||
if (showDemo)
|
||||
ImGui::ShowDemoWindow(&showDemo);
|
||||
|
||||
}
|
||||
|
||||
void GameModule::LateRender() {
|
||||
ImGui::Render();
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
|
||||
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), VulkanManager::getInstance()->getCurrentCommandBuffer());
|
||||
|
||||
// Update and Render additional Platform Windows
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
||||
{
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
}
|
||||
bool open = false;
|
||||
ImGui::ShowDemoWindow(&open);
|
||||
}
|
||||
|
||||
void GameModule::AfterFrameEnd() {
|
||||
Time::UpdateTime();
|
||||
}
|
||||
|
||||
void GameModule::Destroy() {
|
||||
ImGui_ImplVulkan_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
void GameModule::LateRender(VkCommandBuffer& commands, int frame) {}
|
||||
void GameModule::PreRender() {}
|
||||
|
||||
void GameModule::Event(SDL_Event *) {
|
||||
|
||||
}
|
||||
void GameModule::Destroy() {}
|
||||
void GameModule::Event(SDL_Event *) {}
|
|
@ -7,19 +7,8 @@
|
|||
#include "core/ShadowApplication.h"
|
||||
#include "GameModule.h"
|
||||
#include "core/ShadowApplication.h"
|
||||
#include "vlkx/vulkan/VulkanManager.h"
|
||||
#include "vlkx/vulkan/VulkanModule.h"
|
||||
|
||||
extern "C" __declspec(dllexport) void shadow_main(ShadowEngine::ShadowApplication* app) {
|
||||
|
||||
std::cout << "HIIII from a loaded dll weeeeeee!!!" << std::endl;
|
||||
|
||||
app->GetModuleManager().PushModule(std::make_shared<GameModule>(), "game");
|
||||
|
||||
if(app == &ShadowEngine::ShadowApplication::Get()){
|
||||
std::cout << "They are the same!!!" << std::endl;
|
||||
}
|
||||
|
||||
printf("dll side: %p \n", VulkanManager::getInstance());
|
||||
printf("dll next ID: %llu \n", ShadowEngine::SHObject::GenerateId());
|
||||
|
||||
}
|
||||
|
|
10
resources/planets/skybox.frag
Normal file
10
resources/planets/skybox.frag
Normal file
|
@ -0,0 +1,10 @@
|
|||
#version 460 core
|
||||
|
||||
layout(binding = 1) uniform samplerCube tex;
|
||||
|
||||
layout(location = 0) in vec3 tex_coord;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(tex, tex_coord);
|
||||
}
|
18
resources/planets/skybox.vert
Normal file
18
resources/planets/skybox.vert
Normal file
|
@ -0,0 +1,18 @@
|
|||
#version 450
|
||||
|
||||
layout(std140, push_constant) uniform Transform {
|
||||
mat4 matrix;
|
||||
} transf;
|
||||
|
||||
layout(location = 0) in vec3 in_pos;
|
||||
layout(location = 1) in vec3 in_normal;
|
||||
layout(location = 2) in vec2 in_uv;
|
||||
|
||||
layout(location = 0) out vec3 texCoord;
|
||||
|
||||
void main() {
|
||||
|
||||
gl_Position = transf.matrix * vec4(in_pos, 1.0);
|
||||
gl_Position.zw = vec2(1.0);
|
||||
texCoord = in_pos;
|
||||
}
|
13
resources/tri/tri.frag
Normal file
13
resources/tri/tri.frag
Normal file
|
@ -0,0 +1,13 @@
|
|||
#version 450
|
||||
|
||||
layout(push_constant) uniform Alpha {
|
||||
float value;
|
||||
} alpha;
|
||||
|
||||
layout(location = 0) in vec3 color;
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = vec4(color, alpha.value);
|
||||
}
|
BIN
resources/tri/tri.frag.spv
Normal file
BIN
resources/tri/tri.frag.spv
Normal file
Binary file not shown.
11
resources/tri/tri.vert
Normal file
11
resources/tri/tri.vert
Normal file
|
@ -0,0 +1,11 @@
|
|||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 in_pos;
|
||||
layout(location = 1) in vec3 in_color;
|
||||
|
||||
layout(location = 0) out vec3 color;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(in_pos, 1.0);
|
||||
color = in_color;
|
||||
}
|
BIN
resources/tri/tri.vert.spv
Normal file
BIN
resources/tri/tri.vert.spv
Normal file
Binary file not shown.
10
resources/walrus/cube.frag
Normal file
10
resources/walrus/cube.frag
Normal file
|
@ -0,0 +1,10 @@
|
|||
#version 450
|
||||
|
||||
layout(binding = 1) uniform sampler2D tex;
|
||||
|
||||
layout(location = 0) in vec2 tex_coord;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(tex, tex_coord);
|
||||
}
|
BIN
resources/walrus/cube.frag.spv
Normal file
BIN
resources/walrus/cube.frag.spv
Normal file
Binary file not shown.
16
resources/walrus/cube.vert
Normal file
16
resources/walrus/cube.vert
Normal file
|
@ -0,0 +1,16 @@
|
|||
#version 450
|
||||
|
||||
layout(std140, push_constant) uniform Transform {
|
||||
mat4 matrix;
|
||||
} transf;
|
||||
|
||||
layout(location = 0) in vec3 in_pos;
|
||||
layout(location = 1) in vec3 in_normal;
|
||||
layout(location = 2) in vec2 in_uv;
|
||||
|
||||
layout(location = 0) out vec2 texCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = transf.matrix * vec4(in_pos, 1.0);
|
||||
texCoord = in_uv;
|
||||
}
|
BIN
resources/walrus/cube.vert.spv
Normal file
BIN
resources/walrus/cube.vert.spv
Normal file
Binary file not shown.
BIN
resources/walrus/texture.png
Normal file
BIN
resources/walrus/texture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 487 KiB |
12745
resources/walrus/walrus.obj
Normal file
12745
resources/walrus/walrus.obj
Normal file
File diff suppressed because it is too large
Load Diff
BIN
vlkx-resources/pkg/01 - Copy.vxp
Normal file
BIN
vlkx-resources/pkg/01 - Copy.vxp
Normal file
Binary file not shown.
BIN
vlkx-resources/pkg/01.vxp
Normal file
BIN
vlkx-resources/pkg/01.vxp
Normal file
Binary file not shown.
1
vlkx-resources/pkg/index.vxi
Normal file
1
vlkx-resources/pkg/index.vxi
Normal file
|
@ -0,0 +1 @@
|
|||
01.vxp
|
BIN
vlkx-resources/shader/SPIRV/basic.frag.spv
Normal file
BIN
vlkx-resources/shader/SPIRV/basic.frag.spv
Normal file
Binary file not shown.
BIN
vlkx-resources/shader/SPIRV/basic.vert.spv
Normal file
BIN
vlkx-resources/shader/SPIRV/basic.vert.spv
Normal file
Binary file not shown.
BIN
vlkx-resources/shader/SPIRV/rt.stage1.frag.spv
Normal file
BIN
vlkx-resources/shader/SPIRV/rt.stage1.frag.spv
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user