yes
This commit is contained in:
126
Runtime/Sources/Core/Engine.cpp
git.filemode.normal_file
126
Runtime/Sources/Core/Engine.cpp
git.filemode.normal_file
@@ -0,0 +1,126 @@
|
||||
#include <Core/Engine.h>
|
||||
#include <Renderer/RenderCore.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <Core/Logs.h>
|
||||
#include <Core/EventBus.h>
|
||||
#include <csignal>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
struct InterruptEvent : public EventBase
|
||||
{
|
||||
Event What() const override { return Event::QuitEventCode; }
|
||||
};
|
||||
|
||||
struct SceneChangedEvent : public EventBase
|
||||
{
|
||||
Event What() const override { return Event::SceneHasChangedEventCode; }
|
||||
};
|
||||
}
|
||||
|
||||
void FatalErrorEventHandle()
|
||||
{
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void SignalHandler([[maybe_unused]] int sig)
|
||||
{
|
||||
EventBus::Send("__ScopEngine", Internal::InterruptEvent{});
|
||||
}
|
||||
|
||||
ScopEngine* ScopEngine::s_instance = nullptr;
|
||||
|
||||
ScopEngine::ScopEngine(int ac, char** av, const std::string& title, std::uint32_t width, std::uint32_t height, std::filesystem::path assets_path)
|
||||
: m_renderer(), m_window(title, width, height), m_assets_path(std::move(assets_path))
|
||||
#ifdef DEBUG
|
||||
,m_imgui(&m_renderer)
|
||||
#endif
|
||||
{
|
||||
s_instance = this;
|
||||
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
|
||||
{
|
||||
if(event.What() == Event::FatalErrorEventCode)
|
||||
FatalErrorEventHandle();
|
||||
if(event.What() == Event::QuitEventCode)
|
||||
this->Quit();
|
||||
};
|
||||
EventBus::RegisterListener({ functor, "__ScopEngine" });
|
||||
|
||||
m_cli.Feed(ac, av);
|
||||
|
||||
signal(SIGINT, SignalHandler);
|
||||
|
||||
SDL_SetHint("SDL_MOUSE_RELATIVE_MODE_WARP", "1");
|
||||
SDL_SetHint("SDL_MOUSE_RELATIVE_MODE_CENTER", "1");
|
||||
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER) != 0)
|
||||
FatalError("SDL error: unable to init all subsystems : %", SDL_GetError());
|
||||
p_renderer_core = std::make_unique<RenderCore>();
|
||||
m_renderer.Init(&m_window);
|
||||
m_scene_renderer.Init();
|
||||
#ifdef DEBUG
|
||||
m_imgui.Init(m_inputs);
|
||||
#endif
|
||||
}
|
||||
|
||||
ScopEngine& ScopEngine::Get() noexcept
|
||||
{
|
||||
Verify(s_instance != nullptr, "ScopEngine has not been instanciated");
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
void ScopEngine::Run()
|
||||
{
|
||||
Verify(p_current_scene, "no main scene registered");
|
||||
float old_timestep = static_cast<float>(SDL_GetTicks64()) / 1000.0f;
|
||||
p_current_scene->Init(&m_renderer);
|
||||
while(m_running)
|
||||
{
|
||||
float current_timestep = (static_cast<float>(SDL_GetTicks64()) / 1000.0f) - old_timestep;
|
||||
old_timestep = static_cast<float>(SDL_GetTicks64()) / 1000.0f;
|
||||
|
||||
m_inputs.Update();
|
||||
m_window.FetchWindowInfos();
|
||||
p_current_scene->Update(m_inputs, current_timestep, static_cast<float>(m_window.GetWidth()) / static_cast<float>(m_window.GetHeight()));
|
||||
|
||||
if(m_scene_changed)
|
||||
{
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
EventBus::SendBroadcast(Internal::SceneChangedEvent{});
|
||||
m_scene_changed = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
m_renderer.BeginFrame();
|
||||
m_scene_renderer.Render(*p_current_scene, m_renderer);
|
||||
#ifdef DEBUG
|
||||
m_imgui.BeginFrame();
|
||||
m_imgui.DisplayRenderStatistics();
|
||||
m_imgui.EndFrame();
|
||||
#endif
|
||||
m_renderer.EndFrame();
|
||||
|
||||
if(m_running)
|
||||
m_running = !m_inputs.HasRecievedCloseEvent();
|
||||
}
|
||||
}
|
||||
|
||||
ScopEngine::~ScopEngine()
|
||||
{
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
p_main_scene->Destroy();
|
||||
p_main_scene.reset();
|
||||
m_window.Destroy();
|
||||
#ifdef DEBUG
|
||||
m_imgui.Destroy();
|
||||
#endif
|
||||
m_scene_renderer.Destroy();
|
||||
m_renderer.Destroy();
|
||||
Model::s_default_material.reset();
|
||||
p_renderer_core.reset();
|
||||
SDL_Quit();
|
||||
Message("Successfully executed !");
|
||||
s_instance = nullptr;
|
||||
}
|
||||
}
|
||||
24
Runtime/Sources/Core/EventBus.cpp
git.filemode.normal_file
24
Runtime/Sources/Core/EventBus.cpp
git.filemode.normal_file
@@ -0,0 +1,24 @@
|
||||
#include <Core/EventBus.h>
|
||||
#include <Core/Logs.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
void EventBus::Send(const std::string& listener_name, const EventBase& event)
|
||||
{
|
||||
for(const EventListener& listener : s_listeners)
|
||||
{
|
||||
if(listener.GetName() == listener_name)
|
||||
{
|
||||
listener.Call(event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Warning("Event Bus : listener not found, '%'", listener_name);
|
||||
}
|
||||
|
||||
void EventBus::SendBroadcast(const EventBase& event)
|
||||
{
|
||||
for(const EventListener& listener : s_listeners)
|
||||
listener.Call(event);
|
||||
}
|
||||
}
|
||||
8
Runtime/Sources/Core/EventListener.cpp
git.filemode.normal_file
8
Runtime/Sources/Core/EventListener.cpp
git.filemode.normal_file
@@ -0,0 +1,8 @@
|
||||
#include <Core/EventListener.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
EventListener::EventListener(std::function<void(const EventBase&)> functor, std::string name)
|
||||
: m_listen_functor(std::move(functor)), m_name(std::move(name))
|
||||
{}
|
||||
}
|
||||
66
Runtime/Sources/Core/Logs.cpp
git.filemode.normal_file
66
Runtime/Sources/Core/Logs.cpp
git.filemode.normal_file
@@ -0,0 +1,66 @@
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <Core/Logs.h>
|
||||
#include <Utils/Ansi.h>
|
||||
#include <Core/EventBase.h>
|
||||
#include <Core/EventBus.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
struct FatalErrorEvent : public EventBase
|
||||
{
|
||||
Event What() const override { return Event::FatalErrorEventCode; }
|
||||
};
|
||||
}
|
||||
|
||||
void Logs::Report(LogType type, std::string message)
|
||||
{
|
||||
Report(type, 0, {}, {}, std::move(message));
|
||||
}
|
||||
|
||||
void Logs::Report(LogType type, unsigned int line, std::string_view file, std::string_view function, std::string message)
|
||||
{
|
||||
using namespace std::literals;
|
||||
|
||||
#ifndef VOX_DEBUG
|
||||
if(type == LogType::Debug && std::getenv("VOX_DEBUG_LOGS") == nullptr)
|
||||
return;
|
||||
#endif
|
||||
|
||||
std::string code_infos;
|
||||
if((type == LogType::Error || type == LogType::FatalError) && !file.empty() && !function.empty())
|
||||
{
|
||||
code_infos += "{in file '"s;
|
||||
code_infos += file;
|
||||
code_infos += "', line "s + std::to_string(line) + ", in function '"s;
|
||||
code_infos += function;
|
||||
code_infos += "'} "s;
|
||||
}
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case LogType::Debug: std::cout << Ansi::blue << "[Scop Debug] "; break;
|
||||
case LogType::Message: std::cout << Ansi::blue << "[Scop Message] "; break;
|
||||
case LogType::Warning: std::cout << Ansi::magenta << "[Scop Warning] "; break;
|
||||
case LogType::Error: std::cerr << Ansi::red << "[Scop Error] "; break;
|
||||
case LogType::FatalError: std::cerr << Ansi::red << "[Scop Fatal Error] "; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
std::time_t now = time(0);
|
||||
std::tm tstruct = *localtime(&now);
|
||||
char buffer[80];
|
||||
std::strftime(buffer, sizeof(buffer), "[%X] ", &tstruct);
|
||||
std::cout << Ansi::yellow << buffer << Ansi::def << code_infos << message << std::endl;
|
||||
|
||||
if(type == LogType::FatalError)
|
||||
{
|
||||
std::cout << Ansi::bg_red << "Fatal Error: emergency exit" << Ansi::bg_def << std::endl;
|
||||
EventBus::Send("__ScopEngine", Internal::FatalErrorEvent{});
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Runtime/Sources/Core/UUID.cpp
git.filemode.normal_file
21
Runtime/Sources/Core/UUID.cpp
git.filemode.normal_file
@@ -0,0 +1,21 @@
|
||||
#include <random>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <Core/UUID.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
static std::random_device random_device;
|
||||
static std::mt19937_64 engine(random_device());
|
||||
static std::uniform_int_distribution<std::uint64_t> uniform_distribution;
|
||||
static std::unordered_set<std::uint64_t> registry;
|
||||
|
||||
UUID::UUID()
|
||||
{
|
||||
do
|
||||
{
|
||||
m_uuid = uniform_distribution(engine);
|
||||
} while(registry.contains(m_uuid));
|
||||
registry.emplace(m_uuid);
|
||||
}
|
||||
}
|
||||
283
Runtime/Sources/Debug/ImGuiRenderer.cpp
git.filemode.normal_file
283
Runtime/Sources/Debug/ImGuiRenderer.cpp
git.filemode.normal_file
@@ -0,0 +1,283 @@
|
||||
#include <Debug/ImGuiRenderer.h>
|
||||
#include <Renderer/Buffer.h>
|
||||
#include <Renderer/Image.h>
|
||||
#include <Platform/Inputs.h>
|
||||
#include <Core/EventBus.h>
|
||||
|
||||
#undef DebugLog
|
||||
|
||||
#include <imgui.h>
|
||||
#include <backends/imgui_impl_sdl2.h>
|
||||
#include <backends/imgui_impl_vulkan.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
std::string HumanSize(uint64_t bytes)
|
||||
{
|
||||
std::string_view suffix[] = { "B", "KB", "MB", "GB", "TB" };
|
||||
std::size_t length = sizeof(suffix) / sizeof(suffix[0]);
|
||||
|
||||
int i = 0;
|
||||
double dbl_bytes = bytes;
|
||||
|
||||
if(bytes > 1024)
|
||||
{
|
||||
for(i = 0; (bytes / 1024) > 0 && i < length-1; i++, bytes /= 1024)
|
||||
dbl_bytes = bytes / 1024.0;
|
||||
}
|
||||
|
||||
std::string output(256, 0);
|
||||
std::sprintf(output.data(), "%.02lf %s", dbl_bytes, suffix[i].data());
|
||||
return output;
|
||||
}
|
||||
|
||||
ImGuiRenderer::ImGuiRenderer(NonOwningPtr<Renderer> renderer) : p_renderer(renderer)
|
||||
{}
|
||||
|
||||
void ImGuiRenderer::Init(Inputs& inputs)
|
||||
{
|
||||
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
|
||||
{
|
||||
if(event.What() == Event::ResizeEventCode)
|
||||
{
|
||||
kvfDestroyRenderPass(RenderCore::Get().GetDevice(), m_renderpass);
|
||||
std::vector<VkAttachmentDescription> attachments;
|
||||
const Image& image = p_renderer->GetSwapchain().GetSwapchainImages()[0];
|
||||
attachments.push_back(kvfBuildAttachmentDescription(KVF_IMAGE_COLOR, image.GetFormat(), image.GetLayout(), image.GetLayout(), false, VK_SAMPLE_COUNT_1_BIT));
|
||||
m_renderpass = kvfCreateRenderPass(RenderCore::Get().GetDevice(), attachments.data(), attachments.size(), VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
CreateFramebuffers();
|
||||
ImGui_ImplVulkan_SetMinImageCount(kvfGetSwapchainMinImagesCount(p_renderer->GetSwapchain().Get()));
|
||||
}
|
||||
};
|
||||
EventBus::RegisterListener({ functor, std::to_string((std::uintptr_t)(void**)this) });
|
||||
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGui::GetIO().IniFilename = nullptr;
|
||||
|
||||
SetTheme();
|
||||
|
||||
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 = (std::uint32_t)IM_ARRAYSIZE(pool_sizes);
|
||||
pool_info.pPoolSizes = pool_sizes;
|
||||
RenderCore::Get().vkCreateDescriptorPool(RenderCore::Get().GetDevice(), &pool_info, nullptr, &m_pool);
|
||||
|
||||
// Setup Platform/Renderer bindings
|
||||
ImGui_ImplVulkan_LoadFunctions([](const char* function_name, void* vulkan_instance) {
|
||||
return RenderCore::Get().vkGetInstanceProcAddr(*(reinterpret_cast<VkInstance*>(vulkan_instance)), function_name);
|
||||
}, &RenderCore::Get().GetInstanceRef());
|
||||
|
||||
std::vector<VkAttachmentDescription> attachments;
|
||||
const Image& image = p_renderer->GetSwapchain().GetSwapchainImages()[0];
|
||||
attachments.push_back(kvfBuildAttachmentDescription(KVF_IMAGE_COLOR, image.GetFormat(), image.GetLayout(), image.GetLayout(), false, VK_SAMPLE_COUNT_1_BIT));
|
||||
m_renderpass = kvfCreateRenderPass(RenderCore::Get().GetDevice(), attachments.data(), attachments.size(), VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
CreateFramebuffers();
|
||||
|
||||
ImGui_ImplSDL2_InitForVulkan(p_renderer->GetWindow()->GetSDLWindow());
|
||||
ImGui_ImplVulkan_InitInfo init_info{};
|
||||
init_info.Instance = RenderCore::Get().GetInstance();
|
||||
init_info.PhysicalDevice = RenderCore::Get().GetPhysicalDevice();
|
||||
init_info.Device = RenderCore::Get().GetDevice();
|
||||
init_info.QueueFamily = kvfGetDeviceQueueFamily(RenderCore::Get().GetDevice(), KVF_GRAPHICS_QUEUE);
|
||||
init_info.Queue = kvfGetDeviceQueue(RenderCore::Get().GetDevice(), KVF_GRAPHICS_QUEUE);
|
||||
init_info.DescriptorPool = m_pool;
|
||||
init_info.Allocator = nullptr;
|
||||
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
init_info.Subpass = 0;
|
||||
init_info.MinImageCount = kvfGetSwapchainMinImagesCount(p_renderer->GetSwapchain().Get());
|
||||
init_info.ImageCount = p_renderer->GetSwapchain().GetSwapchainImages().size();
|
||||
init_info.CheckVkResultFn = [](VkResult result){ kvfCheckVk(result); };
|
||||
init_info.RenderPass = m_renderpass;
|
||||
ImGui_ImplVulkan_Init(&init_info);
|
||||
|
||||
inputs.AddEventUpdateHook(ImGui_ImplSDL2_ProcessEvent);
|
||||
}
|
||||
|
||||
void ImGuiRenderer::Destroy()
|
||||
{
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
for(VkFramebuffer fb : m_framebuffers)
|
||||
kvfDestroyFramebuffer(RenderCore::Get().GetDevice(), fb);
|
||||
m_framebuffers.clear();
|
||||
ImGui_ImplVulkan_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
kvfDestroyRenderPass(RenderCore::Get().GetDevice(), m_renderpass);
|
||||
RenderCore::Get().vkDestroyDescriptorPool(RenderCore::Get().GetDevice(), m_pool, nullptr);
|
||||
}
|
||||
|
||||
bool ImGuiRenderer::BeginFrame()
|
||||
{
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGuiRenderer::DisplayRenderStatistics()
|
||||
{
|
||||
static std::array<std::string, 5> gpu_types_string = {
|
||||
"Other",
|
||||
"Integrated",
|
||||
"Graphics Card",
|
||||
"Virtual",
|
||||
"CPU"
|
||||
};
|
||||
static std::array<float, 1000> frame_histogram;
|
||||
|
||||
float delta = ImGui::GetIO().DeltaTime;
|
||||
ImGui::SetNextWindowPos(ImVec2{ 20.0f, 20.0f }, ImGuiCond_FirstUseEver);
|
||||
if(ImGui::Begin("Render Statistics", nullptr, ImGuiWindowFlags_NoResize))
|
||||
{
|
||||
VkPhysicalDeviceProperties props;
|
||||
RenderCore::Get().vkGetPhysicalDeviceProperties(RenderCore::Get().GetPhysicalDevice(), &props);
|
||||
ImGui::Text("GPU in use:\n%s", props.deviceName);
|
||||
ImGui::Text("GPU type: %s", gpu_types_string[static_cast<int>(props.deviceType)].c_str());
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Frame time %.3fms", delta);
|
||||
ImGui::PlotLines("##Frames_Histogram", frame_histogram.data(), frame_histogram.size(), 0, nullptr, 0.0f, 1.0f, ImVec2(ImGui::GetContentRegionAvail().x, 40.0f));
|
||||
ImGui::Text("FPS %.0f", ImGui::GetIO().Framerate);
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Swapchain images count %ld", p_renderer->GetSwapchain().GetSwapchainImages().size());
|
||||
ImGui::Text("Drawcalls %ld", p_renderer->GetDrawCallsCounterRef());
|
||||
ImGui::Text("Polygon drawn %ld", p_renderer->GetPolygonDrawnCounterRef());
|
||||
ImGui::Separator();
|
||||
ImGui::Text("VRAM usage %s", HumanSize(RenderCore::Get().GetAllocator().GetVramUsage()).c_str());
|
||||
ImGui::Text("Host visible usage %s", HumanSize(RenderCore::Get().GetAllocator().GetVramHostVisibleUsage()).c_str());
|
||||
ImGui::Text("Allocations count %ld / %u", RenderCore::Get().GetAllocator().GetAllocationsCount(), props.limits.maxMemoryAllocationCount);
|
||||
ImGui::Text("Buffer count %ld", GPUBuffer::GetBufferCount());
|
||||
ImGui::Text("Image count %ld", Image::GetImageCount());
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Window dimensions: %ux%u", p_renderer->GetWindow()->GetWidth(), p_renderer->GetWindow()->GetHeight());
|
||||
}
|
||||
ImGui::End();
|
||||
std::rotate(frame_histogram.begin(), frame_histogram.begin() + 1, frame_histogram.end());
|
||||
frame_histogram.back() = delta;
|
||||
}
|
||||
|
||||
void ImGuiRenderer::EndFrame()
|
||||
{
|
||||
VkFramebuffer fb = m_framebuffers[p_renderer->GetSwapchain().GetImageIndex()];
|
||||
ImGui::Render();
|
||||
ImDrawData* draw_data = ImGui::GetDrawData();
|
||||
if(draw_data->DisplaySize.x >= 0.0f && draw_data->DisplaySize.y >= 0.0f)
|
||||
{
|
||||
VkExtent2D fb_extent = kvfGetFramebufferSize(fb);
|
||||
kvfBeginRenderPass(m_renderpass, p_renderer->GetActiveCommandBuffer(), fb, fb_extent, nullptr, 0);
|
||||
ImGui_ImplVulkan_RenderDrawData(draw_data, p_renderer->GetActiveCommandBuffer());
|
||||
RenderCore::Get().vkCmdEndRenderPass(p_renderer->GetActiveCommandBuffer());
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiRenderer::CreateFramebuffers()
|
||||
{
|
||||
for(VkFramebuffer fb : m_framebuffers)
|
||||
kvfDestroyFramebuffer(RenderCore::Get().GetDevice(), fb);
|
||||
m_framebuffers.clear();
|
||||
std::vector<VkAttachmentDescription> attachments;
|
||||
std::vector<VkImageView> attachment_views;
|
||||
const Image& image = p_renderer->GetSwapchain().GetSwapchainImages()[0];
|
||||
attachments.push_back(kvfBuildAttachmentDescription((kvfIsDepthFormat(image.GetFormat()) ? KVF_IMAGE_DEPTH : KVF_IMAGE_COLOR), image.GetFormat(), image.GetLayout(), image.GetLayout(), false, VK_SAMPLE_COUNT_1_BIT));
|
||||
attachment_views.push_back(image.GetImageView());
|
||||
for(const Image& image : p_renderer->GetSwapchain().GetSwapchainImages())
|
||||
{
|
||||
attachment_views[0] = image.GetImageView();
|
||||
m_framebuffers.push_back(kvfCreateFramebuffer(RenderCore::Get().GetDevice(), m_renderpass, attachment_views.data(), attachment_views.size(), { .width = image.GetWidth(), .height = image.GetHeight() }));
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiRenderer::SetTheme()
|
||||
{
|
||||
ImGuiStyle* style = &ImGui::GetStyle();
|
||||
ImVec4* colors = style->Colors;
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||
colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
|
||||
colors[ImGuiCol_WindowBg] = ImVec4(0.10f, 0.10f, 0.10f, 0.50f);
|
||||
colors[ImGuiCol_ChildBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.20f);
|
||||
colors[ImGuiCol_PopupBg] = ImVec4(0.19f, 0.19f, 0.19f, 0.92f);
|
||||
colors[ImGuiCol_Border] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
|
||||
colors[ImGuiCol_BorderShadow] = ImVec4(0.05f, 0.05f, 0.05f, 0.24f);
|
||||
colors[ImGuiCol_FrameBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f);
|
||||
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.19f, 0.19f, 0.19f, 0.54f);
|
||||
colors[ImGuiCol_FrameBgActive] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f);
|
||||
colors[ImGuiCol_TitleBg] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
|
||||
colors[ImGuiCol_TitleBgActive] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
|
||||
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
|
||||
colors[ImGuiCol_MenuBarBg] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
|
||||
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f);
|
||||
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.34f, 0.34f, 0.34f, 0.54f);
|
||||
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.40f, 0.54f);
|
||||
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.54f);
|
||||
colors[ImGuiCol_CheckMark] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f);
|
||||
colors[ImGuiCol_SliderGrab] = ImVec4(0.34f, 0.34f, 0.34f, 0.54f);
|
||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.54f);
|
||||
colors[ImGuiCol_Button] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f);
|
||||
colors[ImGuiCol_ButtonHovered] = ImVec4(0.19f, 0.19f, 0.19f, 0.54f);
|
||||
colors[ImGuiCol_ButtonActive] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f);
|
||||
colors[ImGuiCol_Header] = ImVec4(0.05f, 0.05f, 0.05f, 0.52f);
|
||||
colors[ImGuiCol_HeaderHovered] = ImVec4(0.05f, 0.05f, 0.05f, 0.36f);
|
||||
colors[ImGuiCol_HeaderActive] = ImVec4(0.20f, 0.22f, 0.23f, 0.33f);
|
||||
colors[ImGuiCol_Separator] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
|
||||
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.29f);
|
||||
colors[ImGuiCol_SeparatorActive] = ImVec4(0.40f, 0.44f, 0.47f, 1.00f);
|
||||
colors[ImGuiCol_ResizeGrip] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f);
|
||||
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.29f);
|
||||
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.40f, 0.44f, 0.47f, 1.00f);
|
||||
colors[ImGuiCol_Tab] = ImVec4(0.05f, 0.05f, 0.05f, 0.52f);
|
||||
colors[ImGuiCol_TabHovered] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
|
||||
colors[ImGuiCol_TabActive] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f);
|
||||
colors[ImGuiCol_TabUnfocused] = ImVec4(0.05f, 0.05f, 0.05f, 0.52f);
|
||||
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f);
|
||||
colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogram] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.52f);
|
||||
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.05f, 0.05f, 0.05f, 0.52f);
|
||||
colors[ImGuiCol_TableBorderLight] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f);
|
||||
colors[ImGuiCol_TableRowBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.00f);
|
||||
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
|
||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f);
|
||||
colors[ImGuiCol_DragDropTarget] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f);
|
||||
colors[ImGuiCol_NavHighlight] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 0.00f, 0.00f, 0.70f);
|
||||
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
||||
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
||||
|
||||
style->ChildRounding = 4.0f;
|
||||
style->FrameBorderSize = 1.0f;
|
||||
style->FrameRounding = 4.0f;
|
||||
style->GrabMinSize = 7.0f;
|
||||
style->PopupRounding = 2.0f;
|
||||
style->ScrollbarRounding = 12.0f;
|
||||
style->ScrollbarSize = 13.0f;
|
||||
style->TabBorderSize = 0.0f;
|
||||
style->TabRounding = 5.0f;
|
||||
style->WindowRounding = 0.0f;
|
||||
style->WindowBorderSize = 1.0f;
|
||||
style->AntiAliasedLines = true;
|
||||
style->AntiAliasedFill = true;
|
||||
style->TabBorderSize = 2.0f;
|
||||
}
|
||||
}
|
||||
39
Runtime/Sources/Graphics/Actor.cpp
git.filemode.normal_file
39
Runtime/Sources/Graphics/Actor.cpp
git.filemode.normal_file
@@ -0,0 +1,39 @@
|
||||
#include <Graphics/Actor.h>
|
||||
#include <Platform/Inputs.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
Actor::Actor()
|
||||
{
|
||||
m_uuid = UUID();
|
||||
if(p_script)
|
||||
p_script->OnInit(this);
|
||||
}
|
||||
|
||||
Actor::Actor(Model model) : m_model(std::move(model))
|
||||
{
|
||||
m_uuid = UUID();
|
||||
if(p_script)
|
||||
p_script->OnInit(this);
|
||||
}
|
||||
|
||||
Actor::Actor(std::uint64_t uuid, Model model) : m_model(std::move(model)), m_uuid(uuid)
|
||||
{
|
||||
if(p_script)
|
||||
p_script->OnInit(this);
|
||||
}
|
||||
|
||||
void Actor::Update(NonOwningPtr<Scene> scene, Inputs& input, float delta)
|
||||
{
|
||||
if(p_script)
|
||||
p_script->OnUpdate(scene, this, input, delta);
|
||||
}
|
||||
|
||||
Actor::~Actor()
|
||||
{
|
||||
if(p_script)
|
||||
p_script->OnQuit(this);
|
||||
if(m_custom_pipeline.has_value() && m_custom_pipeline->data_uniform_buffer)
|
||||
m_custom_pipeline->data_uniform_buffer->Destroy();
|
||||
}
|
||||
}
|
||||
77
Runtime/Sources/Graphics/Cameras/FirstPerson3D.cpp
git.filemode.normal_file
77
Runtime/Sources/Graphics/Cameras/FirstPerson3D.cpp
git.filemode.normal_file
@@ -0,0 +1,77 @@
|
||||
#include <Graphics/Cameras/FirstPerson3D.h>
|
||||
#include <Platform/Inputs.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
FirstPerson3D::FirstPerson3D() : BaseCamera(), c_up(0, 1, 0), m_position(0.0, 0.0, 0.0)
|
||||
{}
|
||||
|
||||
FirstPerson3D::FirstPerson3D(Vec3f position, float fov, float speed) : BaseCamera(), m_position(std::move(position)), c_up(0, 1, 0), c_speed(speed), m_fov(fov)
|
||||
{}
|
||||
|
||||
void FirstPerson3D::Update(class Inputs& input, float aspect, float timestep)
|
||||
{
|
||||
UpdateView();
|
||||
m_target = m_position + m_direction;
|
||||
m_view = Mat4f::LookAt(m_position, m_target, c_up);
|
||||
m_proj = Mat4f::Perspective(RadianAnglef(m_fov), aspect, 0.1f, 100'000.f);
|
||||
|
||||
if(m_inputs_blocked)
|
||||
return;
|
||||
|
||||
if(input.IsMouseGrabbed())
|
||||
{
|
||||
m_theta -= input.GetXRel() * c_sensivity;
|
||||
m_phi -= input.GetYRel() * c_sensivity;
|
||||
}
|
||||
|
||||
if(input.IsKeyPressed(SDL_SCANCODE_ESCAPE))
|
||||
input.ReleaseMouse();
|
||||
else if(input.IsMouseButtonPressed(SDL_BUTTON_LEFT))
|
||||
input.GrabMouse();
|
||||
|
||||
m_mov = Vec3f(0.0);
|
||||
|
||||
if(input.IsKeyPressed(SDL_SCANCODE_W) || input.IsKeyPressed(SDL_SCANCODE_UP))
|
||||
m_mov -= m_forward;
|
||||
if(input.IsKeyPressed(SDL_SCANCODE_S) || input.IsKeyPressed(SDL_SCANCODE_DOWN))
|
||||
m_mov += m_forward;
|
||||
if(input.IsKeyPressed(SDL_SCANCODE_D) || input.IsKeyPressed(SDL_SCANCODE_RIGHT))
|
||||
m_mov -= m_left;
|
||||
if(input.IsKeyPressed(SDL_SCANCODE_A) || input.IsKeyPressed(SDL_SCANCODE_LEFT))
|
||||
m_mov += m_left;
|
||||
if(input.IsKeyPressed(SDL_SCANCODE_LSHIFT) || input.IsKeyPressed(SDL_SCANCODE_RSHIFT))
|
||||
m_mov -= c_up;
|
||||
if(input.IsKeyPressed(SDL_SCANCODE_SPACE))
|
||||
m_mov += c_up;
|
||||
|
||||
/*
|
||||
if(input.IsMouseWheelUp())
|
||||
m_speed_factor *= 1.5f;
|
||||
if(input.IsMouseWheelDown())
|
||||
m_speed_factor /= 1.5f;
|
||||
*/
|
||||
if(input.IsKeyPressed(SDL_SCANCODE_Q))
|
||||
m_speed_factor = 20.0f;
|
||||
else
|
||||
m_speed_factor = 1.0f;
|
||||
m_position += m_mov * c_speed * m_speed_factor * timestep;
|
||||
}
|
||||
|
||||
void FirstPerson3D::UpdateView()
|
||||
{
|
||||
m_phi = m_phi > 89 ? 89 : m_phi;
|
||||
m_phi = m_phi < -89 ? -89 : m_phi;
|
||||
|
||||
// Spherical coordinate system
|
||||
m_direction.x = std::cos(m_phi * Pi<float>() / 180) * std::cos(m_theta * Pi<float>() / 180);
|
||||
m_direction.y = std::sin(m_phi * Pi<float>() / 180);
|
||||
m_direction.z = std::cos(m_phi * Pi<float>() / 180) * std::sin(-m_theta * Pi<float>() / 180);
|
||||
|
||||
m_left = c_up.CrossProduct(m_direction);
|
||||
m_left.Normalize();
|
||||
|
||||
m_forward = c_up.CrossProduct(m_left);
|
||||
m_forward.Normalize();
|
||||
}
|
||||
}
|
||||
65
Runtime/Sources/Graphics/Font.cpp
git.filemode.normal_file
65
Runtime/Sources/Graphics/Font.cpp
git.filemode.normal_file
@@ -0,0 +1,65 @@
|
||||
#include <Graphics/Font.h>
|
||||
#include <Core/Logs.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#define STBRP_ASSERT(x) Scop::Assert(x, "internal stb assertion " #x)
|
||||
#define STB_RECT_PACK_IMPLEMENTATION
|
||||
#include <stb_rect_pack.h>
|
||||
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include <stb_truetype.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
void Font::BuildFont()
|
||||
{
|
||||
std::vector<std::uint8_t> file_bytes;
|
||||
if(std::holds_alternative<std::filesystem::path>(m_build_data))
|
||||
{
|
||||
std::ifstream file(std::get<std::filesystem::path>(m_build_data), std::ios::binary);
|
||||
if(!file.is_open())
|
||||
{
|
||||
Error("Font: cannot open font file, %", m_name);
|
||||
return;
|
||||
}
|
||||
std::ifstream::pos_type file_size = std::filesystem::file_size(std::get<std::filesystem::path>(m_build_data));
|
||||
file.seekg(0, std::ios::beg);
|
||||
file_bytes.resize(file_size);
|
||||
file.read(reinterpret_cast<char*>(file_bytes.data()), file_size);
|
||||
file.close();
|
||||
}
|
||||
|
||||
CPUBuffer bitmap(RANGE * RANGE);
|
||||
|
||||
stbtt_pack_context pc;
|
||||
stbtt_PackBegin(&pc, bitmap.GetData(), RANGE, RANGE, RANGE, 1, nullptr);
|
||||
if(std::holds_alternative<std::filesystem::path>(m_build_data))
|
||||
stbtt_PackFontRange(&pc, file_bytes.data(), 0, m_scale, 32, 96, m_cdata.data());
|
||||
else
|
||||
stbtt_PackFontRange(&pc, std::get<std::vector<std::uint8_t>>(m_build_data).data(), 0, m_scale, 32, 96, m_cdata.data());
|
||||
stbtt_PackEnd(&pc);
|
||||
|
||||
// TODO : find better solution; No, using VK_FORMAT_R8_SRGB does not work
|
||||
CPUBuffer vulkan_bitmap(RANGE * RANGE * 4);
|
||||
for(int i = 0, j = 0; i < RANGE * RANGE; i++, j += 4)
|
||||
{
|
||||
vulkan_bitmap.GetData()[j + 0] = bitmap.GetData()[i];
|
||||
vulkan_bitmap.GetData()[j + 1] = bitmap.GetData()[i];
|
||||
vulkan_bitmap.GetData()[j + 2] = bitmap.GetData()[i];
|
||||
vulkan_bitmap.GetData()[j + 3] = bitmap.GetData()[i];
|
||||
}
|
||||
|
||||
m_atlas.Init(std::move(vulkan_bitmap), RANGE, RANGE, VK_FORMAT_R8G8B8A8_SRGB, false, m_name + "_font_atlas_" + std::to_string(m_scale));
|
||||
|
||||
Message("Font: loaded % with a scale of %", m_name, m_scale);
|
||||
}
|
||||
|
||||
void Font::Destroy()
|
||||
{
|
||||
if(!m_atlas.IsInit())
|
||||
return;
|
||||
m_atlas.Destroy();
|
||||
Message("Font: unloaded % with a scale of %", m_name, m_scale);
|
||||
}
|
||||
}
|
||||
45
Runtime/Sources/Graphics/Loaders/BMP.cpp
git.filemode.normal_file
45
Runtime/Sources/Graphics/Loaders/BMP.cpp
git.filemode.normal_file
@@ -0,0 +1,45 @@
|
||||
#include <Graphics/Loaders/BMP.h>
|
||||
#include <Maths/Vec3.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
CPUBuffer LoadBMPFile(const std::filesystem::path& path, Vec2ui32& dimensions)
|
||||
{
|
||||
if(path.extension() != ".bmp")
|
||||
{
|
||||
Error("BMP loader: not a BMP file, %", path);
|
||||
return {};
|
||||
}
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
if(!file.is_open())
|
||||
{
|
||||
Error("BMP loader: could not open %", path);
|
||||
return {};
|
||||
}
|
||||
std::int16_t bpp;
|
||||
file.seekg(18, std::ios_base::beg);
|
||||
file.read(reinterpret_cast<char*>(&dimensions.x), sizeof(dimensions.x));
|
||||
file.read(reinterpret_cast<char*>(&dimensions.y), sizeof(dimensions.y));
|
||||
file.seekg(28, std::ios_base::beg);
|
||||
file.read(reinterpret_cast<char*>(&bpp), sizeof(bpp));
|
||||
file.seekg(54, std::ios_base::beg);
|
||||
if(bpp != 24)
|
||||
Warning("BMP loader: warning while loading %, cannot handle yet different color palette sizes", path);
|
||||
CPUBuffer buffer{ dimensions.x * dimensions.y * 4 };
|
||||
for(std::size_t i = 0; i < buffer.GetSize(); i += 4)
|
||||
{
|
||||
Vec3b data{ 0, 0, 0 };
|
||||
data.x = (file.eof() ? 0x00 : file.get());
|
||||
data.y = (file.eof() ? 0x00 : file.get());
|
||||
data.z = (file.eof() ? 0x00 : file.get());
|
||||
buffer.GetData()[i + 0] = data.z;
|
||||
buffer.GetData()[i + 1] = data.y;
|
||||
buffer.GetData()[i + 2] = data.x;
|
||||
buffer.GetData()[i + 3] = 0xFF;
|
||||
}
|
||||
Message("BMP Loader: loaded %", path);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
171
Runtime/Sources/Graphics/Loaders/OBJ.cpp
git.filemode.normal_file
171
Runtime/Sources/Graphics/Loaders/OBJ.cpp
git.filemode.normal_file
@@ -0,0 +1,171 @@
|
||||
#include <Graphics/Loaders/OBJ.h>
|
||||
#include <Core/Logs.h>
|
||||
|
||||
#include <set>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
std::optional<ObjData> LoadObjFromFile(const std::filesystem::path& path)
|
||||
{
|
||||
if(!std::filesystem::exists(path))
|
||||
{
|
||||
Error("OBJ loader: OBJ file does not exists; %", path);
|
||||
return std::nullopt;
|
||||
}
|
||||
char line[1024];
|
||||
std::string op;
|
||||
std::istringstream line_in;
|
||||
std::set<std::string> groups;
|
||||
groups.insert("default");
|
||||
|
||||
std::ifstream in(path);
|
||||
|
||||
ObjData data;
|
||||
|
||||
while(in.good())
|
||||
{
|
||||
in.getline(line, 1023);
|
||||
line_in.clear();
|
||||
line_in.str(line);
|
||||
|
||||
if(!(line_in >> op))
|
||||
continue;
|
||||
|
||||
if(op == "v")
|
||||
{
|
||||
Vec3f v;
|
||||
line_in >> v.x;
|
||||
line_in >> v.y;
|
||||
line_in >> v.z;
|
||||
data.vertex.push_back(std::move(v));
|
||||
}
|
||||
else if(op == "vt")
|
||||
{
|
||||
Vec2f v;
|
||||
line_in >> v.x;
|
||||
line_in >> v.y;
|
||||
data.tex_coord.push_back(std::move(v));
|
||||
}
|
||||
else if(op == "vn")
|
||||
{
|
||||
Vec3f v;
|
||||
line_in >> v.x;
|
||||
line_in >> v.y;
|
||||
line_in >> v.z;
|
||||
data.normal.push_back(std::move(v));
|
||||
}
|
||||
else if(op == "vc")
|
||||
{
|
||||
Vec4f v;
|
||||
line_in >> v.x;
|
||||
line_in >> v.y;
|
||||
line_in >> v.z;
|
||||
line_in >> v.w;
|
||||
data.color.push_back(std::move(v));
|
||||
}
|
||||
else if(op == "g")
|
||||
{
|
||||
groups.clear();
|
||||
while(line_in >> groups);
|
||||
groups.insert("default");
|
||||
}
|
||||
else if(op == "f")
|
||||
{
|
||||
std::vector<ObjData::FaceVertex> list;
|
||||
while(line_in >> list);
|
||||
for(const auto& group : groups)
|
||||
{
|
||||
ObjData::FaceList& fl = data.faces[group];
|
||||
fl.second.push_back(fl.first.size());
|
||||
fl.first.insert(fl.first.end(), list.begin(), list.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
for(auto& [_, face] : data.faces)
|
||||
{
|
||||
ObjData::FaceList& fl = face;
|
||||
fl.second.push_back(fl.first.size());
|
||||
}
|
||||
Message("OBJ Loader: loaded %", path);
|
||||
return data;
|
||||
}
|
||||
|
||||
static void TesselateObjData(std::vector<ObjData::FaceVertex>& input, std::vector<std::uint32_t>& input_start) noexcept
|
||||
{
|
||||
std::vector<ObjData::FaceVertex> output;
|
||||
std::vector<std::uint32_t> output_start;
|
||||
output.reserve(input.size());
|
||||
output_start.reserve(input_start.size());
|
||||
for(auto s = input_start.begin(); s != input_start.end() - 1; ++s)
|
||||
{
|
||||
const std::uint32_t size = *(s + 1) - *s;
|
||||
if(size > 3)
|
||||
{
|
||||
const ObjData::FaceVertex & start_vertex = input[*s];
|
||||
for(std::size_t i = 1; i < size - 1; i++)
|
||||
{
|
||||
output_start.push_back(output.size());
|
||||
output.push_back(start_vertex);
|
||||
output.push_back(input[*s + i]);
|
||||
output.push_back(input[*s + i + 1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
output_start.push_back(output.size());
|
||||
output.insert(output.end(), input.begin() + *s, input.begin() + *(s + 1));
|
||||
}
|
||||
}
|
||||
output_start.push_back(output.size());
|
||||
input.swap(output);
|
||||
input_start.swap(output_start);
|
||||
}
|
||||
|
||||
void TesselateObjData(ObjData& obj)
|
||||
{
|
||||
for(auto& face : obj.faces)
|
||||
{
|
||||
ObjData::FaceList& fl = face.second;
|
||||
TesselateObjData(fl.first, fl.second);
|
||||
}
|
||||
Message("OBJ Loader : object data tesselated");
|
||||
}
|
||||
|
||||
ObjModel ConvertObjDataToObjModel(const ObjData& data)
|
||||
{
|
||||
std::vector<ObjData::FaceVertex> unique(data.faces.find("default")->second.first);
|
||||
std::sort(unique.begin(), unique.end());
|
||||
//unique.erase(std::unique(unique.begin(), unique.end()), unique.end());
|
||||
|
||||
ObjModel model;
|
||||
for(auto& face : unique)
|
||||
{
|
||||
model.vertex.push_back(data.vertex[face.v]);
|
||||
if(!data.tex_coord.empty())
|
||||
{
|
||||
const int index = (face.t > -1) ? face.t : face.v;
|
||||
model.tex_coord.push_back(data.tex_coord[index]);
|
||||
}
|
||||
if(!data.normal.empty())
|
||||
{
|
||||
const int index = (face.n > -1) ? face.n : face.v;
|
||||
model.normal.push_back(data.normal[index]);
|
||||
}
|
||||
if(!data.color.empty())
|
||||
{
|
||||
const int index = face.v;
|
||||
model.color.push_back(data.color[index]);
|
||||
}
|
||||
}
|
||||
for(auto& [group, faces] : data.faces)
|
||||
{
|
||||
std::vector<std::uint32_t>& v = model.faces[group];
|
||||
v.reserve(faces.first.size());
|
||||
for(auto& face : faces.first)
|
||||
v.push_back(std::distance(unique.begin(), std::lower_bound(unique.begin(), unique.end(), face)));
|
||||
}
|
||||
return model;
|
||||
}
|
||||
}
|
||||
28
Runtime/Sources/Graphics/Mesh.cpp
git.filemode.normal_file
28
Runtime/Sources/Graphics/Mesh.cpp
git.filemode.normal_file
@@ -0,0 +1,28 @@
|
||||
#include <Graphics/Mesh.h>
|
||||
#include <Utils/Buffer.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
void Mesh::Draw(VkCommandBuffer cmd, std::size_t& drawcalls, std::size_t& polygondrawn) const noexcept
|
||||
{
|
||||
for(std::size_t i = 0; i < m_sub_meshes.size(); i++)
|
||||
Draw(cmd, drawcalls, polygondrawn, i);
|
||||
}
|
||||
|
||||
void Mesh::Draw(VkCommandBuffer cmd, std::size_t& drawcalls, std::size_t& polygondrawn, std::size_t submesh_index) const noexcept
|
||||
{
|
||||
Verify(submesh_index < m_sub_meshes.size(), "invalid submesh index");
|
||||
m_sub_meshes[submesh_index].buffer.BindVertex(cmd);
|
||||
m_sub_meshes[submesh_index].buffer.BindIndex(cmd);
|
||||
RenderCore::Get().vkCmdDrawIndexed(cmd, m_sub_meshes[submesh_index].index_size, 1, 0, 0, 0);
|
||||
polygondrawn += m_sub_meshes[submesh_index].triangle_count;
|
||||
drawcalls++;
|
||||
}
|
||||
|
||||
Mesh::~Mesh()
|
||||
{
|
||||
for(auto& mesh : m_sub_meshes)
|
||||
mesh.buffer.Destroy();
|
||||
}
|
||||
}
|
||||
565
Runtime/Sources/Graphics/MeshFactory.cpp
git.filemode.normal_file
565
Runtime/Sources/Graphics/MeshFactory.cpp
git.filemode.normal_file
@@ -0,0 +1,565 @@
|
||||
#include <Graphics/MeshFactory.h>
|
||||
#include <Graphics/Mesh.h>
|
||||
#include <Renderer/Vertex.h>
|
||||
#include <Maths/Constants.h>
|
||||
#include <Maths/Quaternions.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
std::shared_ptr<Mesh> CreateQuad(float x, float y, float width, float height)
|
||||
{
|
||||
std::vector<Vertex> data(4);
|
||||
|
||||
data[0].position = Vec4f(x, y, 0.0f, 1.0f);
|
||||
data[0].uv = Vec2f(1.0f, 1.0f);
|
||||
|
||||
data[1].position = Vec4f(x + width, y, 0.0f, 1.0f);
|
||||
data[1].uv = Vec2f(0.0f, 1.0f);
|
||||
|
||||
data[2].position = Vec4f(x + width, y + height, 0.0f, 1.0f);
|
||||
data[2].uv = Vec2f(0.0f, 0.0f);
|
||||
|
||||
data[3].position = Vec4f(x, y + height, 0.0f, 1.0f);
|
||||
data[3].uv = Vec2f(1.0f, 0.0f);
|
||||
|
||||
std::vector<std::uint32_t> indices = {
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
3,
|
||||
0,
|
||||
};
|
||||
|
||||
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
|
||||
mesh->AddSubMesh({ std::move(data), std::move(indices) });
|
||||
return mesh;
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> CreateQuad(const Vec2f& position, const Vec2f& size)
|
||||
{
|
||||
return CreateQuad(position.x, position.y, size.x, size.y);
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> CreateQuad()
|
||||
{
|
||||
std::vector<Vertex> data(4);
|
||||
|
||||
data[0].position = Vec4f(-1.0f, -1.0f, 0.0f, 1.0f);
|
||||
data[0].uv = Vec2f(0.0f, 0.0f);
|
||||
data[0].color = Vec4f(1.0f);
|
||||
|
||||
data[1].position = Vec4f(1.0f, -1.0f, 0.0f, 1.0f);
|
||||
data[1].color = Vec4f(1.0f);
|
||||
data[1].uv = Vec2f(1.0f, 0.0f);
|
||||
|
||||
data[2].position = Vec4f(1.0f, 1.0f, 0.0f, 1.0f);
|
||||
data[2].color = Vec4f(1.0f);
|
||||
data[2].uv = Vec2f(1.0f, 1.0f);
|
||||
|
||||
data[3].position = Vec4f(-1.0f, 1.0f, 0.0f, 1.0f);
|
||||
data[3].color = Vec4f(1.0f);
|
||||
data[3].uv = Vec2f(0.0f, 1.0f);
|
||||
|
||||
std::vector<std::uint32_t> indices = {
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
3,
|
||||
0,
|
||||
};
|
||||
|
||||
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
|
||||
mesh->AddSubMesh({ std::move(data), std::move(indices) });
|
||||
return mesh;
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> CreateCube()
|
||||
{
|
||||
// v6----- v5
|
||||
// /| /|
|
||||
// v1------v0|
|
||||
// | | | |
|
||||
// | |v7---|-|v4
|
||||
// |/ |/
|
||||
// v2------v3
|
||||
std::vector<Vertex> data(24);
|
||||
|
||||
data[0].position = Vec4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
data[0].color = Vec4f(1.0f);
|
||||
data[0].normal = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
|
||||
data[1].position = Vec4f(-1.0f, 1.0f, 1.0f, 1.0f);
|
||||
data[1].color = Vec4f(1.0f);
|
||||
data[1].normal = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
|
||||
data[2].position = Vec4f(-1.0f, -1.0f, 1.0f, 1.0f);
|
||||
data[2].color = Vec4f(1.0f);
|
||||
data[2].normal = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
|
||||
data[3].position = Vec4f(1.0f, -1.0f, 1.0f, 1.0f);
|
||||
data[3].color = Vec4f(1.0f);
|
||||
data[3].normal = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
|
||||
data[4].position = Vec4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
data[4].color = Vec4f(1.0f);
|
||||
data[4].normal = Vec4f(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
data[5].position = Vec4f(1.0f, -1.0f, 1.0f, 1.0f);
|
||||
data[5].color = Vec4f(1.0f);
|
||||
data[5].normal = Vec4f(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
data[6].position = Vec4f(1.0f, -1.0f, -1.0f, 1.0f);
|
||||
data[6].color = Vec4f(1.0f);
|
||||
data[6].normal = Vec4f(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
data[7].position = Vec4f(1.0f, 1.0f, -1.0f, 1.0f);
|
||||
data[7].color = Vec4f(1.0f);
|
||||
data[7].normal = Vec4f(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
data[8].position = Vec4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
data[8].color = Vec4f(1.0f);
|
||||
data[8].normal = Vec4f(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
|
||||
data[9].position = Vec4f(1.0f, 1.0f, -1.0f, 1.0f);
|
||||
data[9].color = Vec4f(1.0f);
|
||||
data[9].normal = Vec4f(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
|
||||
data[10].position = Vec4f(-1.0f, 1.0f, -1.0f, 1.0f);
|
||||
data[10].color = Vec4f(1.0f);
|
||||
data[10].normal = Vec4f(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
|
||||
data[11].position = Vec4f(-1.0f, 1.0f, 1.0f, 1.0f);
|
||||
data[11].color = Vec4f(1.0f);
|
||||
data[11].normal = Vec4f(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
|
||||
data[12].position = Vec4f(-1.0f, 1.0f, 1.0f, 1.0f);
|
||||
data[12].color = Vec4f(1.0f);
|
||||
data[12].normal = Vec4f(-1.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
data[13].position = Vec4f(-1.0f, 1.0f, -1.0f, 1.0f);
|
||||
data[13].color = Vec4f(1.0f);
|
||||
data[13].normal = Vec4f(-1.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
data[14].position = Vec4f(-1.0f, -1.0f, -1.0f, 1.0f);
|
||||
data[14].color = Vec4f(1.0f);
|
||||
data[14].normal = Vec4f(-1.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
data[15].position = Vec4f(-1.0f, -1.0f, 1.0f, 1.0f);
|
||||
data[15].color = Vec4f(1.0f);
|
||||
data[15].normal = Vec4f(-1.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
data[16].position = Vec4f(-1.0f, -1.0f, -1.0f, 1.0f);
|
||||
data[16].color = Vec4f(1.0f);
|
||||
data[16].normal = Vec4f(0.0f, -1.0f, 0.0f, 1.0f);
|
||||
|
||||
data[17].position = Vec4f(1.0f, -1.0f, -1.0f, 1.0f);
|
||||
data[17].color = Vec4f(1.0f);
|
||||
data[17].normal = Vec4f(0.0f, -1.0f, 0.0f, 1.0f);
|
||||
|
||||
data[18].position = Vec4f(1.0f, -1.0f, 1.0f, 1.0f);
|
||||
data[18].color = Vec4f(1.0f);
|
||||
data[18].normal = Vec4f(0.0f, -1.0f, 0.0f, 1.0f);
|
||||
|
||||
data[19].position = Vec4f(-1.0f, -1.0f, 1.0f, 1.0f);
|
||||
data[19].color = Vec4f(1.0f);
|
||||
data[19].normal = Vec4f(0.0f, -1.0f, 0.0f, 1.0f);
|
||||
|
||||
data[20].position = Vec4f(1.0f, -1.0f, -1.0f, 1.0f);
|
||||
data[20].color = Vec4f(1.0f);
|
||||
data[20].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
|
||||
|
||||
data[21].position = Vec4f(-1.0f, -1.0f, -1.0f, 1.0f);
|
||||
data[21].color = Vec4f(1.0f);
|
||||
data[21].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
|
||||
|
||||
data[22].position = Vec4f(-1.0f, 1.0f, -1.0f, 1.0f);
|
||||
data[22].color = Vec4f(1.0f);
|
||||
data[22].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
|
||||
|
||||
data[23].position = Vec4f(1.0f, 1.0f, -1.0f, 1.0f);
|
||||
data[23].color = Vec4f(1.0f);
|
||||
data[23].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
|
||||
|
||||
for(int i = 0; i < 6; i++)
|
||||
{
|
||||
data[i * 4 + 0].uv = Vec2f(0.0f, 0.0f);
|
||||
data[i * 4 + 1].uv = Vec2f(1.0f, 0.0f);
|
||||
data[i * 4 + 2].uv = Vec2f(1.0f, 1.0f);
|
||||
data[i * 4 + 3].uv = Vec2f(0.0f, 1.0f);
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t> indices = {
|
||||
0, 1, 2, 0, 2, 3, // Right
|
||||
4, 5, 6, 4, 6, 7, // Front
|
||||
8, 9, 10, 8, 10, 11, // Top
|
||||
12, 13, 14, 12, 14, 15, // Back
|
||||
16, 17, 18, 16, 18, 19, // Bottom
|
||||
20, 21, 22, 20, 22, 23 // Left
|
||||
};
|
||||
|
||||
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
|
||||
mesh->AddSubMesh({ std::move(data), std::move(indices) });
|
||||
return mesh;
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> CreatePyramid()
|
||||
{
|
||||
std::vector<Vertex> data(18);
|
||||
|
||||
data[0].position = Vec4f(1.0f, 1.0f, -1.0f, 1.0f);
|
||||
data[0].color = Vec4f(1.0f);
|
||||
data[0].uv = Vec2f(0.24f, 0.20f);
|
||||
data[0].normal = Vec4f(0.0f, 0.8948f, 0.4464f, 1.0f);
|
||||
|
||||
data[1].position = Vec4f(-1.0f, 1.0f, -1.0f, 1.0f);
|
||||
data[1].color = Vec4f(1.0f);
|
||||
data[1].uv = Vec2f(0.24f, 0.81f);
|
||||
data[1].normal = Vec4f(0.0f, 0.8948f, 0.4464f, 1.0f);
|
||||
|
||||
data[2].position = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
data[2].color = Vec4f(1.0f);
|
||||
data[2].uv = Vec2f(0.95f, 0.50f);
|
||||
data[2].normal = Vec4f(0.0f, 0.8948f, 0.4464f, 1.0f);
|
||||
|
||||
data[3].position = Vec4f(-1.0f, 1.0f, -1.0f, 1.0f);
|
||||
data[3].color = Vec4f(1.0f);
|
||||
data[3].uv = Vec2f(0.24f, 0.21f);
|
||||
data[3].normal = Vec4f(-0.8948f, 0.0f, 0.4464f, 1.0f);
|
||||
|
||||
data[4].position = Vec4f(-1.0f, -1.0f, -1.0f, 1.0f);
|
||||
data[4].color = Vec4f(1.0f);
|
||||
data[4].uv = Vec2f(0.24f, 0.81f);
|
||||
data[4].normal = Vec4f(-0.8948f, 0.0f, 0.4464f, 1.0f);
|
||||
|
||||
data[5].position = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
data[5].color = Vec4f(1.0f);
|
||||
data[5].uv = Vec2f(0.95f, 0.50f);
|
||||
data[5].normal = Vec4f(-0.8948f, 0.0f, 0.4464f, 1.0f);
|
||||
|
||||
data[6].position = Vec4f(1.0f, 1.0f, -1.0f, 1.0f);
|
||||
data[6].color = Vec4f(1.0f);
|
||||
data[6].uv = Vec2f(0.24f, 0.81f);
|
||||
data[6].normal = Vec4f(0.8948f, 0.0f, 0.4475f, 1.0f);
|
||||
|
||||
data[7].position = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
data[7].color = Vec4f(1.0f);
|
||||
data[7].uv = Vec2f(0.95f, 0.50f);
|
||||
data[7].normal = Vec4f(0.8948f, 0.0f, 0.4475f, 1.0f);
|
||||
|
||||
data[8].position = Vec4f(1.0f, -1.0f, -1.0f, 1.0f);
|
||||
data[8].color = Vec4f(1.0f);
|
||||
data[8].uv = Vec2f(0.24f, 0.21f);
|
||||
data[8].normal = Vec4f(0.8948f, 0.0f, 0.4475f, 1.0f);
|
||||
|
||||
data[9].position = Vec4f(-1.0f, -1.0f, -1.0f, 1.0f);
|
||||
data[9].color = Vec4f(1.0f);
|
||||
data[9].uv = Vec2f(0.24f, 0.21f);
|
||||
data[9].normal = Vec4f(0.0f, -0.8948f, 0.448f, 1.0f);
|
||||
|
||||
data[10].position = Vec4f(1.0f, -1.0f, -1.0f, 1.0f);
|
||||
data[10].color = Vec4f(1.0f);
|
||||
data[10].uv = Vec2f(0.24f, 0.81f);
|
||||
data[10].normal = Vec4f(0.0f, -0.8948f, 0.448f, 1.0f);
|
||||
|
||||
data[11].position = Vec4f(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
data[11].color = Vec4f(1.0f);
|
||||
data[11].uv = Vec2f(0.95f, 0.50f);
|
||||
data[11].normal = Vec4f(0.0f, -0.8948f, 0.448f, 1.0f);
|
||||
|
||||
data[12].position = Vec4f(-1.0f, 1.0f, -1.0f, 1.0f);
|
||||
data[12].color = Vec4f(1.0f);
|
||||
data[12].uv = Vec2f(0.0f, 0.0f);
|
||||
data[12].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
|
||||
|
||||
data[13].position = Vec4f(1.0f, 1.0f, -1.0f, 1.0f);
|
||||
data[13].color = Vec4f(1.0f);
|
||||
data[13].uv = Vec2f(0.0f, 1.0f);
|
||||
data[13].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
|
||||
|
||||
data[14].position = Vec4f(1.0f, -1.0f, -1.0f, 1.0f);
|
||||
data[14].color = Vec4f(1.0f);
|
||||
data[14].uv = Vec2f(1.0f, 1.0f);
|
||||
data[14].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
|
||||
|
||||
data[15].position = Vec4f(-1.0f, -1.0f, -1.0f, 1.0f);
|
||||
data[15].color = Vec4f(1.0f);
|
||||
data[15].uv = Vec2f(0.96f, 0.0f);
|
||||
data[15].normal = Vec4f(0.0f, 0.0f, -1.0f, 1.0f);
|
||||
|
||||
data[16].position = Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
data[16].color = Vec4f(1.0f);
|
||||
data[16].uv = Vec2f(0.0f, 0.0f);
|
||||
data[16].normal = Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
data[17].position = Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
data[17].color = Vec4f(1.0f);
|
||||
data[17].uv = Vec2f(0.0f, 0.0f);
|
||||
data[17].normal = Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
std::vector<std::uint32_t> indices = {
|
||||
0, 1, 2,
|
||||
3, 4, 5,
|
||||
6, 7, 8,
|
||||
9, 10, 11,
|
||||
12, 13, 14,
|
||||
15, 12, 14
|
||||
};
|
||||
|
||||
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
|
||||
mesh->AddSubMesh({ std::move(data), std::move(indices) });
|
||||
return mesh;
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> CreateSphere(std::uint32_t x_segments, std::uint32_t y_segments)
|
||||
{
|
||||
std::vector<Vertex> data;
|
||||
|
||||
float sector_count = static_cast<float>(x_segments);
|
||||
float stack_count = static_cast<float>(y_segments);
|
||||
float sector_step = 2 * Pi<float>() / sector_count;
|
||||
float stack_step = Pi<float>() / stack_count;
|
||||
float radius = 1.0f;
|
||||
|
||||
for(int i = 0; i <= stack_count; ++i)
|
||||
{
|
||||
float stack_angle = Pi<float>() / 2 - i * stack_step;
|
||||
float xy = radius * std::cos(stack_angle);
|
||||
float z = radius * std::sin(stack_angle);
|
||||
|
||||
for(int j = 0; j <= sector_count; ++j)
|
||||
{
|
||||
float sector_angle = j * sector_step;
|
||||
|
||||
float x = xy * cosf(sector_angle);
|
||||
float y = xy * sinf(sector_angle);
|
||||
|
||||
float s = static_cast<float>(j / sector_count);
|
||||
float t = static_cast<float>(i / stack_count);
|
||||
|
||||
Vertex vertex;
|
||||
vertex.position = Vec4f(x, y, z, 1.0f);
|
||||
vertex.uv = Vec2f(s, t);
|
||||
vertex.normal = vertex.position.Normalize();
|
||||
|
||||
data.emplace_back(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t> indices;
|
||||
std::uint32_t k1, k2;
|
||||
for(uint32_t i = 0; i < stack_count; ++i)
|
||||
{
|
||||
k1 = i * (static_cast<std::uint32_t>(sector_count) + 1U);
|
||||
k2 = k1 + static_cast<std::uint32_t>(sector_count) + 1U;
|
||||
|
||||
for(std::uint32_t j = 0; j < sector_count; ++j, ++k1, ++k2)
|
||||
{
|
||||
if(i != 0)
|
||||
{
|
||||
indices.push_back(k1);
|
||||
indices.push_back(k2);
|
||||
indices.push_back(k1 + 1);
|
||||
}
|
||||
|
||||
if(i != (stack_count - 1))
|
||||
{
|
||||
indices.push_back(k1 + 1);
|
||||
indices.push_back(k2);
|
||||
indices.push_back(k2 + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
|
||||
mesh->AddSubMesh({ std::move(data), std::move(indices) });
|
||||
return mesh;
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> CreatePlane(float width, float height, const Vec3f& normal)
|
||||
{
|
||||
Vec3 vec = normal * 90.0f;
|
||||
|
||||
std::vector<Vertex> data(4);
|
||||
|
||||
data[0].position = Vec4f(-width * 0.5f, -1.0f, -height * 0.5f);
|
||||
data[0].normal = normal;
|
||||
data[0].uv = Vec2f(0.0f, 0.0f);
|
||||
|
||||
data[1].position = Vec4f(-width * 0.5f, -1.0f, height * 0.5f);
|
||||
data[1].normal = normal;
|
||||
data[1].uv = Vec2f(0.0f, 1.0f);
|
||||
|
||||
data[2].position = Vec4f(width * 0.5f, 1.0f, height * 0.5f);
|
||||
data[2].normal = normal;
|
||||
data[2].uv = Vec2f(1.0f, 1.0f);
|
||||
|
||||
data[3].position = Vec4f(width * 0.5f, 1.0f, -height * 0.5f);
|
||||
data[3].normal = normal;
|
||||
data[3].uv = Vec2f(1.0f, 0.0f);
|
||||
|
||||
std::vector<std::uint32_t> indices = { 0, 1, 2, 2, 3, 0 };
|
||||
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
|
||||
mesh->AddSubMesh({ std::move(data), std::move(indices) });
|
||||
return mesh;
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> CreateCapsule(float radius, float mid_height, int radial_segments, int rings)
|
||||
{
|
||||
int i, j, prevrow, thisrow, point;
|
||||
float x, y, z, u, v, w;
|
||||
float onethird = 1.0f / 3.0f;
|
||||
float twothirds = 2.0f / 3.0f;
|
||||
|
||||
std::vector<Vertex> data;
|
||||
std::vector<std::uint32_t> indices;
|
||||
|
||||
point = 0;
|
||||
|
||||
/* top hemisphere */
|
||||
thisrow = 0;
|
||||
prevrow = 0;
|
||||
for(j = 0; j <= (rings + 1); j++)
|
||||
{
|
||||
v = float(j);
|
||||
|
||||
v /= (rings + 1);
|
||||
w = std::sin(0.5f * Pi<float>() * v);
|
||||
y = radius * std::cos(0.5f * Pi<float>() * v);
|
||||
|
||||
for(i = 0; i <= radial_segments; i++)
|
||||
{
|
||||
u = float(i);
|
||||
u /= radial_segments;
|
||||
|
||||
x = std::sin(u * (Pi<float>() * 2.0f));
|
||||
z = std::cos(u * (Pi<float>() * 2.0f));
|
||||
|
||||
Vec3 p = Vec3f(x * radius * w, y, z * radius * w);
|
||||
|
||||
Vertex vertex;
|
||||
vertex.position = Vec4f(p + Vec3f(0.0f, 0.5f * mid_height, 0.0f), 1.0f);
|
||||
vertex.normal = p.Normalize();
|
||||
vertex.uv = Vec2f(u, onethird * v);
|
||||
data.emplace_back(vertex);
|
||||
point++;
|
||||
|
||||
if(i > 0 && j > 0)
|
||||
{
|
||||
indices.push_back(thisrow + i - 1);
|
||||
indices.push_back(prevrow + i);
|
||||
indices.push_back(prevrow + i - 1);
|
||||
|
||||
indices.push_back(thisrow + i - 1);
|
||||
indices.push_back(thisrow + i);
|
||||
indices.push_back(prevrow + i);
|
||||
};
|
||||
};
|
||||
|
||||
prevrow = thisrow;
|
||||
thisrow = point;
|
||||
};
|
||||
|
||||
/* cylinder */
|
||||
thisrow = point;
|
||||
prevrow = 0;
|
||||
for(j = 0; j <= (rings + 1); j++)
|
||||
{
|
||||
v = float(j);
|
||||
v /= (rings + 1);
|
||||
|
||||
y = mid_height * v;
|
||||
y = (mid_height * 0.5f) - y;
|
||||
|
||||
for(i = 0; i <= radial_segments; i++)
|
||||
{
|
||||
u = float(i);
|
||||
u /= radial_segments;
|
||||
|
||||
x = std::sin(u * (Pi<float>() * 2.0f));
|
||||
z = std::cos(u * (Pi<float>() * 2.0f));
|
||||
|
||||
Vec3 p = Vec3f(x * radius, y, z * radius);
|
||||
|
||||
Vertex vertex;
|
||||
vertex.position = p;
|
||||
// vertex.normal = Vec4f(x, z, 0.0f);
|
||||
vertex.normal = Vec4f(x, 0.0f, z);
|
||||
// vertex.uv = Vec2f(u, onethird + (v * onethird));
|
||||
vertex.uv = Vec2f(u, v * 0.5f);
|
||||
data.emplace_back(vertex);
|
||||
|
||||
point++;
|
||||
|
||||
if(i > 0 && j > 0)
|
||||
{
|
||||
indices.push_back(thisrow + i - 1);
|
||||
indices.push_back(prevrow + i);
|
||||
indices.push_back(prevrow + i - 1);
|
||||
|
||||
indices.push_back(thisrow + i - 1);
|
||||
indices.push_back(thisrow + i);
|
||||
indices.push_back(prevrow + i);
|
||||
};
|
||||
};
|
||||
|
||||
prevrow = thisrow;
|
||||
thisrow = point;
|
||||
};
|
||||
|
||||
/* bottom hemisphere */
|
||||
thisrow = point;
|
||||
prevrow = 0;
|
||||
|
||||
for(j = 0; j <= (rings + 1); j++)
|
||||
{
|
||||
v = float(j);
|
||||
|
||||
v /= (rings + 1);
|
||||
v += 1.0f;
|
||||
w = std::sin(0.5f * Pi<float>() * v);
|
||||
y = radius * std::cos(0.5f * Pi<float>() * v);
|
||||
|
||||
for(i = 0; i <= radial_segments; i++)
|
||||
{
|
||||
float u2 = float(i);
|
||||
u2 /= radial_segments;
|
||||
|
||||
x = std::sin(u2 * (Pi<float>() * 2.0f));
|
||||
z = std::cos(u2 * (Pi<float>() * 2.0f));
|
||||
|
||||
Vec3 p = Vec3f(x * radius * w, y, z * radius * w);
|
||||
|
||||
Vertex vertex;
|
||||
vertex.position = p + Vec3f(0.0f, -0.5f * mid_height, 0.0f);
|
||||
vertex.normal = p.Normalize();
|
||||
vertex.uv = Vec2f(u2, twothirds + ((v - 1.0f) * onethird));
|
||||
data.emplace_back(vertex);
|
||||
|
||||
point++;
|
||||
|
||||
if(i > 0 && j > 0)
|
||||
{
|
||||
indices.push_back(thisrow + i - 1);
|
||||
indices.push_back(prevrow + i);
|
||||
indices.push_back(prevrow + i - 1);
|
||||
|
||||
indices.push_back(thisrow + i - 1);
|
||||
indices.push_back(thisrow + i);
|
||||
indices.push_back(prevrow + i);
|
||||
};
|
||||
};
|
||||
|
||||
prevrow = thisrow;
|
||||
thisrow = point;
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
|
||||
mesh->AddSubMesh({ std::move(data), std::move(indices) });
|
||||
return mesh;
|
||||
}
|
||||
}
|
||||
158
Runtime/Sources/Graphics/Model.cpp
git.filemode.normal_file
158
Runtime/Sources/Graphics/Model.cpp
git.filemode.normal_file
@@ -0,0 +1,158 @@
|
||||
#include <Graphics/Model.h>
|
||||
#include <Graphics/Loaders/OBJ.h>
|
||||
#include <Renderer/Pipelines/Graphics.h>
|
||||
#include <Maths/Angles.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
Model::Model(std::shared_ptr<Mesh> mesh) : p_mesh(mesh)
|
||||
{
|
||||
if(p_mesh)
|
||||
m_materials.resize(p_mesh->GetSubMeshCount() + 1);
|
||||
|
||||
if(!s_default_material)
|
||||
{
|
||||
CPUBuffer default_pixels{ kvfFormatSize(VK_FORMAT_R8G8B8A8_SRGB) };
|
||||
default_pixels.GetDataAs<std::uint32_t>()[0] = 0xFFFFFFFF;
|
||||
std::shared_ptr<Texture> default_texture = std::make_shared<Texture>(std::move(default_pixels), 1, 1, VK_FORMAT_R8G8B8A8_SRGB);
|
||||
|
||||
MaterialTextures textures;
|
||||
textures.albedo = default_texture;
|
||||
s_default_material = std::make_shared<Material>(textures);
|
||||
}
|
||||
m_materials.back() = s_default_material;
|
||||
}
|
||||
|
||||
void Model::Draw(VkCommandBuffer cmd, std::shared_ptr<DescriptorSet> matrices_set, const GraphicPipeline& pipeline, std::shared_ptr<DescriptorSet> set, std::size_t& drawcalls, std::size_t& polygondrawn, std::size_t frame_index) const
|
||||
{
|
||||
if(!p_mesh)
|
||||
return;
|
||||
for(std::size_t i = 0; i < GetSubMeshCount(); i++)
|
||||
{
|
||||
std::shared_ptr<Material> material;
|
||||
if(!m_materials[i])
|
||||
material = m_materials.back();
|
||||
else
|
||||
material = m_materials[i];
|
||||
if(!material->IsSetInit())
|
||||
material->UpdateDescriptorSet(set);
|
||||
material->Bind(frame_index, cmd);
|
||||
std::array<VkDescriptorSet, 2> sets = { matrices_set->GetSet(frame_index), material->GetSet(frame_index) };
|
||||
RenderCore::Get().vkCmdBindDescriptorSets(cmd, pipeline.GetPipelineBindPoint(), pipeline.GetPipelineLayout(), 0, sets.size(), sets.data(), 0, nullptr);
|
||||
p_mesh->Draw(cmd, drawcalls, polygondrawn, i);
|
||||
}
|
||||
}
|
||||
|
||||
RadianAnglef GetAngleBetweenVectors(const Vec3f& a, const Vec3f& b) noexcept
|
||||
{
|
||||
float cosine_theta = (a.DotProduct(b)) / (a.GetLength() * b.GetLength());
|
||||
RadianAnglef angle(std::acos(cosine_theta));
|
||||
return angle;
|
||||
}
|
||||
|
||||
Model LoadModelFromObjFile(std::filesystem::path path) noexcept
|
||||
{
|
||||
auto obj_data = LoadObjFromFile(path);
|
||||
if(!obj_data)
|
||||
return { nullptr };
|
||||
TesselateObjData(*obj_data);
|
||||
ObjModel obj_model = ConvertObjDataToObjModel(*obj_data);
|
||||
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
|
||||
|
||||
float min_x = std::numeric_limits<float>::max(), max_x = std::numeric_limits<float>::lowest();
|
||||
float min_y = std::numeric_limits<float>::max(), max_y = std::numeric_limits<float>::lowest();
|
||||
float min_z = std::numeric_limits<float>::max(), max_z = std::numeric_limits<float>::lowest();
|
||||
|
||||
bool needs_to_generate_normals = obj_model.normal.empty();
|
||||
std::unordered_map<std::string, std::vector<Vec3f>> generated_normals;
|
||||
if(needs_to_generate_normals)
|
||||
{
|
||||
for(auto& [group, faces] : obj_model.faces)
|
||||
{
|
||||
generated_normals[group] = std::vector<Vec3f>(faces.size(), Vec3f{});
|
||||
for(std::size_t i = 0; i < faces.size(); i += 3)
|
||||
{
|
||||
Vec3f vec_a{ obj_model.vertex[faces[i + 1]] - obj_model.vertex[faces[i]] };
|
||||
Vec3f vec_b{ obj_model.vertex[faces[i + 2]] - obj_model.vertex[faces[i]] };
|
||||
Vec3f normal = vec_a.CrossProduct(vec_b).Normalize();
|
||||
generated_normals[group][i + 0] = normal;
|
||||
generated_normals[group][i + 1] = normal;
|
||||
generated_normals[group][i + 2] = normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& [group, faces] : obj_model.faces)
|
||||
{
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<std::uint32_t> indices;
|
||||
for(std::size_t i = 0; i < faces.size(); i++)
|
||||
{
|
||||
min_x = std::min(obj_model.vertex[faces[i]].x, min_x);
|
||||
min_y = std::min(obj_model.vertex[faces[i]].y, min_y);
|
||||
min_z = std::min(obj_model.vertex[faces[i]].z, min_z);
|
||||
max_x = std::max(obj_model.vertex[faces[i]].x, max_x);
|
||||
max_y = std::max(obj_model.vertex[faces[i]].y, max_y);
|
||||
max_z = std::max(obj_model.vertex[faces[i]].z, max_z);
|
||||
|
||||
Vec4f color{};
|
||||
switch(i % 10)
|
||||
{
|
||||
case 0: color = Vec4f{ 1.0f, 0.0f, 1.0f, 1.0f }; break;
|
||||
case 1: color = Vec4f{ 1.0f, 1.0f, 0.0f, 1.0f }; break;
|
||||
case 2: color = Vec4f{ 1.0f, 0.5f, 0.0f, 1.0f }; break;
|
||||
case 3: color = Vec4f{ 1.0f, 0.0f, 0.0f, 1.0f }; break;
|
||||
case 4: color = Vec4f{ 0.2f, 0.0f, 0.8f, 1.0f }; break;
|
||||
case 5: color = Vec4f{ 0.0f, 1.0f, 1.0f, 1.0f }; break;
|
||||
case 6: color = Vec4f{ 0.0f, 1.0f, 0.0f, 1.0f }; break;
|
||||
case 7: color = Vec4f{ 0.0f, 0.0f, 1.0f, 1.0f }; break;
|
||||
case 8: color = Vec4f{ 0.3f, 0.0f, 0.4f, 1.0f }; break;
|
||||
default: color = Vec4f{ 1.0f, 1.0f, 1.0f, 1.0f }; break;
|
||||
}
|
||||
|
||||
Vec3f normal{};
|
||||
if(needs_to_generate_normals)
|
||||
{
|
||||
normal = generated_normals[group][i];
|
||||
for(std::size_t j = 0; j < faces.size(); j++)
|
||||
{
|
||||
if(faces[j] == faces[i] && i != j)
|
||||
{
|
||||
RadianAnglef angle = GetAngleBetweenVectors(generated_normals[group][i], generated_normals[group][j]);
|
||||
if(angle.ToDegrees() < 89.0f)
|
||||
normal += generated_normals[group][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
normal = obj_model.normal[faces[i]];
|
||||
|
||||
Vertex v(
|
||||
Vec4f{
|
||||
obj_model.vertex[faces[i]],
|
||||
1.0f
|
||||
},
|
||||
color,
|
||||
Vec4f{
|
||||
normal.Normalize(),
|
||||
1.0f
|
||||
},
|
||||
(obj_model.tex_coord.empty() ?
|
||||
Vec2f{ (obj_model.vertex[faces[i]].x - min_x) / (max_x - min_x), 1.0f - ((obj_model.vertex[faces[i]].y - min_y) / (max_y - min_y)) }
|
||||
:
|
||||
obj_model.tex_coord[faces[i]]
|
||||
)
|
||||
);
|
||||
indices.push_back(vertices.size());
|
||||
vertices.push_back(std::move(v));
|
||||
}
|
||||
|
||||
mesh->AddSubMesh({ vertices, indices });
|
||||
}
|
||||
Model model(mesh);
|
||||
model.m_center = Vec3f{ (min_x + max_x) / 2.0f, (min_y + max_y) / 2.0f, (min_z + max_z) / 2.0f };
|
||||
return model;
|
||||
}
|
||||
}
|
||||
223
Runtime/Sources/Graphics/Scene.cpp
git.filemode.normal_file
223
Runtime/Sources/Graphics/Scene.cpp
git.filemode.normal_file
@@ -0,0 +1,223 @@
|
||||
#include <Graphics/Scene.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Renderer/RenderCore.h>
|
||||
#include <Platform/Inputs.h>
|
||||
#include <Core/Logs.h>
|
||||
#include <Renderer/ViewerData.h>
|
||||
#include <Core/EventBus.h>
|
||||
#include <Core/Engine.h>
|
||||
#include <Graphics/DogicaTTF.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
Scene::Scene(std::string_view name, SceneDescriptor desc)
|
||||
: m_name(name), m_descriptor(std::move(desc)), p_parent(nullptr)
|
||||
{
|
||||
LoadFont("default", 6.0f);
|
||||
}
|
||||
|
||||
Scene::Scene(std::string_view name, SceneDescriptor desc, NonOwningPtr<Scene> parent)
|
||||
: m_name(name), m_descriptor(std::move(desc)), p_parent(parent)
|
||||
{
|
||||
LoadFont("default", 6.0f);
|
||||
}
|
||||
|
||||
Actor& Scene::CreateActor(Model model) noexcept
|
||||
{
|
||||
UUID uuid = UUID();
|
||||
return m_actors.try_emplace(uuid, uuid, std::move(model)).first->second;
|
||||
}
|
||||
|
||||
Actor& Scene::CreateActor(std::string_view name, Model model)
|
||||
{
|
||||
UUID uuid = UUID();
|
||||
return m_actors.try_emplace(uuid, uuid, std::move(model)).first->second;
|
||||
}
|
||||
|
||||
Narrator& Scene::CreateNarrator() noexcept
|
||||
{
|
||||
UUID uuid = UUID();
|
||||
return m_narrators.try_emplace(uuid, uuid).first->second;
|
||||
}
|
||||
|
||||
Narrator& Scene::CreateNarrator(std::string_view name)
|
||||
{
|
||||
UUID uuid = UUID();
|
||||
return m_narrators.try_emplace(uuid, uuid).first->second;
|
||||
}
|
||||
|
||||
Sprite& Scene::CreateSprite(std::shared_ptr<Texture> texture) noexcept
|
||||
{
|
||||
UUID uuid = UUID();
|
||||
return m_sprites.try_emplace(uuid, uuid, texture).first->second;
|
||||
}
|
||||
|
||||
Sprite& Scene::CreateSprite(std::string_view name, std::shared_ptr<Texture> texture)
|
||||
{
|
||||
UUID uuid = UUID();
|
||||
return m_sprites.try_emplace(uuid, uuid, texture).first->second;
|
||||
}
|
||||
|
||||
Text& Scene::CreateText(std::string text) noexcept
|
||||
{
|
||||
UUID uuid = UUID();
|
||||
return m_texts.try_emplace(uuid, uuid, std::move(text), p_bound_font).first->second;
|
||||
}
|
||||
|
||||
Text& Scene::CreateText(std::string_view name, std::string text)
|
||||
{
|
||||
UUID uuid = UUID();
|
||||
return m_texts.try_emplace(uuid, uuid, std::move(text), p_bound_font).first->second;
|
||||
}
|
||||
|
||||
void Scene::LoadFont(std::filesystem::path path, float scale)
|
||||
{
|
||||
std::shared_ptr<Font> font = m_fonts_registry.GetFont(path, scale);
|
||||
if(!font)
|
||||
{
|
||||
if(path.string() == "default")
|
||||
font = std::make_shared<Font>("default", dogica_ttf, scale);
|
||||
else
|
||||
font = std::make_shared<Font>(std::move(path), scale);
|
||||
font->BuildFont();
|
||||
m_fonts_registry.RegisterFont(font);
|
||||
}
|
||||
p_bound_font = font;
|
||||
}
|
||||
|
||||
void Scene::RemoveActor(Actor& actor) noexcept
|
||||
{
|
||||
auto it = m_actors.find(actor.GetUUID());
|
||||
if(it == m_actors.end())
|
||||
{
|
||||
Error("Actor not found");
|
||||
return;
|
||||
}
|
||||
m_actors.erase(it);
|
||||
}
|
||||
|
||||
void Scene::RemoveNarrator(Narrator& narrator) noexcept
|
||||
{
|
||||
auto it = m_narrators.find(narrator.GetUUID());
|
||||
if(it == m_narrators.end())
|
||||
{
|
||||
Error("Narrator not found");
|
||||
return;
|
||||
}
|
||||
m_narrators.erase(it);
|
||||
}
|
||||
|
||||
void Scene::RemoveSprite(Sprite& sprite) noexcept
|
||||
{
|
||||
auto it = m_sprites.find(sprite.GetUUID());
|
||||
if(it == m_sprites.end())
|
||||
{
|
||||
Error("Sprite not found");
|
||||
return;
|
||||
}
|
||||
m_sprites.erase(it);
|
||||
}
|
||||
|
||||
void Scene::RemoveText(Text& text) noexcept
|
||||
{
|
||||
auto it = m_texts.find(text.GetUUID());
|
||||
if(it == m_texts.end())
|
||||
{
|
||||
Error("Text not found");
|
||||
return;
|
||||
}
|
||||
m_texts.erase(it);
|
||||
}
|
||||
|
||||
void Scene::SwitchToChild(std::string_view name) const noexcept
|
||||
{
|
||||
auto it = std::find_if(m_scene_children.begin(), m_scene_children.end(), [name](const Scene& scene){ return name == scene.GetName(); });
|
||||
if(it == m_scene_children.end())
|
||||
{
|
||||
Error("Cannot switch to scene '%', scene not found in children of '%'", name, m_name);
|
||||
return;
|
||||
}
|
||||
ScopEngine::Get().SwitchToScene(const_cast<Scene*>(&(*it)));
|
||||
}
|
||||
|
||||
void Scene::SwitchToParent() const noexcept
|
||||
{
|
||||
ScopEngine::Get().SwitchToScene(p_parent);
|
||||
}
|
||||
|
||||
void Scene::Init(NonOwningPtr<Renderer> renderer)
|
||||
{
|
||||
std::function<void(const EventBase&)> functor = [this, renderer](const EventBase& event)
|
||||
{
|
||||
if(event.What() == Event::ResizeEventCode)
|
||||
{
|
||||
m_depth.Destroy();
|
||||
m_depth.Init(renderer->GetSwapchain().GetSwapchainImages().back().GetWidth(), renderer->GetSwapchain().GetSwapchainImages().back().GetHeight(), false, m_name + "_depth");
|
||||
m_depth.CreateSampler();
|
||||
}
|
||||
|
||||
if(event.What() == Event::ResizeEventCode || event.What() == Event::SceneHasChangedEventCode)
|
||||
m_pipeline.Destroy(); // Ugly but f*ck off
|
||||
};
|
||||
EventBus::RegisterListener({ functor, m_name + std::to_string(reinterpret_cast<std::uintptr_t>(this)) });
|
||||
|
||||
if(m_descriptor.post_process_shader)
|
||||
{
|
||||
m_post_process.set = RenderCore::Get().GetDescriptorPoolManager().GetAvailablePool().RequestDescriptorSet(m_descriptor.post_process_shader->GetShaderLayout().set_layouts.at(0), ShaderType::Fragment);
|
||||
m_post_process.data_buffer = std::make_shared<UniformBuffer>();
|
||||
m_post_process.data_buffer->Init(m_descriptor.post_process_data_size, m_name + "_post_process_data_buffer");
|
||||
}
|
||||
|
||||
m_post_process.data.Allocate(m_descriptor.post_process_data_size);
|
||||
|
||||
auto vertex_shader = RenderCore::Get().GetDefaultVertexShader();
|
||||
m_depth.Init(renderer->GetSwapchain().GetSwapchainImages().back().GetWidth(), renderer->GetSwapchain().GetSwapchainImages().back().GetHeight(), false, m_name + "_depth");
|
||||
m_depth.CreateSampler();
|
||||
m_forward.matrices_buffer = std::make_shared<UniformBuffer>();
|
||||
m_forward.matrices_buffer->Init(sizeof(ViewerData), m_name + "_matrice_buffer");
|
||||
|
||||
m_forward.matrices_set = RenderCore::Get().GetDescriptorPoolManager().GetAvailablePool().RequestDescriptorSet(vertex_shader->GetShaderLayout().set_layouts.at(0), ShaderType::Vertex);
|
||||
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||
{
|
||||
m_forward.matrices_set->SetUniformBuffer(i, 0, m_forward.matrices_buffer->Get(i));
|
||||
m_forward.matrices_set->Update(i);
|
||||
}
|
||||
m_forward.albedo_set = RenderCore::Get().GetDescriptorPoolManager().GetAvailablePool().RequestDescriptorSet(m_descriptor.fragment_shader->GetShaderLayout().set_layouts.at(1), ShaderType::Fragment);
|
||||
|
||||
for(auto& child : m_scene_children)
|
||||
child.Init(renderer);
|
||||
}
|
||||
|
||||
void Scene::Update(Inputs& input, float timestep, float aspect)
|
||||
{
|
||||
for(auto& [_, actor] : m_actors)
|
||||
actor.Update(this, input, timestep);
|
||||
for(auto& [_, narrator] : m_narrators)
|
||||
narrator.Update(this, input, timestep);
|
||||
for(auto& [_, sprite] : m_sprites)
|
||||
sprite.Update(this, input, timestep);
|
||||
if(m_descriptor.camera)
|
||||
m_descriptor.camera->Update(input, aspect, timestep);
|
||||
}
|
||||
|
||||
void Scene::Destroy()
|
||||
{
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
p_skybox.reset();
|
||||
m_depth.Destroy();
|
||||
m_actors.clear();
|
||||
m_narrators.clear();
|
||||
m_sprites.clear();
|
||||
m_pipeline.Destroy();
|
||||
m_descriptor.fragment_shader.reset();
|
||||
m_descriptor.post_process_shader.reset();
|
||||
m_forward.matrices_buffer->Destroy();
|
||||
if(m_post_process.data_buffer)
|
||||
m_post_process.data_buffer->Destroy();
|
||||
m_fonts_registry.Reset();
|
||||
for(auto& child : m_scene_children)
|
||||
child.Destroy();
|
||||
}
|
||||
}
|
||||
41
Runtime/Sources/Graphics/Sprite.cpp
git.filemode.normal_file
41
Runtime/Sources/Graphics/Sprite.cpp
git.filemode.normal_file
@@ -0,0 +1,41 @@
|
||||
#include <Graphics/Sprite.h>
|
||||
#include <Core/Script.h>
|
||||
#include <Renderer/Image.h>
|
||||
#include <Graphics/MeshFactory.h>
|
||||
#include <Core/Logs.h>
|
||||
#include <Core/UUID.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
Sprite::Sprite(std::shared_ptr<Texture> texture)
|
||||
{
|
||||
Verify((bool)texture, "Sprite: invalid texture");
|
||||
m_uuid = UUID();
|
||||
p_mesh = CreateQuad(0, 0, texture->GetWidth(), texture->GetHeight());
|
||||
p_texture = texture;
|
||||
if(p_script)
|
||||
p_script->OnInit(this);
|
||||
}
|
||||
|
||||
Sprite::Sprite(std::uint64_t uuid, std::shared_ptr<Texture> texture)
|
||||
{
|
||||
Verify((bool)texture, "Sprite: invalid texture");
|
||||
m_uuid = uuid;
|
||||
p_mesh = CreateQuad(0, 0, texture->GetWidth(), texture->GetHeight());
|
||||
p_texture = texture;
|
||||
if(p_script)
|
||||
p_script->OnInit(this);
|
||||
}
|
||||
|
||||
void Sprite::Update(NonOwningPtr<Scene> scene, Inputs& input, float delta)
|
||||
{
|
||||
if(p_script)
|
||||
p_script->OnUpdate(scene, this, input, delta);
|
||||
}
|
||||
|
||||
Sprite::~Sprite()
|
||||
{
|
||||
if(p_script)
|
||||
p_script->OnQuit(this);
|
||||
}
|
||||
}
|
||||
48
Runtime/Sources/Graphics/Text.cpp
git.filemode.normal_file
48
Runtime/Sources/Graphics/Text.cpp
git.filemode.normal_file
@@ -0,0 +1,48 @@
|
||||
#include <Graphics/Text.h>
|
||||
#include <Renderer/Vertex.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
Text::Text(std::uint64_t uuid, const std::string& text, std::shared_ptr<Font> font) : m_uuid(uuid)
|
||||
{
|
||||
Assert(font != nullptr, "invalid font");
|
||||
|
||||
std::vector<Vertex> vertex_data;
|
||||
std::vector<std::uint32_t> index_data;
|
||||
|
||||
float stb_x = 0.0f;
|
||||
float stb_y = 0.0f;
|
||||
|
||||
const auto& char_data = font->GetCharData();
|
||||
for(char c : text)
|
||||
{
|
||||
if(c < 32)
|
||||
continue;
|
||||
|
||||
stbtt_aligned_quad q;
|
||||
stbtt_GetPackedQuad(char_data.data(), RANGE, RANGE, c - 32, &stb_x, &stb_y, &q, 1);
|
||||
|
||||
std::size_t index = vertex_data.size();
|
||||
|
||||
vertex_data.emplace_back(Vec4f{ q.x0, q.y0, 0.0f, 1.0f }, Vec4f{ 1.0f }, -Vec2f{ q.s0, -q.t0 });
|
||||
vertex_data.emplace_back(Vec4f{ q.x1, q.y0, 0.0f, 1.0f }, Vec4f{ 1.0f }, -Vec2f{ q.s1, -q.t0 });
|
||||
vertex_data.emplace_back(Vec4f{ q.x1, q.y1, 0.0f, 1.0f }, Vec4f{ 1.0f }, -Vec2f{ q.s1, -q.t1 });
|
||||
vertex_data.emplace_back(Vec4f{ q.x0, q.y1, 0.0f, 1.0f }, Vec4f{ 1.0f }, -Vec2f{ q.s0, -q.t1 });
|
||||
|
||||
index_data.push_back(index + 0);
|
||||
index_data.push_back(index + 1);
|
||||
index_data.push_back(index + 2);
|
||||
index_data.push_back(index + 2);
|
||||
index_data.push_back(index + 3);
|
||||
index_data.push_back(index + 0);
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>();
|
||||
mesh->AddSubMesh({ std::move(vertex_data), std::move(index_data) });
|
||||
p_mesh = mesh;
|
||||
p_font = font;
|
||||
m_text = text;
|
||||
}
|
||||
}
|
||||
59
Runtime/Sources/Platform/Inputs.cpp
git.filemode.normal_file
59
Runtime/Sources/Platform/Inputs.cpp
git.filemode.normal_file
@@ -0,0 +1,59 @@
|
||||
#include <Platform/Inputs.h>
|
||||
#include <Core/EventBus.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
struct ResizeEventSwapchain : public EventBase
|
||||
{
|
||||
Event What() const override { return Event::ResizeEventCode; }
|
||||
};
|
||||
}
|
||||
|
||||
Inputs::Inputs() : m_keys(SDL_GetKeyboardState(&m_keys_count))
|
||||
{}
|
||||
|
||||
void Inputs::Update()
|
||||
{
|
||||
SDL_GetRelativeMouseState(&m_x_rel, &m_y_rel);
|
||||
std::uint8_t mouse_state = SDL_GetMouseState(&m_x, &m_y);
|
||||
m_mouse[0] = SDL_BUTTON(mouse_state) & SDL_BUTTON_LMASK;
|
||||
m_mouse[1] = SDL_BUTTON(mouse_state) & SDL_BUTTON_MMASK;
|
||||
m_mouse[2] = SDL_BUTTON(mouse_state) & SDL_BUTTON_RMASK;
|
||||
m_mouse[3] = SDL_BUTTON(mouse_state) & SDL_BUTTON_X1MASK;
|
||||
m_mouse[4] = SDL_BUTTON(mouse_state) & SDL_BUTTON_X2MASK;
|
||||
|
||||
m_is_mouse_wheel_up = false;
|
||||
m_is_mouse_wheel_down = false;
|
||||
|
||||
while(SDL_PollEvent(&m_event))
|
||||
{
|
||||
for(auto& hook : m_hooks)
|
||||
hook(&m_event);
|
||||
switch(m_event.type)
|
||||
{
|
||||
case SDL_MOUSEWHEEL:
|
||||
{
|
||||
if(m_event.wheel.y > 0) // scroll up
|
||||
m_is_mouse_wheel_up = true;
|
||||
else if(m_event.wheel.y < 0) // scroll down
|
||||
m_is_mouse_wheel_down = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
{
|
||||
switch(m_event.window.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_CLOSE: m_has_recieved_close_event = true; break;
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED: EventBus::Send("__ScopSwapchain", Internal::ResizeEventSwapchain{}); break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
73
Runtime/Sources/Platform/Window.cpp
git.filemode.normal_file
73
Runtime/Sources/Platform/Window.cpp
git.filemode.normal_file
@@ -0,0 +1,73 @@
|
||||
#include <Platform/Window.h>
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
#include <Core/Logs.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
Window::Window(const std::string& title, std::uint32_t width, std::uint32_t height, bool hidden) : m_title(title), m_height(height), m_width(width)
|
||||
{
|
||||
std::uint32_t flags = SDL_WINDOW_VULKAN;
|
||||
if(hidden)
|
||||
flags |= SDL_WINDOW_HIDDEN;
|
||||
else
|
||||
flags |= SDL_WINDOW_SHOWN;
|
||||
|
||||
if(width == 0 && height == 0)
|
||||
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
else
|
||||
flags |= SDL_WINDOW_RESIZABLE;
|
||||
|
||||
p_window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, flags);
|
||||
if(!p_window)
|
||||
FatalError("Unable to open a new window, %", SDL_GetError());
|
||||
}
|
||||
|
||||
void Window::FetchWindowInfos() noexcept
|
||||
{
|
||||
Vec2i size;
|
||||
SDL_GetWindowSize(p_window, &size.x, &size.y);
|
||||
m_width = size.x;
|
||||
m_height = size.y;
|
||||
}
|
||||
|
||||
VkSurfaceKHR Window::CreateVulkanSurface(VkInstance instance) const noexcept
|
||||
{
|
||||
VkSurfaceKHR surface;
|
||||
if(!SDL_Vulkan_CreateSurface(p_window, instance, &surface))
|
||||
FatalError("SDL: could not create a Vulkan surface; %", SDL_GetError());
|
||||
return surface;
|
||||
}
|
||||
|
||||
std::vector<const char*> Window::GetRequiredVulkanInstanceExtentions() const noexcept
|
||||
{
|
||||
std::uint32_t count;
|
||||
if(!SDL_Vulkan_GetInstanceExtensions(p_window, &count, nullptr))
|
||||
FatalError("SDL Manager: could not retrieve Vulkan instance extensions");
|
||||
std::vector<const char*> extensions(count);
|
||||
if(!SDL_Vulkan_GetInstanceExtensions(p_window, &count, extensions.data()))
|
||||
FatalError("SDL Manager: could not retrieve Vulkan instance extensions");
|
||||
extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||
return extensions;
|
||||
}
|
||||
|
||||
Vec2ui Window::GetVulkanDrawableSize() const noexcept
|
||||
{
|
||||
Vec2i extent;
|
||||
SDL_Vulkan_GetDrawableSize(p_window, &extent.x, &extent.y);
|
||||
return Vec2ui{ extent };
|
||||
}
|
||||
|
||||
void Window::Destroy() noexcept
|
||||
{
|
||||
if(p_window != nullptr)
|
||||
{
|
||||
SDL_DestroyWindow(p_window);
|
||||
p_window = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Window::~Window()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
246
Runtime/Sources/Renderer/Buffer.cpp
git.filemode.normal_file
246
Runtime/Sources/Renderer/Buffer.cpp
git.filemode.normal_file
@@ -0,0 +1,246 @@
|
||||
#include <Renderer/RenderCore.h>
|
||||
#include <Core/Logs.h>
|
||||
#include <Renderer/Buffer.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
void GPUBuffer::Init(BufferType type, VkDeviceSize size, VkBufferUsageFlags usage, CPUBuffer data, std::string_view name, bool dedicated_alloc)
|
||||
{
|
||||
if(type == BufferType::Constant)
|
||||
{
|
||||
if(data.Empty())
|
||||
{
|
||||
Warning("Vulkan: trying to create constant buffer without data (constant buffers cannot be modified after creation)");
|
||||
return;
|
||||
}
|
||||
m_usage = usage | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
m_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
}
|
||||
else if(type == BufferType::HighDynamic)
|
||||
{
|
||||
m_usage = usage;
|
||||
m_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
}
|
||||
else // LowDynamic or Staging
|
||||
{
|
||||
m_usage = usage | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
m_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
}
|
||||
|
||||
if(type == BufferType::Staging && data.Empty())
|
||||
Warning("Vulkan: trying to create staging buffer without data (wtf?)");
|
||||
|
||||
CreateBuffer(size, m_usage, m_flags, std::move(name), dedicated_alloc);
|
||||
|
||||
if(!data.Empty())
|
||||
{
|
||||
if(m_memory.map != nullptr)
|
||||
std::memcpy(m_memory.map, data.GetData(), data.GetSize());
|
||||
}
|
||||
if(type == BufferType::Constant || type == BufferType::LowDynamic)
|
||||
PushToGPU();
|
||||
}
|
||||
|
||||
void GPUBuffer::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, std::string_view name, bool dedicated_alloc)
|
||||
{
|
||||
auto device = RenderCore::Get().GetDevice();
|
||||
m_buffer = kvfCreateBuffer(device, usage, size);
|
||||
|
||||
VkMemoryRequirements mem_requirements;
|
||||
RenderCore::Get().vkGetBufferMemoryRequirements(device, m_buffer, &mem_requirements);
|
||||
|
||||
//m_memory = RenderCore::Get().GetAllocator().Allocate(size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties), dedicated_alloc);
|
||||
m_memory = RenderCore::Get().GetAllocator().Allocate(mem_requirements.size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties), dedicated_alloc);
|
||||
RenderCore::Get().vkBindBufferMemory(device, m_buffer, m_memory.memory, m_memory.offset);
|
||||
|
||||
#ifdef SCOP_HAS_DEBUG_UTILS_FUNCTIONS
|
||||
std::string alloc_name{ name };
|
||||
if(usage & VK_BUFFER_USAGE_INDEX_BUFFER_BIT)
|
||||
alloc_name.append("_index_buffer");
|
||||
else if(usage & VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)
|
||||
alloc_name.append("_vertex_buffer");
|
||||
else if(usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)
|
||||
alloc_name.append("_uniform_buffer");
|
||||
else
|
||||
alloc_name.append("_buffer");
|
||||
if(m_flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
|
||||
alloc_name.append("_gpu");
|
||||
m_name = name;
|
||||
|
||||
VkDebugUtilsObjectNameInfoEXT name_info{};
|
||||
name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
|
||||
name_info.objectType = VK_OBJECT_TYPE_BUFFER;
|
||||
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_buffer);
|
||||
name_info.pObjectName = m_name.c_str();
|
||||
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
|
||||
#endif
|
||||
|
||||
m_size = size;
|
||||
|
||||
m_is_dedicated_alloc = dedicated_alloc;
|
||||
|
||||
s_buffer_count++;
|
||||
}
|
||||
|
||||
bool GPUBuffer::CopyFrom(const GPUBuffer& buffer, std::size_t src_offset, std::size_t dst_offset) noexcept
|
||||
{
|
||||
if(!(m_usage & VK_BUFFER_USAGE_TRANSFER_DST_BIT))
|
||||
{
|
||||
Error("Vulkan: buffer cannot be the destination of a copy because it does not have the correct usage flag");
|
||||
return false;
|
||||
}
|
||||
if(!(buffer.m_usage & VK_BUFFER_USAGE_TRANSFER_SRC_BIT))
|
||||
{
|
||||
Error("Vulkan: buffer cannot be the source of a copy because it does not have the correct usage flag");
|
||||
return false;
|
||||
}
|
||||
|
||||
VkCommandBuffer cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
|
||||
kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
|
||||
kvfCopyBufferToBuffer(cmd, m_buffer, buffer.Get(), buffer.GetSize(), src_offset, dst_offset);
|
||||
kvfEndCommandBuffer(cmd);
|
||||
if(!RenderCore::Get().StackSubmits())
|
||||
{
|
||||
VkFence fence = kvfCreateFence(RenderCore::Get().GetDevice());
|
||||
kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, fence);
|
||||
kvfWaitForFence(RenderCore::Get().GetDevice(), fence);
|
||||
kvfDestroyFence(RenderCore::Get().GetDevice(), fence);
|
||||
kvfDestroyCommandBuffer(RenderCore::Get().GetDevice(), cmd);
|
||||
}
|
||||
else
|
||||
kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, VK_NULL_HANDLE);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPUBuffer::PushToGPU() noexcept
|
||||
{
|
||||
GPUBuffer new_buffer;
|
||||
new_buffer.m_usage = (this->m_usage & 0xFFFFFFFC) | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
||||
new_buffer.m_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
new_buffer.CreateBuffer(m_memory.size, new_buffer.m_usage, new_buffer.m_flags, m_name, m_is_dedicated_alloc);
|
||||
|
||||
if(new_buffer.CopyFrom(*this))
|
||||
Swap(new_buffer);
|
||||
new_buffer.Destroy();
|
||||
}
|
||||
|
||||
void GPUBuffer::Destroy() noexcept
|
||||
{
|
||||
if(m_buffer == VK_NULL_HANDLE)
|
||||
return;
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
RenderCore::Get().vkDestroyBuffer(RenderCore::Get().GetDevice(), m_buffer, nullptr);
|
||||
RenderCore::Get().GetAllocator().Deallocate(m_memory);
|
||||
m_buffer = VK_NULL_HANDLE;
|
||||
m_memory = NULL_MEMORY_BLOCK;
|
||||
s_buffer_count--;
|
||||
}
|
||||
|
||||
void GPUBuffer::Swap(GPUBuffer& buffer) noexcept
|
||||
{
|
||||
std::swap(m_buffer, buffer.m_buffer);
|
||||
m_memory.Swap(buffer.m_memory);
|
||||
std::swap(m_usage, buffer.m_usage);
|
||||
std::swap(m_flags, buffer.m_flags);
|
||||
}
|
||||
|
||||
void VertexBuffer::SetData(CPUBuffer data)
|
||||
{
|
||||
if(data.GetSize() > m_memory.size)
|
||||
{
|
||||
Error("Vulkan: trying to store too much data in a vertex buffer (% bytes in % bytes)", data.GetSize(), m_memory.size);
|
||||
return;
|
||||
}
|
||||
if(data.Empty())
|
||||
{
|
||||
Warning("Vulkan: cannot set empty data in a vertex buffer");
|
||||
return;
|
||||
}
|
||||
GPUBuffer staging;
|
||||
staging.Init(BufferType::Staging, data.GetSize(), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, data);
|
||||
CopyFrom(staging);
|
||||
staging.Destroy();
|
||||
}
|
||||
|
||||
void IndexBuffer::SetData(CPUBuffer data)
|
||||
{
|
||||
if(data.GetSize() > m_memory.size)
|
||||
{
|
||||
Error("Vulkan: trying to store too much data in an index buffer (% bytes in % bytes)", data.GetSize(), m_memory.size);
|
||||
return;
|
||||
}
|
||||
if(data.Empty())
|
||||
{
|
||||
Warning("Vulkan: cannot set empty data in an index buffer");
|
||||
return;
|
||||
}
|
||||
GPUBuffer staging;
|
||||
staging.Init(BufferType::Staging, data.GetSize(), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, data);
|
||||
CopyFrom(staging);
|
||||
staging.Destroy();
|
||||
}
|
||||
|
||||
void MeshBuffer::SetVertexData(CPUBuffer data)
|
||||
{
|
||||
if(data.GetSize() > m_index_offset)
|
||||
{
|
||||
Error("Vulkan: trying to store too much data in a vertex buffer (% bytes in % bytes)", data.GetSize(), m_index_offset);
|
||||
return;
|
||||
}
|
||||
if(data.Empty())
|
||||
{
|
||||
Warning("Vulkan: cannot set empty data in a vertex buffer");
|
||||
return;
|
||||
}
|
||||
GPUBuffer staging;
|
||||
staging.Init(BufferType::Staging, data.GetSize(), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, data);
|
||||
CopyFrom(staging, 0, 0);
|
||||
staging.Destroy();
|
||||
}
|
||||
|
||||
void MeshBuffer::SetIndexData(CPUBuffer data)
|
||||
{
|
||||
if(data.GetSize() > m_memory.size - m_index_offset)
|
||||
{
|
||||
Error("Vulkan: trying to store too much data in an index buffer (% bytes in % bytes)", data.GetSize(), m_memory.size - m_index_offset);
|
||||
return;
|
||||
}
|
||||
if(data.Empty())
|
||||
{
|
||||
Warning("Vulkan: cannot set empty data in an index buffer");
|
||||
return;
|
||||
}
|
||||
GPUBuffer staging;
|
||||
staging.Init(BufferType::Staging, data.GetSize(), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, data);
|
||||
CopyFrom(staging, 0, m_index_offset);
|
||||
staging.Destroy();
|
||||
}
|
||||
|
||||
void UniformBuffer::Init(std::uint32_t size, std::string_view name)
|
||||
{
|
||||
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||
{
|
||||
m_buffers[i].Init(BufferType::HighDynamic, size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, {}, name);
|
||||
m_maps[i] = m_buffers[i].GetMap();
|
||||
if(m_maps[i] == nullptr)
|
||||
FatalError("Vulkan: unable to map a uniform buffer");
|
||||
}
|
||||
}
|
||||
|
||||
void UniformBuffer::SetData(CPUBuffer data, std::size_t frame_index)
|
||||
{
|
||||
if(data.GetSize() > m_buffers[frame_index].GetSize())
|
||||
{
|
||||
Error("Vulkan: invalid data size to update to a uniform buffer, % != %", data.GetSize(), m_buffers[frame_index].GetSize());
|
||||
return;
|
||||
}
|
||||
if(m_maps[frame_index] != nullptr)
|
||||
std::memcpy(m_maps[frame_index], data.GetData(), data.GetSize());
|
||||
}
|
||||
|
||||
void UniformBuffer::Destroy() noexcept
|
||||
{
|
||||
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||
m_buffers[i].Destroy();
|
||||
}
|
||||
}
|
||||
293
Runtime/Sources/Renderer/Descriptor.cpp
git.filemode.normal_file
293
Runtime/Sources/Renderer/Descriptor.cpp
git.filemode.normal_file
@@ -0,0 +1,293 @@
|
||||
#include <kvf.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include <Core/Logs.h>
|
||||
#include <Renderer/Image.h>
|
||||
#include <Renderer/Enums.h>
|
||||
#include <Renderer/Buffer.h>
|
||||
#include <Renderer/RenderCore.h>
|
||||
#include <Renderer/Descriptor.h>
|
||||
#include <Renderer/RenderCore.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
constexpr std::size_t MAX_SETS_PER_POOL = MAX_FRAMES_IN_FLIGHT * 1024;
|
||||
|
||||
void TransitionImageToCorrectLayout(Image& image, VkCommandBuffer cmd)
|
||||
{
|
||||
if(!image.IsInit())
|
||||
return;
|
||||
if(image.GetType() == ImageType::Color || image.GetType() == ImageType::Cube)
|
||||
image.TransitionLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, cmd);
|
||||
else if(image.GetType() == ImageType::Depth)
|
||||
image.TransitionLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, cmd);
|
||||
else
|
||||
Error("Vulkan: cannot transition descriptor image layout, unkown image type");
|
||||
}
|
||||
|
||||
void DescriptorPool::Init() noexcept
|
||||
{
|
||||
VkDescriptorPoolSize pool_sizes[] = {
|
||||
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, MAX_SETS_PER_POOL },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, MAX_SETS_PER_POOL },
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, MAX_SETS_PER_POOL }
|
||||
};
|
||||
|
||||
VkDescriptorPoolCreateInfo pool_info{};
|
||||
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
pool_info.poolSizeCount = sizeof(pool_sizes) / sizeof(pool_sizes[0]);
|
||||
pool_info.pPoolSizes = pool_sizes;
|
||||
pool_info.maxSets = MAX_SETS_PER_POOL;
|
||||
pool_info.flags = 0;
|
||||
kvfCheckVk(RenderCore::Get().vkCreateDescriptorPool(RenderCore::Get().GetDevice(), &pool_info, nullptr, &m_pool));
|
||||
m_allocation_count = 0;
|
||||
}
|
||||
|
||||
void DescriptorPool::Destroy() noexcept
|
||||
{
|
||||
if(m_pool == VK_NULL_HANDLE)
|
||||
return;
|
||||
for(auto& set : m_free_sets)
|
||||
kvfDestroyDescriptorSetLayout(RenderCore::Get().GetDevice(), set->m_set_layout);
|
||||
for(auto& set : m_used_sets)
|
||||
kvfDestroyDescriptorSetLayout(RenderCore::Get().GetDevice(), set->m_set_layout);
|
||||
RenderCore::Get().vkDestroyDescriptorPool(RenderCore::Get().GetDevice(), m_pool, nullptr);
|
||||
m_pool = VK_NULL_HANDLE;
|
||||
m_allocation_count = 0;
|
||||
m_free_sets.clear();
|
||||
m_used_sets.clear();
|
||||
}
|
||||
|
||||
std::shared_ptr<DescriptorSet> DescriptorPool::RequestDescriptorSet(const ShaderSetLayout& layout, ShaderType shader_type)
|
||||
{
|
||||
auto it = std::find_if(m_free_sets.begin(), m_free_sets.end(), [&](std::shared_ptr<DescriptorSet> set)
|
||||
{
|
||||
return shader_type == set->GetShaderType() && layout == set->GetShaderLayout();
|
||||
});
|
||||
if(it != m_free_sets.end())
|
||||
{
|
||||
std::shared_ptr<DescriptorSet> set = *it;
|
||||
m_free_sets.erase(it);
|
||||
m_used_sets.push_back(set);
|
||||
return set;
|
||||
}
|
||||
|
||||
std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> vulkan_sets;
|
||||
|
||||
VkShaderStageFlagBits vulkan_shader_stage;
|
||||
switch(shader_type)
|
||||
{
|
||||
case ShaderType::Vertex: vulkan_shader_stage = VK_SHADER_STAGE_VERTEX_BIT; break;
|
||||
case ShaderType::Fragment: vulkan_shader_stage = VK_SHADER_STAGE_FRAGMENT_BIT; break;
|
||||
|
||||
default: FatalError("wtf"); vulkan_shader_stage = VK_SHADER_STAGE_VERTEX_BIT; /* Just to shut up warnings */ break;
|
||||
}
|
||||
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings(layout.binds.size());
|
||||
{
|
||||
std::size_t i = 0;
|
||||
for(auto& [bind, type] : layout.binds)
|
||||
{
|
||||
bindings[i].binding = bind;
|
||||
bindings[i].descriptorCount = 1;
|
||||
bindings[i].descriptorType = type;
|
||||
bindings[i].pImmutableSamplers = nullptr;
|
||||
bindings[i].stageFlags = vulkan_shader_stage;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
VkDescriptorSetLayout vulkan_layout = kvfCreateDescriptorSetLayout(RenderCore::Get().GetDevice(), bindings.data(), bindings.size());
|
||||
|
||||
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||
{
|
||||
VkDescriptorSetAllocateInfo alloc_info = {};
|
||||
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
alloc_info.descriptorPool = m_pool;
|
||||
alloc_info.descriptorSetCount = 1;
|
||||
alloc_info.pSetLayouts = &vulkan_layout;
|
||||
VkDescriptorSet vulkan_set;
|
||||
kvfCheckVk(RenderCore::Get().vkAllocateDescriptorSets(RenderCore::Get().GetDevice(), &alloc_info, &vulkan_set));
|
||||
m_allocation_count++;
|
||||
vulkan_sets[i] = vulkan_set;
|
||||
}
|
||||
|
||||
std::shared_ptr<DescriptorSet> set(new DescriptorSet(*this, vulkan_layout, layout, std::move(vulkan_sets), shader_type));
|
||||
m_used_sets.push_back(set);
|
||||
return set;
|
||||
}
|
||||
|
||||
void DescriptorPool::ReturnDescriptorSet(std::shared_ptr<DescriptorSet> set)
|
||||
{
|
||||
//std::size_t i = 0;
|
||||
auto it = std::find_if(m_used_sets.begin(), m_used_sets.end(), [&](const std::shared_ptr<DescriptorSet>& rhs_set)
|
||||
{
|
||||
//i++;
|
||||
//std::cout << m_used_sets.size() << " " << i << std::endl;
|
||||
return set == rhs_set;
|
||||
});
|
||||
if(it == m_used_sets.end())
|
||||
return;
|
||||
m_used_sets.erase(it);
|
||||
m_free_sets.push_back(set);
|
||||
}
|
||||
|
||||
DescriptorPool& DescriptorPoolManager::GetAvailablePool()
|
||||
{
|
||||
for(auto& pool : m_pools)
|
||||
{
|
||||
if(pool.GetNumberOfSetsAllocated() < MAX_SETS_PER_POOL)
|
||||
return pool;
|
||||
}
|
||||
m_pools.emplace_back().Init();
|
||||
return m_pools.back();
|
||||
}
|
||||
|
||||
void DescriptorPoolManager::Destroy()
|
||||
{
|
||||
for(auto& pool : m_pools)
|
||||
pool.Destroy();
|
||||
m_pools.clear();
|
||||
}
|
||||
|
||||
DescriptorSet::DescriptorSet(DescriptorPool& pool, VkDescriptorSetLayout vulkan_layout, const ShaderSetLayout& layout, std::array<VkDescriptorSet, MAX_FRAMES_IN_FLIGHT> vulkan_sets, ShaderType shader_type) :
|
||||
m_shader_layout(layout),
|
||||
m_sets(std::move(vulkan_sets)),
|
||||
m_set_layout(vulkan_layout),
|
||||
m_shader_type(shader_type),
|
||||
m_pool(pool)
|
||||
{
|
||||
for(auto& [binding, type] : layout.binds)
|
||||
{
|
||||
m_descriptors.emplace_back();
|
||||
m_descriptors.back().type = type;
|
||||
m_descriptors.back().binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
void DescriptorSet::SetImage(std::size_t i, std::uint32_t binding, class Image& image)
|
||||
{
|
||||
Verify(m_sets[i] != VK_NULL_HANDLE, "invalid descriptor");
|
||||
auto it = std::find_if(m_descriptors.begin(), m_descriptors.end(), [=](Descriptor descriptor)
|
||||
{
|
||||
return binding == descriptor.binding;
|
||||
});
|
||||
if(it == m_descriptors.end())
|
||||
{
|
||||
Warning("Vulkan: cannot update descriptor set image; invalid binding");
|
||||
return;
|
||||
}
|
||||
if(it->type != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
|
||||
{
|
||||
Error("Vulkan: trying to bind an image to the wrong descriptor");
|
||||
return;
|
||||
}
|
||||
it->image_ptr = ℑ
|
||||
}
|
||||
|
||||
void DescriptorSet::SetStorageBuffer(std::size_t i, std::uint32_t binding, class GPUBuffer& buffer)
|
||||
{
|
||||
Verify(m_sets[i] != VK_NULL_HANDLE, "invalid descriptor");
|
||||
auto it = std::find_if(m_descriptors.begin(), m_descriptors.end(), [=](Descriptor descriptor)
|
||||
{
|
||||
return binding == descriptor.binding;
|
||||
});
|
||||
if(it == m_descriptors.end())
|
||||
{
|
||||
Warning("Vulkan: cannot update descriptor set buffer; invalid binding");
|
||||
return;
|
||||
}
|
||||
if(it->type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
|
||||
{
|
||||
Error("Vulkan: trying to bind a buffer to the wrong descriptor");
|
||||
return;
|
||||
}
|
||||
it->storage_buffer_ptr = &buffer;
|
||||
}
|
||||
|
||||
void DescriptorSet::SetUniformBuffer(std::size_t i, std::uint32_t binding, class GPUBuffer& buffer)
|
||||
{
|
||||
Verify(m_sets[i] != VK_NULL_HANDLE, "invalid descriptor");
|
||||
auto it = std::find_if(m_descriptors.begin(), m_descriptors.end(), [=](Descriptor descriptor)
|
||||
{
|
||||
return binding == descriptor.binding;
|
||||
});
|
||||
if(it == m_descriptors.end())
|
||||
{
|
||||
Warning("Vulkan: cannot update descriptor set buffer; invalid binding");
|
||||
return;
|
||||
}
|
||||
if(it->type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
|
||||
{
|
||||
Error("Vulkan: trying to bind a buffer to the wrong descriptor");
|
||||
return;
|
||||
}
|
||||
it->uniform_buffer_ptr = &buffer;
|
||||
}
|
||||
|
||||
void DescriptorSet::Update(std::size_t i, VkCommandBuffer cmd) noexcept
|
||||
{
|
||||
Verify(m_sets[i] != VK_NULL_HANDLE, "invalid descriptor");
|
||||
|
||||
std::size_t image_count = 0;
|
||||
std::size_t buffer_count = 0;
|
||||
|
||||
for(auto& descriptor : m_descriptors)
|
||||
{
|
||||
if(descriptor.image_ptr)
|
||||
image_count++;
|
||||
else if(descriptor.uniform_buffer_ptr || descriptor.storage_buffer_ptr)
|
||||
buffer_count++;
|
||||
else
|
||||
FatalError("unknown descriptor data");
|
||||
}
|
||||
|
||||
std::vector<VkWriteDescriptorSet> writes(m_descriptors.size());
|
||||
std::vector<VkDescriptorBufferInfo> buffer_infos(buffer_count);
|
||||
std::vector<VkDescriptorImageInfo> image_infos(image_count);
|
||||
std::size_t buffer_index = 0;
|
||||
std::size_t image_index = 0;
|
||||
std::size_t write_index = 0;
|
||||
|
||||
for(auto& descriptor : m_descriptors)
|
||||
{
|
||||
if(descriptor.image_ptr)
|
||||
{
|
||||
TransitionImageToCorrectLayout(*descriptor.image_ptr, cmd);
|
||||
VkDescriptorImageInfo info{};
|
||||
info.sampler = descriptor.image_ptr->GetSampler();
|
||||
info.imageLayout = descriptor.image_ptr->GetLayout();
|
||||
info.imageView = descriptor.image_ptr->GetImageView();
|
||||
image_infos[image_index] = std::move(info);
|
||||
writes[write_index] = kvfWriteImageToDescriptorSet(RenderCore::Get().GetDevice(), m_sets[i], &image_infos[image_index], descriptor.binding);
|
||||
image_index++;
|
||||
}
|
||||
else if(descriptor.uniform_buffer_ptr)
|
||||
{
|
||||
VkDescriptorBufferInfo info{};
|
||||
info.buffer = descriptor.uniform_buffer_ptr->Get();
|
||||
info.offset = descriptor.uniform_buffer_ptr->GetOffset();
|
||||
info.range = VK_WHOLE_SIZE;
|
||||
buffer_infos[buffer_index] = std::move(info);
|
||||
writes[write_index] = kvfWriteUniformBufferToDescriptorSet(RenderCore::Get().GetDevice(), m_sets[i], &buffer_infos[buffer_index], descriptor.binding);
|
||||
buffer_index++;
|
||||
}
|
||||
else if(descriptor.storage_buffer_ptr)
|
||||
{
|
||||
VkDescriptorBufferInfo info{};
|
||||
info.buffer = descriptor.storage_buffer_ptr->Get();
|
||||
info.offset = descriptor.storage_buffer_ptr->GetOffset();
|
||||
info.range = VK_WHOLE_SIZE;
|
||||
buffer_infos[buffer_index] = std::move(info);
|
||||
writes[write_index] = kvfWriteStorageBufferToDescriptorSet(RenderCore::Get().GetDevice(), m_sets[i], &buffer_infos[buffer_index], descriptor.binding);
|
||||
buffer_index++;
|
||||
}
|
||||
write_index++;
|
||||
}
|
||||
RenderCore::Get().vkUpdateDescriptorSets(RenderCore::Get().GetDevice(), writes.size(), writes.data(), 0, nullptr);
|
||||
}
|
||||
|
||||
void DescriptorSet::ReturnDescriptorSetToPool()
|
||||
{
|
||||
m_pool.ReturnDescriptorSet(shared_from_this());
|
||||
}
|
||||
}
|
||||
265
Runtime/Sources/Renderer/Image.cpp
git.filemode.normal_file
265
Runtime/Sources/Renderer/Image.cpp
git.filemode.normal_file
@@ -0,0 +1,265 @@
|
||||
#include <Renderer/Image.h>
|
||||
#include <Renderer/RenderCore.h>
|
||||
#include <Core/Logs.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
void Image::Init(ImageType type, std::uint32_t width, std::uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, bool is_multisampled, std::string_view name, bool dedicated_alloc)
|
||||
{
|
||||
m_type = type;
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_format = format;
|
||||
m_tiling = tiling;
|
||||
m_is_multisampled = is_multisampled;
|
||||
|
||||
KvfImageType kvf_type = KVF_IMAGE_OTHER;
|
||||
switch(m_type)
|
||||
{
|
||||
case ImageType::Color: kvf_type = KVF_IMAGE_COLOR; break;
|
||||
case ImageType::Depth: kvf_type = KVF_IMAGE_DEPTH; break;
|
||||
case ImageType::Cube: kvf_type = KVF_IMAGE_CUBE; break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
if(m_is_multisampled)
|
||||
{
|
||||
VkImageCreateInfo image_info{};
|
||||
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
image_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
image_info.extent.width = width;
|
||||
image_info.extent.height = height;
|
||||
image_info.extent.depth = 1;
|
||||
image_info.mipLevels = 1;
|
||||
image_info.arrayLayers = (m_type == ImageType::Cube ? 6 : 1);
|
||||
image_info.format = format;
|
||||
image_info.tiling = tiling;
|
||||
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
image_info.usage = usage;
|
||||
image_info.samples = VK_SAMPLE_COUNT_4_BIT;
|
||||
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
kvfCheckVk(RenderCore::Get().vkCreateImage(RenderCore::Get().GetDevice(), &image_info, nullptr, &m_image));
|
||||
}
|
||||
else
|
||||
m_image = kvfCreateImage(RenderCore::Get().GetDevice(), width, height, format, tiling, usage, kvf_type);
|
||||
|
||||
VkMemoryRequirements mem_requirements;
|
||||
RenderCore::Get().vkGetImageMemoryRequirements(RenderCore::Get().GetDevice(), m_image, &mem_requirements);
|
||||
|
||||
m_memory = RenderCore::Get().GetAllocator().Allocate(mem_requirements.size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties), dedicated_alloc);
|
||||
RenderCore::Get().vkBindImageMemory(RenderCore::Get().GetDevice(), m_image, m_memory.memory, m_memory.offset);
|
||||
|
||||
#ifdef SCOP_HAS_DEBUG_UTILS_FUNCTIONS
|
||||
VkDebugUtilsObjectNameInfoEXT name_info{};
|
||||
name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
|
||||
name_info.objectType = VK_OBJECT_TYPE_IMAGE;
|
||||
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_image);
|
||||
name_info.pObjectName = name.data();
|
||||
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
|
||||
#endif
|
||||
|
||||
s_image_count++;
|
||||
}
|
||||
|
||||
void Image::CreateImageView(VkImageViewType type, VkImageAspectFlags aspect_flags, int layer_count) noexcept
|
||||
{
|
||||
m_image_view = kvfCreateImageView(RenderCore::Get().GetDevice(), m_image, m_format, type, aspect_flags, layer_count);
|
||||
}
|
||||
|
||||
void Image::CreateSampler() noexcept
|
||||
{
|
||||
m_sampler = kvfCreateSampler(RenderCore::Get().GetDevice(), VK_FILTER_NEAREST, VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_MIPMAP_MODE_NEAREST);
|
||||
}
|
||||
|
||||
void Image::TransitionLayout(VkImageLayout new_layout, VkCommandBuffer cmd)
|
||||
{
|
||||
if(new_layout == m_layout)
|
||||
return;
|
||||
bool is_single_time_cmd_buffer = (cmd == VK_NULL_HANDLE);
|
||||
KvfImageType kvf_type = KVF_IMAGE_OTHER;
|
||||
switch(m_type)
|
||||
{
|
||||
case ImageType::Color: kvf_type = KVF_IMAGE_COLOR; break;
|
||||
case ImageType::Depth: kvf_type = KVF_IMAGE_DEPTH; break;
|
||||
case ImageType::Cube: kvf_type = KVF_IMAGE_CUBE; break;
|
||||
default: break;
|
||||
}
|
||||
if(is_single_time_cmd_buffer)
|
||||
cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
|
||||
kvfTransitionImageLayout(RenderCore::Get().GetDevice(), m_image, kvf_type, cmd, m_format, m_layout, new_layout, is_single_time_cmd_buffer);
|
||||
if(is_single_time_cmd_buffer)
|
||||
kvfDestroyCommandBuffer(RenderCore::Get().GetDevice(), cmd);
|
||||
m_layout = new_layout;
|
||||
}
|
||||
|
||||
void Image::Clear(VkCommandBuffer cmd, Vec4f color)
|
||||
{
|
||||
VkImageSubresourceRange subresource_range{};
|
||||
subresource_range.baseMipLevel = 0;
|
||||
subresource_range.layerCount = (m_type == ImageType::Cube ? 6 : 1);
|
||||
subresource_range.levelCount = 1;
|
||||
subresource_range.baseArrayLayer = 0;
|
||||
|
||||
if(m_type == ImageType::Color || m_type == ImageType::Cube)
|
||||
{
|
||||
VkImageLayout old_layout = m_layout;
|
||||
subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cmd);
|
||||
VkClearColorValue clear_color = VkClearColorValue({ { color.x, color.y, color.z, color.w } });
|
||||
RenderCore::Get().vkCmdClearColorImage(cmd, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range);
|
||||
TransitionLayout(old_layout, cmd);
|
||||
}
|
||||
else if(m_type == ImageType::Depth)
|
||||
{
|
||||
VkClearDepthStencilValue clear_depth_stencil = { 1.0f, 1 };
|
||||
subresource_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cmd);
|
||||
RenderCore::Get().vkCmdClearDepthStencilImage(cmd, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_depth_stencil, 1, &subresource_range);
|
||||
TransitionLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void Image::DestroySampler() noexcept
|
||||
{
|
||||
if(m_sampler != VK_NULL_HANDLE)
|
||||
kvfDestroySampler(RenderCore::Get().GetDevice(), m_sampler);
|
||||
m_sampler = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void Image::DestroyImageView() noexcept
|
||||
{
|
||||
if(m_image_view != VK_NULL_HANDLE)
|
||||
kvfDestroyImageView(RenderCore::Get().GetDevice(), m_image_view);
|
||||
m_image_view = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void Image::Destroy() noexcept
|
||||
{
|
||||
if(m_image == VK_NULL_HANDLE && m_image_view == VK_NULL_HANDLE && m_sampler == VK_NULL_HANDLE)
|
||||
return;
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
DestroySampler();
|
||||
DestroyImageView();
|
||||
|
||||
if(m_image != VK_NULL_HANDLE)
|
||||
{
|
||||
RenderCore::Get().GetAllocator().Deallocate(m_memory);
|
||||
m_memory = NULL_MEMORY_BLOCK;
|
||||
kvfDestroyImage(RenderCore::Get().GetDevice(), m_image);
|
||||
}
|
||||
m_image = VK_NULL_HANDLE;
|
||||
m_memory = NULL_MEMORY_BLOCK;
|
||||
m_image = VK_NULL_HANDLE;
|
||||
m_image_view = VK_NULL_HANDLE;
|
||||
m_sampler = VK_NULL_HANDLE;
|
||||
m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
m_width = 0;
|
||||
m_height = 0;
|
||||
m_is_multisampled = false;
|
||||
s_image_count--;
|
||||
}
|
||||
|
||||
void CubeTexture::Init(CPUBuffer pixels, std::uint32_t width, std::uint32_t height, VkFormat format, std::string_view name)
|
||||
{
|
||||
if(!pixels)
|
||||
FatalError("Vulkan: a cubemap cannot be created without pixels data");
|
||||
|
||||
std::array<std::vector<std::uint8_t>, 6> texture_data;
|
||||
std::uint32_t face_width = width / 4;
|
||||
std::uint32_t face_height = height / 3;
|
||||
|
||||
std::size_t size = 0;
|
||||
|
||||
for(std::uint32_t cy = 0, face = 0; cy < 3; cy++)
|
||||
{
|
||||
for(std::uint32_t cx = 0; cx < 4; cx++)
|
||||
{
|
||||
if(cx == 0 || cx == 2 || cx == 3)
|
||||
{
|
||||
if(cy != 1)
|
||||
continue;
|
||||
}
|
||||
|
||||
texture_data[face] = std::vector<std::uint8_t>(face_width * face_height * sizeof(std::uint32_t));
|
||||
|
||||
size += sizeof(std::uint32_t) * width * height;
|
||||
|
||||
for(std::uint32_t y = 0; y < face_height; y++)
|
||||
{
|
||||
std::uint32_t offset = y;
|
||||
std::uint32_t yp = cy * face_height + offset;
|
||||
for(std::uint32_t x = 0; x < face_width; x++)
|
||||
{
|
||||
offset = x;
|
||||
std::uint32_t xp = cx * face_width + offset;
|
||||
texture_data[face][(x + y * face_width) * sizeof(std::uint32_t) + 0] = pixels.GetData()[(xp + yp * width) * sizeof(std::uint32_t) + 0];
|
||||
texture_data[face][(x + y * face_width) * sizeof(std::uint32_t) + 1] = pixels.GetData()[(xp + yp * width) * sizeof(std::uint32_t) + 1];
|
||||
texture_data[face][(x + y * face_width) * sizeof(std::uint32_t) + 2] = pixels.GetData()[(xp + yp * width) * sizeof(std::uint32_t) + 2];
|
||||
texture_data[face][(x + y * face_width) * sizeof(std::uint32_t) + 3] = pixels.GetData()[(xp + yp * width) * sizeof(std::uint32_t) + 3];
|
||||
}
|
||||
}
|
||||
face++;
|
||||
}
|
||||
}
|
||||
|
||||
CPUBuffer complete_data(size);
|
||||
std::uint32_t pointer_offset = 0;
|
||||
|
||||
const std::uint32_t face_order[6] = { 3, 1, 0, 5, 2, 4 };
|
||||
|
||||
for(std::uint32_t face : face_order)
|
||||
{
|
||||
std::size_t current_size = face_width * face_height * sizeof(std::uint32_t);
|
||||
std::memcpy(complete_data.GetData() + pointer_offset, texture_data[face].data(), current_size);
|
||||
pointer_offset += current_size;
|
||||
}
|
||||
|
||||
Image::Init(ImageType::Cube, face_width, face_height, format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, false, std::move(name));
|
||||
Image::CreateImageView(VK_IMAGE_VIEW_TYPE_CUBE, VK_IMAGE_ASPECT_COLOR_BIT, 6);
|
||||
Image::CreateSampler();
|
||||
|
||||
GPUBuffer staging_buffer;
|
||||
staging_buffer.Init(BufferType::Staging, complete_data.GetSize(), VK_BUFFER_USAGE_TRANSFER_SRC_BIT, complete_data);
|
||||
std::vector<VkBufferImageCopy> buffer_copy_regions;
|
||||
std::uint32_t offset = 0;
|
||||
|
||||
for(std::uint32_t face = 0; face < 6; face++)
|
||||
{
|
||||
VkBufferImageCopy buffer_copy_region{};
|
||||
buffer_copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
buffer_copy_region.imageSubresource.mipLevel = 0;
|
||||
buffer_copy_region.imageSubresource.baseArrayLayer = face;
|
||||
buffer_copy_region.imageSubresource.layerCount = 1;
|
||||
buffer_copy_region.imageExtent.width = face_width;
|
||||
buffer_copy_region.imageExtent.height = face_height;
|
||||
buffer_copy_region.imageExtent.depth = 1;
|
||||
buffer_copy_region.bufferOffset = offset;
|
||||
buffer_copy_regions.push_back(buffer_copy_region);
|
||||
|
||||
offset += face_width * face_height * kvfFormatSize(format);
|
||||
}
|
||||
|
||||
VkImageSubresourceRange subresource_range{};
|
||||
subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
subresource_range.baseMipLevel = 0;
|
||||
subresource_range.levelCount = 1;
|
||||
subresource_range.layerCount = 6;
|
||||
|
||||
auto device = RenderCore::Get().GetDevice();
|
||||
|
||||
VkCommandBuffer cmd = kvfCreateCommandBuffer(device);
|
||||
kvfBeginCommandBuffer(cmd, 0);
|
||||
TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cmd);
|
||||
RenderCore::Get().vkCmdCopyBufferToImage(cmd, staging_buffer.Get(), Image::Get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, buffer_copy_regions.size(), buffer_copy_regions.data());
|
||||
TransitionLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, cmd);
|
||||
kvfEndCommandBuffer(cmd);
|
||||
|
||||
VkFence fence = kvfCreateFence(device);
|
||||
kvfSubmitSingleTimeCommandBuffer(device, cmd, KVF_GRAPHICS_QUEUE, fence);
|
||||
kvfWaitForFence(device, fence);
|
||||
kvfDestroyFence(device, fence);
|
||||
|
||||
staging_buffer.Destroy();
|
||||
}
|
||||
}
|
||||
110
Runtime/Sources/Renderer/Memory/Chunk.cpp
git.filemode.normal_file
110
Runtime/Sources/Renderer/Memory/Chunk.cpp
git.filemode.normal_file
@@ -0,0 +1,110 @@
|
||||
#include <Renderer/Memory/Chunk.h>
|
||||
#include <Renderer/RenderCore.h>
|
||||
#include <Core/EventBus.h>
|
||||
#include <Core/Logs.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
struct MemoryChunkAllocFailedEvent : public EventBase
|
||||
{
|
||||
Event What() const override { return Event::MemoryChunkAllocationFailed; }
|
||||
};
|
||||
}
|
||||
|
||||
MemoryChunk::MemoryChunk(VkDevice device, VkPhysicalDevice physical, VkDeviceSize size, std::int32_t memory_type_index, bool is_dedicated, std::uint32_t& vram_usage, std::uint32_t& vram_host_visible_usage)
|
||||
: m_device(device), m_physical(physical), m_size(size), m_memory_type_index(memory_type_index), m_is_dedicated(is_dedicated)
|
||||
{
|
||||
Verify(device != VK_NULL_HANDLE, "Memory Chunk : invalid device");
|
||||
VkMemoryAllocateInfo alloc_info{};
|
||||
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
alloc_info.allocationSize = size;
|
||||
alloc_info.memoryTypeIndex = m_memory_type_index;
|
||||
if(RenderCore::Get().vkAllocateMemory(m_device, &alloc_info, nullptr, &m_memory) != VK_SUCCESS)
|
||||
{
|
||||
EventBus::Send("__ScopDeviceAllocator", Internal::MemoryChunkAllocFailedEvent{});
|
||||
return;
|
||||
}
|
||||
|
||||
VkPhysicalDeviceMemoryProperties properties;
|
||||
RenderCore::Get().vkGetPhysicalDeviceMemoryProperties(m_physical, &properties);
|
||||
if((properties.memoryTypes[m_memory_type_index].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
|
||||
{
|
||||
if(RenderCore::Get().vkMapMemory(m_device, m_memory, 0, VK_WHOLE_SIZE, 0, &p_map) != VK_SUCCESS)
|
||||
FatalError("Vulkan: failed to map a host visible chunk");
|
||||
vram_host_visible_usage += size;
|
||||
}
|
||||
else
|
||||
vram_usage += size;
|
||||
|
||||
MemoryBlock& block = m_blocks.emplace_back();
|
||||
block.memory = m_memory;
|
||||
block.offset = 0;
|
||||
block.size = size;
|
||||
block.free = true;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<MemoryBlock> MemoryChunk::Allocate(VkDeviceSize size, VkDeviceSize alignment)
|
||||
{
|
||||
for(std::size_t i = 0; i < m_blocks.size(); i++)
|
||||
{
|
||||
if(!m_blocks[i].free || m_blocks[i].size < size)
|
||||
continue;
|
||||
VkDeviceSize offset_displacement = (m_blocks[i].offset % alignment != 0) ? alignment - m_blocks[i].offset % alignment : 0;
|
||||
VkDeviceSize old_size_available = m_blocks[i].size - offset_displacement;
|
||||
|
||||
if(size + offset_displacement <= m_blocks[i].size)
|
||||
{
|
||||
m_blocks[i].offset += offset_displacement;
|
||||
m_blocks[i].size = size;
|
||||
m_blocks[i].free = false;
|
||||
|
||||
if(p_map != nullptr)
|
||||
m_blocks[i].map = reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(p_map) + m_blocks[i].offset);
|
||||
|
||||
MemoryBlock new_block;
|
||||
new_block.memory = m_memory;
|
||||
new_block.offset = m_blocks[i].offset + m_blocks[i].size;
|
||||
new_block.size = old_size_available - size;
|
||||
new_block.free = true;
|
||||
|
||||
if(new_block.size > 0)
|
||||
m_blocks.emplace(m_blocks.begin() + i + 1, new_block);
|
||||
return m_blocks[i];
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void MemoryChunk::Deallocate(const MemoryBlock& block)
|
||||
{
|
||||
auto it = std::find(m_blocks.begin(), m_blocks.end(), block);
|
||||
if(it == m_blocks.end())
|
||||
FatalError("Memory Chunk : cannot deallocate a block that is owned by another chunk");
|
||||
it->free = true;
|
||||
|
||||
bool end = false;
|
||||
while(!end)
|
||||
{
|
||||
end = true;
|
||||
for(auto it = m_blocks.begin(); it != m_blocks.end(); ++it)
|
||||
{
|
||||
if(it->free && it + 1 != m_blocks.end() && (it + 1)->free)
|
||||
{
|
||||
it->size += (it + 1)->size;
|
||||
m_blocks.erase(it + 1);
|
||||
end = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MemoryChunk::~MemoryChunk()
|
||||
{
|
||||
RenderCore::Get().vkFreeMemory(m_device, m_memory, nullptr);
|
||||
}
|
||||
}
|
||||
112
Runtime/Sources/Renderer/Memory/DeviceAllocator.cpp
git.filemode.normal_file
112
Runtime/Sources/Renderer/Memory/DeviceAllocator.cpp
git.filemode.normal_file
@@ -0,0 +1,112 @@
|
||||
#include <Renderer/Memory/DeviceAllocator.h>
|
||||
#include <Renderer/RenderCore.h>
|
||||
#include <Maths/Constants.h>
|
||||
#include <Core/Logs.h>
|
||||
#include <Core/EventBus.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
#define AlignUp(val, alignment) ((val + alignment - 1) & ~(alignment - 1))
|
||||
|
||||
void DeviceAllocator::AttachToDevice(VkDevice device, VkPhysicalDevice physical) noexcept
|
||||
{
|
||||
m_device = device;
|
||||
m_physical = physical;
|
||||
|
||||
RenderCore::Get().vkGetPhysicalDeviceMemoryProperties(physical, &m_mem_props);
|
||||
|
||||
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
|
||||
{
|
||||
if(event.What() == Event::MemoryChunkAllocationFailed)
|
||||
m_last_chunk_creation_failed = true;
|
||||
};
|
||||
EventBus::RegisterListener({ functor, "__ScopDeviceAllocator" });
|
||||
}
|
||||
|
||||
[[nodiscard]] MemoryBlock DeviceAllocator::Allocate(VkDeviceSize size, VkDeviceSize alignment, std::int32_t memory_type_index, bool dedicated_chunk)
|
||||
{
|
||||
Verify(m_device != VK_NULL_HANDLE, "invalid device");
|
||||
Verify(m_physical != VK_NULL_HANDLE, "invalid physical device");
|
||||
const std::lock_guard<std::mutex> guard(m_alloc_mutex);
|
||||
if(!dedicated_chunk)
|
||||
{
|
||||
for(auto& chunk : m_chunks)
|
||||
{
|
||||
if(chunk->GetMemoryTypeIndex() == memory_type_index)
|
||||
{
|
||||
std::optional<MemoryBlock> block = chunk->Allocate(size, alignment);
|
||||
if(block.has_value())
|
||||
return *block;
|
||||
}
|
||||
}
|
||||
}
|
||||
VkDeviceSize chunk_size = dedicated_chunk ? size + alignment : CalcPreferredChunkSize(memory_type_index);
|
||||
if(chunk_size < size + alignment)
|
||||
chunk_size = size + alignment;
|
||||
m_chunks.emplace_back(std::make_unique<MemoryChunk>(m_device, m_physical, chunk_size, memory_type_index, dedicated_chunk, m_vram_usage, m_vram_host_visible_usage));
|
||||
|
||||
if(m_last_chunk_creation_failed && !dedicated_chunk)
|
||||
{
|
||||
// Allocation of this size failed? Try 1/2, 1/4, 1/8 of preferred chunk size.
|
||||
std::uint32_t new_block_size_shift = 0;
|
||||
while(m_last_chunk_creation_failed && new_block_size_shift < NEW_BLOCK_SIZE_SHIFT_MAX)
|
||||
{
|
||||
m_last_chunk_creation_failed = false;
|
||||
m_chunks.pop_back();
|
||||
chunk_size /= 2;
|
||||
if(chunk_size < size + alignment)
|
||||
{
|
||||
m_last_chunk_creation_failed = true;
|
||||
break;
|
||||
}
|
||||
m_chunks.emplace_back(std::make_unique<MemoryChunk>(m_device, m_physical, chunk_size, memory_type_index, false, m_vram_usage, m_vram_host_visible_usage));
|
||||
}
|
||||
}
|
||||
|
||||
// If we could not recover from allocation failure
|
||||
if(m_last_chunk_creation_failed)
|
||||
FatalError("Device Allocator: could not allocate a memory chunk");
|
||||
|
||||
std::optional<MemoryBlock> block = m_chunks.back()->Allocate(size, alignment);
|
||||
m_allocations_count++;
|
||||
if(block.has_value())
|
||||
return *block;
|
||||
FatalError("Device Allocator: could not allocate a memory block");
|
||||
return {}; // to avoid warnings
|
||||
}
|
||||
|
||||
void DeviceAllocator::Deallocate(const MemoryBlock& block)
|
||||
{
|
||||
Verify(m_device != VK_NULL_HANDLE, "invalid device");
|
||||
Verify(m_physical != VK_NULL_HANDLE, "invalid physical device");
|
||||
const std::lock_guard<std::mutex> guard(m_dealloc_mutex);
|
||||
for(auto it = m_chunks.begin(); it != m_chunks.end(); ++it)
|
||||
{
|
||||
if((*it)->Has(block))
|
||||
{
|
||||
(*it)->Deallocate(block);
|
||||
if((*it)->IsDedicated())
|
||||
{
|
||||
if((*it)->GetMap() != nullptr) // If it is host visible
|
||||
m_vram_host_visible_usage -= (*it)->GetSize();
|
||||
else
|
||||
m_vram_usage -= (*it)->GetSize();
|
||||
m_chunks.erase(it);
|
||||
m_allocations_count--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
Error("Device Allocator: unable to free a block; could not find it's chunk");
|
||||
}
|
||||
|
||||
VkDeviceSize DeviceAllocator::CalcPreferredChunkSize(std::uint32_t mem_type_index)
|
||||
{
|
||||
std::uint32_t heap_index = m_mem_props.memoryTypes[mem_type_index].heapIndex;
|
||||
VkDeviceSize heap_size = m_mem_props.memoryHeaps[heap_index].size;
|
||||
bool is_small_heap = heap_size <= SMALL_HEAP_MAX_SIZE;
|
||||
return AlignUp((is_small_heap ? (heap_size / 8) : DEFAULT_LARGE_HEAP_BLOCK_SIZE), (VkDeviceSize)32);
|
||||
}
|
||||
}
|
||||
236
Runtime/Sources/Renderer/Pipelines/Graphics.cpp
git.filemode.normal_file
236
Runtime/Sources/Renderer/Pipelines/Graphics.cpp
git.filemode.normal_file
@@ -0,0 +1,236 @@
|
||||
#include <Renderer/Pipelines/Graphics.h>
|
||||
#include <Renderer/RenderCore.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Renderer/Vertex.h>
|
||||
#include <Graphics/Enums.h>
|
||||
#include <Core/EventBus.h>
|
||||
#include <Core/Logs.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
void GraphicPipeline::Init(GraphicPipelineDescriptor descriptor)
|
||||
{
|
||||
Setup(std::move(descriptor));
|
||||
|
||||
m_description.vertex_shader->SetPipelineInUse(this);
|
||||
m_description.fragment_shader->SetPipelineInUse(this);
|
||||
|
||||
std::vector<VkPushConstantRange> push_constants;
|
||||
std::vector<VkDescriptorSetLayout> set_layouts;
|
||||
push_constants.insert(push_constants.end(), m_description.vertex_shader->GetPipelineLayout().push_constants.begin(), m_description.vertex_shader->GetPipelineLayout().push_constants.end());
|
||||
push_constants.insert(push_constants.end(), m_description.fragment_shader->GetPipelineLayout().push_constants.begin(), m_description.fragment_shader->GetPipelineLayout().push_constants.end());
|
||||
set_layouts.insert(set_layouts.end(), m_description.vertex_shader->GetPipelineLayout().set_layouts.begin(), m_description.vertex_shader->GetPipelineLayout().set_layouts.end());
|
||||
set_layouts.insert(set_layouts.end(), m_description.fragment_shader->GetPipelineLayout().set_layouts.begin(), m_description.fragment_shader->GetPipelineLayout().set_layouts.end());
|
||||
m_pipeline_layout = kvfCreatePipelineLayout(RenderCore::Get().GetDevice(), set_layouts.data(), set_layouts.size(), push_constants.data(), push_constants.size());
|
||||
|
||||
CreateFramebuffers(m_description.color_attachments, m_description.clear_color_attachments);
|
||||
|
||||
VkPhysicalDeviceFeatures features{};
|
||||
RenderCore::Get().vkGetPhysicalDeviceFeatures(RenderCore::Get().GetPhysicalDevice(), &features);
|
||||
|
||||
VkCullModeFlags cullmode;
|
||||
switch(m_description.culling)
|
||||
{
|
||||
case CullMode::None: cullmode = VK_CULL_MODE_NONE; break;
|
||||
case CullMode::Back: cullmode = VK_CULL_MODE_BACK_BIT; break;
|
||||
case CullMode::Front: cullmode = VK_CULL_MODE_FRONT_BIT; break;
|
||||
case CullMode::FrontAndBack: cullmode = VK_CULL_MODE_FRONT_AND_BACK; break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
KvfGraphicsPipelineBuilder* builder = kvfCreateGPipelineBuilder();
|
||||
kvfGPipelineBuilderAddShaderStage(builder, m_description.vertex_shader->GetShaderStage(), m_description.vertex_shader->GetShaderModule(), "main");
|
||||
kvfGPipelineBuilderAddShaderStage(builder, m_description.fragment_shader->GetShaderStage(), m_description.fragment_shader->GetShaderModule(), "main");
|
||||
kvfGPipelineBuilderSetInputTopology(builder, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
|
||||
kvfGPipelineBuilderSetCullMode(builder, cullmode, VK_FRONT_FACE_CLOCKWISE);
|
||||
kvfGPipelineBuilderEnableAlphaBlending(builder);
|
||||
if(m_description.depth)
|
||||
kvfGPipelineBuilderEnableDepthTest(builder, (m_description.depth_test_equal ? VK_COMPARE_OP_EQUAL : VK_COMPARE_OP_LESS), true);
|
||||
else
|
||||
kvfGPipelineBuilderDisableDepthTest(builder);
|
||||
if(m_description.wireframe && features.fillModeNonSolid)
|
||||
kvfGPipelineBuilderSetPolygonMode(builder, VK_POLYGON_MODE_LINE, 1.0f);
|
||||
else
|
||||
kvfGPipelineBuilderSetPolygonMode(builder, VK_POLYGON_MODE_FILL, 1.0f);
|
||||
if(features.sampleRateShading)
|
||||
kvfGPipelineBuilderSetMultisamplingShading(builder, VK_SAMPLE_COUNT_1_BIT, 0.25f);
|
||||
else
|
||||
kvfGPipelineBuilderSetMultisampling(builder, VK_SAMPLE_COUNT_1_BIT);
|
||||
|
||||
if(!m_description.no_vertex_inputs)
|
||||
{
|
||||
VkVertexInputBindingDescription binding_description = Vertex::GetBindingDescription();
|
||||
auto attributes_description = Vertex::GetAttributeDescriptions();
|
||||
kvfGPipelineBuilderSetVertexInputs(builder, binding_description, attributes_description.data(), attributes_description.size());
|
||||
}
|
||||
|
||||
m_pipeline = kvfCreateGraphicsPipeline(RenderCore::Get().GetDevice(), VK_NULL_HANDLE, m_pipeline_layout, builder, m_renderpass);
|
||||
kvfDestroyGPipelineBuilder(builder);
|
||||
|
||||
#ifdef SCOP_HAS_DEBUG_UTILS_FUNCTIONS
|
||||
VkDebugUtilsObjectNameInfoEXT name_info{};
|
||||
name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
|
||||
name_info.objectType = VK_OBJECT_TYPE_PIPELINE;
|
||||
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_pipeline);
|
||||
name_info.pObjectName = m_description.name.data();
|
||||
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
|
||||
|
||||
name_info.objectType = VK_OBJECT_TYPE_RENDER_PASS;
|
||||
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_renderpass);
|
||||
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
|
||||
|
||||
name_info.objectType = VK_OBJECT_TYPE_SHADER_MODULE;
|
||||
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_description.vertex_shader->GetShaderModule());
|
||||
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
|
||||
|
||||
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_description.fragment_shader->GetShaderModule());
|
||||
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
|
||||
|
||||
name_info.objectType = VK_OBJECT_TYPE_FRAMEBUFFER;
|
||||
for(VkFramebuffer fb : m_framebuffers)
|
||||
{
|
||||
name_info.objectHandle = reinterpret_cast<std::uint64_t>(fb);
|
||||
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
|
||||
}
|
||||
Message("Vulkan: % graphics pipeline created", m_description.name);
|
||||
#else
|
||||
Message("Vulkan: graphics pipeline created");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GraphicPipeline::BindPipeline(VkCommandBuffer command_buffer, std::size_t framebuffer_index, std::array<float, 4> clear) noexcept
|
||||
{
|
||||
if(s_bound_pipeline != nullptr)
|
||||
{
|
||||
Error("Vulkan: cannot bind a graphics pipeline because another one have not been unbound");
|
||||
return false;
|
||||
}
|
||||
|
||||
s_bound_pipeline = this;
|
||||
TransitionAttachments(command_buffer);
|
||||
|
||||
VkFramebuffer fb = m_framebuffers[framebuffer_index];
|
||||
VkExtent2D fb_extent = kvfGetFramebufferSize(fb);
|
||||
|
||||
VkViewport viewport{};
|
||||
viewport.x = 0.0f;
|
||||
viewport.y = 0.0f;
|
||||
viewport.width = fb_extent.width;
|
||||
viewport.height = fb_extent.height;
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
RenderCore::Get().vkCmdSetViewport(command_buffer, 0, 1, &viewport);
|
||||
|
||||
VkRect2D scissor{};
|
||||
scissor.offset = { 0, 0 };
|
||||
scissor.extent = fb_extent;
|
||||
RenderCore::Get().vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
||||
|
||||
for(int i = 0; i < m_clears.size(); i++)
|
||||
{
|
||||
m_clears[i].color.float32[0] = clear[0];
|
||||
m_clears[i].color.float32[1] = clear[1];
|
||||
m_clears[i].color.float32[2] = clear[2];
|
||||
m_clears[i].color.float32[3] = clear[3];
|
||||
}
|
||||
|
||||
if(m_description.depth)
|
||||
m_clears.back().depthStencil = VkClearDepthStencilValue{ 1.0f, 0 };
|
||||
|
||||
kvfBeginRenderPass(m_renderpass, command_buffer, fb, fb_extent, m_clears.data(), m_clears.size());
|
||||
RenderCore::Get().vkCmdBindPipeline(command_buffer, GetPipelineBindPoint(), GetPipeline());
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphicPipeline::EndPipeline(VkCommandBuffer command_buffer) noexcept
|
||||
{
|
||||
if(s_bound_pipeline != this)
|
||||
return;
|
||||
RenderCore::Get().vkCmdEndRenderPass(command_buffer);
|
||||
s_bound_pipeline = nullptr;
|
||||
}
|
||||
|
||||
void GraphicPipeline::Destroy() noexcept
|
||||
{
|
||||
if(m_pipeline == VK_NULL_HANDLE)
|
||||
return;
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
|
||||
for(auto& fb : m_framebuffers)
|
||||
{
|
||||
kvfDestroyFramebuffer(RenderCore::Get().GetDevice(), fb);
|
||||
Message("Vulkan: framebuffer destroyed");
|
||||
}
|
||||
|
||||
kvfDestroyPipelineLayout(RenderCore::Get().GetDevice(), m_pipeline_layout);
|
||||
Message("Vulkan: graphics pipeline layout destroyed");
|
||||
kvfDestroyRenderPass(RenderCore::Get().GetDevice(), m_renderpass);
|
||||
Message("Vulkan: renderpass destroyed");
|
||||
kvfDestroyPipeline(RenderCore::Get().GetDevice(), m_pipeline);
|
||||
|
||||
m_description.vertex_shader.reset();
|
||||
m_description.fragment_shader.reset();
|
||||
m_description.color_attachments.clear();
|
||||
m_framebuffers.clear();
|
||||
m_clears.clear();
|
||||
m_renderpass = VK_NULL_HANDLE;
|
||||
m_pipeline = VK_NULL_HANDLE;
|
||||
m_pipeline_layout = VK_NULL_HANDLE;
|
||||
Message("Vulkan: graphics pipeline destroyed");
|
||||
}
|
||||
|
||||
void GraphicPipeline::CreateFramebuffers(const std::vector<NonOwningPtr<Texture>>& render_targets, bool clear_attachments)
|
||||
{
|
||||
TransitionAttachments();
|
||||
|
||||
std::vector<VkAttachmentDescription> attachments;
|
||||
std::vector<VkImageView> attachment_views;
|
||||
if(m_description.renderer)
|
||||
{
|
||||
attachments.push_back(kvfBuildSwapchainAttachmentDescription(m_description.renderer->GetSwapchain().Get(), clear_attachments));
|
||||
attachment_views.push_back(m_description.renderer->GetSwapchain().GetSwapchainImages()[0].GetImageView());
|
||||
}
|
||||
|
||||
for(NonOwningPtr<Texture> image : render_targets)
|
||||
{
|
||||
attachments.push_back(kvfBuildAttachmentDescription(KVF_IMAGE_COLOR, image->GetFormat(), image->GetLayout(), image->GetLayout(), clear_attachments, VK_SAMPLE_COUNT_1_BIT));
|
||||
attachment_views.push_back(image->GetImageView());
|
||||
}
|
||||
|
||||
if(m_description.depth)
|
||||
{
|
||||
attachments.push_back(kvfBuildAttachmentDescription(KVF_IMAGE_DEPTH, m_description.depth->GetFormat(), m_description.depth->GetLayout(), m_description.depth->GetLayout(), clear_attachments, VK_SAMPLE_COUNT_1_BIT));
|
||||
attachment_views.push_back(m_description.depth->GetImageView());
|
||||
}
|
||||
|
||||
m_renderpass = kvfCreateRenderPass(RenderCore::Get().GetDevice(), attachments.data(), attachments.size(), GetPipelineBindPoint());
|
||||
m_clears.clear();
|
||||
m_clears.resize(attachments.size());
|
||||
Message("Vulkan: renderpass created");
|
||||
|
||||
if(m_description.renderer)
|
||||
{
|
||||
for(const Image& image : m_description.renderer->GetSwapchain().GetSwapchainImages())
|
||||
{
|
||||
attachment_views[0] = image.GetImageView();
|
||||
m_framebuffers.push_back(kvfCreateFramebuffer(RenderCore::Get().GetDevice(), m_renderpass, attachment_views.data(), attachment_views.size(), { .width = image.GetWidth(), .height = image.GetHeight() }));
|
||||
Message("Vulkan: framebuffer created");
|
||||
}
|
||||
}
|
||||
for(NonOwningPtr<Texture> image : render_targets)
|
||||
{
|
||||
m_framebuffers.push_back(kvfCreateFramebuffer(RenderCore::Get().GetDevice(), m_renderpass, attachment_views.data(), attachment_views.size(), { .width = image->GetWidth(), .height = image->GetHeight() }));
|
||||
Message("Vulkan: framebuffer created");
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicPipeline::TransitionAttachments(VkCommandBuffer cmd)
|
||||
{
|
||||
if(m_description.depth)
|
||||
m_description.depth->TransitionLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, cmd);
|
||||
|
||||
for(NonOwningPtr<Texture> image : m_description.color_attachments)
|
||||
image->TransitionLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, cmd);
|
||||
}
|
||||
}
|
||||
102
Runtime/Sources/Renderer/Pipelines/Shader.cpp
git.filemode.normal_file
102
Runtime/Sources/Renderer/Pipelines/Shader.cpp
git.filemode.normal_file
@@ -0,0 +1,102 @@
|
||||
#include <Renderer/Pipelines/Shader.h>
|
||||
#include <Renderer/RenderCore.h>
|
||||
#include <Core/Logs.h>
|
||||
#include <fstream>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
Shader::Shader(const std::vector<std::uint32_t>& bytecode, ShaderType type, ShaderLayout layout, std::string name) : m_name(std::move(name)), m_bytecode(bytecode), m_layout(std::move(layout))
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ShaderType::Vertex : m_stage = VK_SHADER_STAGE_VERTEX_BIT; break;
|
||||
case ShaderType::Fragment : m_stage = VK_SHADER_STAGE_FRAGMENT_BIT; break;
|
||||
case ShaderType::Compute : m_stage = VK_SHADER_STAGE_COMPUTE_BIT; break;
|
||||
default : FatalError("wtf"); break;
|
||||
}
|
||||
m_module = kvfCreateShaderModule(RenderCore::Get().GetDevice(), m_bytecode.data(), m_bytecode.size());
|
||||
Message("Vulkan: shader module % created", m_name);
|
||||
|
||||
#ifdef SCOP_HAS_DEBUG_UTILS_FUNCTIONS
|
||||
VkDebugUtilsObjectNameInfoEXT name_info{};
|
||||
name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
|
||||
name_info.objectType = VK_OBJECT_TYPE_SHADER_MODULE;
|
||||
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_module);
|
||||
name_info.pObjectName = m_name.c_str();
|
||||
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
|
||||
#endif
|
||||
|
||||
GeneratePipelineLayout(m_layout);
|
||||
}
|
||||
|
||||
void Shader::GeneratePipelineLayout(ShaderLayout layout)
|
||||
{
|
||||
for(auto& [_, set] : layout.set_layouts)
|
||||
{
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings(set.binds.size());
|
||||
std::size_t i = 0;
|
||||
for(auto& [bind, type] : set.binds)
|
||||
{
|
||||
bindings[i].binding = bind;
|
||||
bindings[i].descriptorCount = 1;
|
||||
bindings[i].descriptorType = type;
|
||||
bindings[i].pImmutableSamplers = nullptr;
|
||||
bindings[i].stageFlags = m_stage;
|
||||
i++;
|
||||
}
|
||||
m_set_layouts.emplace_back(kvfCreateDescriptorSetLayout(RenderCore::Get().GetDevice(), bindings.data(), bindings.size()));
|
||||
Message("Vulkan: descriptor set layout created");
|
||||
m_pipeline_layout_part.set_layouts.push_back(m_set_layouts.back());
|
||||
}
|
||||
|
||||
std::size_t i = 0;
|
||||
std::vector<VkPushConstantRange> push_constants(layout.push_constants.size());
|
||||
m_pipeline_layout_part.push_constants.resize(layout.push_constants.size());
|
||||
for(auto& pc : layout.push_constants)
|
||||
{
|
||||
VkPushConstantRange push_constant_range = {};
|
||||
push_constant_range.offset = pc.offset;
|
||||
push_constant_range.size = pc.size;
|
||||
push_constant_range.stageFlags = m_stage;
|
||||
push_constants[i] = push_constant_range;
|
||||
m_pipeline_layout_part.push_constants[i] = push_constant_range;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::Destroy()
|
||||
{
|
||||
if(m_module == VK_NULL_HANDLE)
|
||||
return;
|
||||
kvfDestroyShaderModule(RenderCore::Get().GetDevice(), m_module);
|
||||
m_module = VK_NULL_HANDLE;
|
||||
Message("Vulkan: shader module % destroyed", m_name);
|
||||
for(auto& layout : m_set_layouts)
|
||||
{
|
||||
kvfDestroyDescriptorSetLayout(RenderCore::Get().GetDevice(), layout);
|
||||
Message("Vulkan: descriptor set layout destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
Shader::~Shader()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
std::shared_ptr<Shader> LoadShaderFromFile(const std::filesystem::path& filepath, ShaderType type, ShaderLayout layout)
|
||||
{
|
||||
std::ifstream stream(filepath, std::ios::binary);
|
||||
if(!stream.is_open())
|
||||
FatalError("Renderer : unable to open a spirv shader file, %", filepath);
|
||||
std::vector<std::uint32_t> data;
|
||||
stream.seekg(0);
|
||||
std::uint32_t part = 0;
|
||||
while(stream.read(reinterpret_cast<char*>(&part), sizeof(part)))
|
||||
data.push_back(part);
|
||||
stream.close();
|
||||
|
||||
std::shared_ptr<Shader> shader = std::make_shared<Shader>(data, type, layout, filepath.stem().string());
|
||||
Message("Vulkan: shader loaded %", filepath);
|
||||
return shader;
|
||||
}
|
||||
}
|
||||
277
Runtime/Sources/Renderer/RenderCore.cpp
git.filemode.normal_file
277
Runtime/Sources/Renderer/RenderCore.cpp
git.filemode.normal_file
@@ -0,0 +1,277 @@
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#include <Core/Engine.h>
|
||||
#include <Platform/Window.h>
|
||||
#include <Renderer/Descriptor.h>
|
||||
#include <Renderer/RenderCore.h>
|
||||
#include <Renderer/Pipelines/Shader.h>
|
||||
#include <Renderer/Vulkan/VulkanLoader.h>
|
||||
#include <Maths/Mat4.h>
|
||||
#include <Core/Logs.h>
|
||||
|
||||
#define KVF_IMPLEMENTATION
|
||||
#ifdef DEBUG
|
||||
#define KVF_ENABLE_VALIDATION_LAYERS
|
||||
#endif
|
||||
#define KVF_ASSERT(x) (Scop::Assert(x, "internal kvf assertion " #x))
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#include <kvf.h>
|
||||
#pragma GCC diagnostic pop
|
||||
#elif defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||
#include <kvf.h>
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
#include <kvf.h>
|
||||
#endif
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
static std::unique_ptr<VulkanLoader> loader;
|
||||
|
||||
std::optional<std::uint32_t> FindMemoryType(std::uint32_t type_filter, VkMemoryPropertyFlags properties, bool error)
|
||||
{
|
||||
VkPhysicalDeviceMemoryProperties mem_properties;
|
||||
RenderCore::Get().vkGetPhysicalDeviceMemoryProperties(RenderCore::Get().GetPhysicalDevice(), &mem_properties);
|
||||
|
||||
for(std::uint32_t i = 0; i < mem_properties.memoryTypeCount; i++)
|
||||
{
|
||||
if((type_filter & (1 << i)) && (mem_properties.memoryTypes[i].propertyFlags & properties) == properties)
|
||||
return i;
|
||||
}
|
||||
if(error)
|
||||
FatalError("Vulkan: failed to find suitable memory type");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void ErrorCallback(const char* message) noexcept
|
||||
{
|
||||
Logs::Report(LogType::FatalError, 0, "", "", message);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void ValidationErrorCallback(const char* message) noexcept
|
||||
{
|
||||
Logs::Report(LogType::Error, 0, "", "", message);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void WarningCallback(const char* message) noexcept
|
||||
{
|
||||
Logs::Report(LogType::Warning, 0, "", "", message);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
RenderCore* RenderCore::s_instance = nullptr;
|
||||
|
||||
RenderCore::RenderCore()
|
||||
{
|
||||
if(s_instance != nullptr)
|
||||
return;
|
||||
|
||||
s_instance = this;
|
||||
|
||||
Window window("", 1, 1, true);
|
||||
std::vector<const char*> instance_extensions = window.GetRequiredVulkanInstanceExtentions();
|
||||
|
||||
loader = std::make_unique<VulkanLoader>();
|
||||
|
||||
LoadKVFGlobalVulkanFunctionPointers();
|
||||
|
||||
kvfSetErrorCallback(&ErrorCallback);
|
||||
kvfSetWarningCallback(&WarningCallback);
|
||||
kvfSetValidationErrorCallback(&ValidationErrorCallback);
|
||||
kvfSetValidationWarningCallback(&WarningCallback);
|
||||
|
||||
//kvfAddLayer("VK_LAYER_MESA_overlay");
|
||||
|
||||
m_instance = kvfCreateInstance(instance_extensions.data(), instance_extensions.size());
|
||||
Message("Vulkan: instance created");
|
||||
|
||||
loader->LoadInstance(m_instance);
|
||||
LoadKVFInstanceVulkanFunctionPointers();
|
||||
|
||||
VkSurfaceKHR surface = window.CreateVulkanSurface(m_instance);
|
||||
|
||||
m_physical_device = kvfPickGoodDefaultPhysicalDevice(m_instance, surface);
|
||||
|
||||
// just for style
|
||||
VkPhysicalDeviceProperties props;
|
||||
vkGetPhysicalDeviceProperties(m_physical_device, &props);
|
||||
Message("Vulkan: physical device picked '%'", props.deviceName);
|
||||
|
||||
const char* device_extensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
|
||||
VkPhysicalDeviceFeatures features{};
|
||||
vkGetPhysicalDeviceFeatures(m_physical_device, &features);
|
||||
m_device = kvfCreateDevice(m_physical_device, device_extensions, sizeof(device_extensions) / sizeof(device_extensions[0]), &features);
|
||||
Message("Vulkan: logical device created");
|
||||
|
||||
loader->LoadDevice(m_device);
|
||||
LoadKVFDeviceVulkanFunctionPointers();
|
||||
|
||||
vkDestroySurfaceKHR(m_instance, surface, nullptr);
|
||||
|
||||
m_allocator.AttachToDevice(m_device, m_physical_device);
|
||||
|
||||
p_descriptor_pool_manager = std::make_unique<DescriptorPoolManager>();
|
||||
|
||||
ShaderLayout vertex_shader_layout(
|
||||
{
|
||||
{ 0,
|
||||
ShaderSetLayout({
|
||||
{ 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }
|
||||
})
|
||||
}
|
||||
}, { ShaderPushConstantLayout({ 0, sizeof(Mat4f) * 2 }) }
|
||||
);
|
||||
m_internal_shaders[DEFAULT_VERTEX_SHADER_ID] = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/ForwardVertex.spv", ShaderType::Vertex, std::move(vertex_shader_layout));
|
||||
|
||||
ShaderLayout default_fragment_shader_layout(
|
||||
{
|
||||
{ 1,
|
||||
ShaderSetLayout({
|
||||
{ 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }
|
||||
})
|
||||
}
|
||||
}, {}
|
||||
);
|
||||
m_internal_shaders[DEFAULT_FRAGMENT_SHADER_ID] = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/ForwardDefaultFragment.spv", ShaderType::Fragment, std::move(default_fragment_shader_layout));
|
||||
|
||||
ShaderLayout basic_fragment_shader_layout(
|
||||
{
|
||||
{ 1,
|
||||
ShaderSetLayout({
|
||||
{ 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }
|
||||
})
|
||||
}
|
||||
}, {}
|
||||
);
|
||||
m_internal_shaders[BASIC_FRAGMENT_SHADER_ID] = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/ForwardBasicFragment.spv", ShaderType::Fragment, std::move(basic_fragment_shader_layout));
|
||||
}
|
||||
|
||||
#undef SCOP_LOAD_FUNCTION
|
||||
#define SCOP_LOAD_FUNCTION(fn) pfns.fn = this->fn
|
||||
|
||||
void RenderCore::LoadKVFGlobalVulkanFunctionPointers() const noexcept
|
||||
{
|
||||
KvfGlobalVulkanFunctions pfns;
|
||||
SCOP_LOAD_FUNCTION(vkCreateInstance);
|
||||
SCOP_LOAD_FUNCTION(vkEnumerateInstanceExtensionProperties);
|
||||
SCOP_LOAD_FUNCTION(vkEnumerateInstanceLayerProperties);
|
||||
SCOP_LOAD_FUNCTION(vkGetInstanceProcAddr);
|
||||
kvfPassGlobalVulkanFunctionPointers(&pfns);
|
||||
}
|
||||
|
||||
void RenderCore::LoadKVFInstanceVulkanFunctionPointers() const noexcept
|
||||
{
|
||||
KvfInstanceVulkanFunctions pfns;
|
||||
SCOP_LOAD_FUNCTION(vkCreateDevice);
|
||||
SCOP_LOAD_FUNCTION(vkDestroyInstance);
|
||||
SCOP_LOAD_FUNCTION(vkEnumerateDeviceExtensionProperties);
|
||||
SCOP_LOAD_FUNCTION(vkEnumeratePhysicalDevices);
|
||||
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceFeatures);
|
||||
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceFormatProperties);
|
||||
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceImageFormatProperties);
|
||||
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceMemoryProperties);
|
||||
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceProperties);
|
||||
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties);
|
||||
SCOP_LOAD_FUNCTION(vkDestroySurfaceKHR);
|
||||
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
|
||||
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR);
|
||||
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR);
|
||||
SCOP_LOAD_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR);
|
||||
kvfPassInstanceVulkanFunctionPointers(&pfns);
|
||||
}
|
||||
|
||||
void RenderCore::LoadKVFDeviceVulkanFunctionPointers() const noexcept
|
||||
{
|
||||
KvfDeviceVulkanFunctions pfns;
|
||||
SCOP_LOAD_FUNCTION(vkAllocateCommandBuffers);
|
||||
SCOP_LOAD_FUNCTION(vkAllocateDescriptorSets);
|
||||
SCOP_LOAD_FUNCTION(vkBeginCommandBuffer);
|
||||
SCOP_LOAD_FUNCTION(vkCmdBeginRenderPass);
|
||||
SCOP_LOAD_FUNCTION(vkCmdCopyBuffer);
|
||||
SCOP_LOAD_FUNCTION(vkCmdCopyBufferToImage);
|
||||
SCOP_LOAD_FUNCTION(vkCmdCopyImage);
|
||||
SCOP_LOAD_FUNCTION(vkCmdCopyImageToBuffer);
|
||||
SCOP_LOAD_FUNCTION(vkCmdEndRenderPass);
|
||||
SCOP_LOAD_FUNCTION(vkCmdPipelineBarrier);
|
||||
SCOP_LOAD_FUNCTION(vkCreateBuffer);
|
||||
SCOP_LOAD_FUNCTION(vkCreateCommandPool);
|
||||
SCOP_LOAD_FUNCTION(vkCreateDescriptorPool);
|
||||
SCOP_LOAD_FUNCTION(vkCreateDescriptorSetLayout);
|
||||
SCOP_LOAD_FUNCTION(vkCreateFence);
|
||||
SCOP_LOAD_FUNCTION(vkCreateFramebuffer);
|
||||
SCOP_LOAD_FUNCTION(vkCreateGraphicsPipelines);
|
||||
SCOP_LOAD_FUNCTION(vkCreateImage);
|
||||
SCOP_LOAD_FUNCTION(vkCreateImageView);
|
||||
SCOP_LOAD_FUNCTION(vkCreatePipelineLayout);
|
||||
SCOP_LOAD_FUNCTION(vkCreateRenderPass);
|
||||
SCOP_LOAD_FUNCTION(vkCreateSampler);
|
||||
SCOP_LOAD_FUNCTION(vkCreateSemaphore);
|
||||
SCOP_LOAD_FUNCTION(vkCreateShaderModule);
|
||||
SCOP_LOAD_FUNCTION(vkDestroyBuffer);
|
||||
SCOP_LOAD_FUNCTION(vkDestroyCommandPool);
|
||||
SCOP_LOAD_FUNCTION(vkDestroyDescriptorPool);
|
||||
SCOP_LOAD_FUNCTION(vkDestroyDescriptorSetLayout);
|
||||
SCOP_LOAD_FUNCTION(vkDestroyDevice);
|
||||
SCOP_LOAD_FUNCTION(vkDestroyFence);
|
||||
SCOP_LOAD_FUNCTION(vkDestroyFramebuffer);
|
||||
SCOP_LOAD_FUNCTION(vkDestroyImage);
|
||||
SCOP_LOAD_FUNCTION(vkDestroyImageView);
|
||||
SCOP_LOAD_FUNCTION(vkDestroyPipeline);
|
||||
SCOP_LOAD_FUNCTION(vkDestroyPipelineLayout);
|
||||
SCOP_LOAD_FUNCTION(vkDestroyRenderPass);
|
||||
SCOP_LOAD_FUNCTION(vkDestroySampler);
|
||||
SCOP_LOAD_FUNCTION(vkDestroySemaphore);
|
||||
SCOP_LOAD_FUNCTION(vkDestroyShaderModule);
|
||||
SCOP_LOAD_FUNCTION(vkDeviceWaitIdle);
|
||||
SCOP_LOAD_FUNCTION(vkEndCommandBuffer);
|
||||
SCOP_LOAD_FUNCTION(vkFreeCommandBuffers);
|
||||
SCOP_LOAD_FUNCTION(vkGetDeviceQueue);
|
||||
SCOP_LOAD_FUNCTION(vkGetImageSubresourceLayout);
|
||||
SCOP_LOAD_FUNCTION(vkQueueSubmit);
|
||||
SCOP_LOAD_FUNCTION(vkResetCommandBuffer);
|
||||
SCOP_LOAD_FUNCTION(vkResetDescriptorPool);
|
||||
SCOP_LOAD_FUNCTION(vkResetEvent);
|
||||
SCOP_LOAD_FUNCTION(vkResetFences);
|
||||
SCOP_LOAD_FUNCTION(vkUpdateDescriptorSets);
|
||||
SCOP_LOAD_FUNCTION(vkWaitForFences);
|
||||
SCOP_LOAD_FUNCTION(vkCreateSwapchainKHR);
|
||||
SCOP_LOAD_FUNCTION(vkDestroySwapchainKHR);
|
||||
SCOP_LOAD_FUNCTION(vkGetSwapchainImagesKHR);
|
||||
SCOP_LOAD_FUNCTION(vkQueuePresentKHR);
|
||||
kvfPassDeviceVulkanFunctionPointers(m_physical_device, m_device, &pfns);
|
||||
}
|
||||
|
||||
#undef SCOP_LOAD_FUNCTION
|
||||
|
||||
RenderCore::~RenderCore()
|
||||
{
|
||||
if(s_instance == nullptr)
|
||||
return;
|
||||
WaitDeviceIdle();
|
||||
p_descriptor_pool_manager->Destroy();
|
||||
p_descriptor_pool_manager.reset();
|
||||
m_allocator.DetachFromDevice();
|
||||
for(auto shader: m_internal_shaders)
|
||||
shader->Destroy();
|
||||
kvfDestroyDevice(m_device);
|
||||
Message("Vulkan: logical device destroyed");
|
||||
kvfDestroyInstance(m_instance);
|
||||
Message("Vulkan: instance destroyed");
|
||||
loader.reset();
|
||||
|
||||
s_instance = nullptr;
|
||||
}
|
||||
}
|
||||
141
Runtime/Sources/Renderer/RenderPasses/2DPass.cpp
git.filemode.normal_file
141
Runtime/Sources/Renderer/RenderPasses/2DPass.cpp
git.filemode.normal_file
@@ -0,0 +1,141 @@
|
||||
#include <Renderer/RenderPasses/2DPass.h>
|
||||
#include <Renderer/Pipelines/Graphics.h>
|
||||
#include <Renderer/ViewerData.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Graphics/Scene.h>
|
||||
#include <Core/Engine.h>
|
||||
#include <Maths/Mat4.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
struct SpriteData
|
||||
{
|
||||
Mat4f model_matrix;
|
||||
Vec4f color;
|
||||
};
|
||||
|
||||
struct ViewerData2D
|
||||
{
|
||||
Mat4f projection;
|
||||
};
|
||||
|
||||
void Render2DPass::Init()
|
||||
{
|
||||
ShaderLayout vertex_shader_layout(
|
||||
{
|
||||
{ 0,
|
||||
ShaderSetLayout({
|
||||
{ 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }
|
||||
})
|
||||
}
|
||||
}, { ShaderPushConstantLayout({ 0, sizeof(SpriteData) }) }
|
||||
);
|
||||
p_vertex_shader = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/2DVertex.spv", ShaderType::Vertex, std::move(vertex_shader_layout));
|
||||
ShaderLayout fragment_shader_layout(
|
||||
{
|
||||
{ 1,
|
||||
ShaderSetLayout({
|
||||
{ 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER }
|
||||
})
|
||||
}
|
||||
}, {}
|
||||
);
|
||||
p_fragment_shader = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/2DFragment.spv", ShaderType::Fragment, std::move(fragment_shader_layout));
|
||||
|
||||
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
|
||||
{
|
||||
if(event.What() == Event::ResizeEventCode || event.What() == Event::SceneHasChangedEventCode)
|
||||
m_pipeline.Destroy();
|
||||
};
|
||||
EventBus::RegisterListener({ functor, "__ScopRender2DPass" });
|
||||
|
||||
p_viewer_data_set = RenderCore::Get().GetDescriptorPoolManager().GetAvailablePool().RequestDescriptorSet(p_vertex_shader->GetShaderLayout().set_layouts.at(0), ShaderType::Vertex);
|
||||
p_texture_set = RenderCore::Get().GetDescriptorPoolManager().GetAvailablePool().RequestDescriptorSet(p_fragment_shader->GetShaderLayout().set_layouts.at(1), ShaderType::Fragment);
|
||||
|
||||
p_viewer_data_buffer = std::make_shared<UniformBuffer>();
|
||||
p_viewer_data_buffer->Init(sizeof(ViewerData2D));
|
||||
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||
{
|
||||
p_viewer_data_set->SetUniformBuffer(i, 0, p_viewer_data_buffer->Get(i));
|
||||
p_viewer_data_set->Update(i);
|
||||
}
|
||||
}
|
||||
|
||||
void Render2DPass::Pass(Scene& scene, Renderer& renderer, Texture& render_target)
|
||||
{
|
||||
if(m_pipeline.GetPipeline() == VK_NULL_HANDLE)
|
||||
{
|
||||
GraphicPipelineDescriptor pipeline_descriptor;
|
||||
pipeline_descriptor.vertex_shader = p_vertex_shader;
|
||||
pipeline_descriptor.fragment_shader = p_fragment_shader;
|
||||
pipeline_descriptor.color_attachments = { &render_target };
|
||||
pipeline_descriptor.culling = CullMode::None;
|
||||
pipeline_descriptor.clear_color_attachments = false;
|
||||
pipeline_descriptor.name = "2D_pass_pipeline";
|
||||
m_pipeline.Init(std::move(pipeline_descriptor));
|
||||
}
|
||||
|
||||
std::uint32_t frame_index = renderer.GetCurrentFrameIndex();
|
||||
|
||||
ViewerData2D viewer_data;
|
||||
viewer_data.projection = Mat4f::Ortho(0.0f, render_target.GetWidth(), render_target.GetHeight(), 0.0f);
|
||||
static CPUBuffer buffer(sizeof(ViewerData2D));
|
||||
std::memcpy(buffer.GetData(), &viewer_data, buffer.GetSize());
|
||||
p_viewer_data_buffer->SetData(buffer, frame_index);
|
||||
|
||||
VkCommandBuffer cmd = renderer.GetActiveCommandBuffer();
|
||||
m_pipeline.BindPipeline(cmd, 0, {});
|
||||
for(const auto& [_, sprite] : scene.GetSprites())
|
||||
{
|
||||
SpriteData sprite_data;
|
||||
sprite_data.color = sprite.GetColor();
|
||||
|
||||
Mat4f translation_matrix = Mat4f::Identity().ApplyTranslation(Vec3f{ Vec2f(sprite.GetPosition()), 0.0f });
|
||||
Mat4f scale_matrix = Mat4f::Identity().ApplyScale(Vec3f{ sprite.GetScale(), 1.0f });
|
||||
|
||||
sprite_data.model_matrix = Mat4f::Identity();
|
||||
sprite_data.model_matrix.ConcatenateTransform(scale_matrix);
|
||||
sprite_data.model_matrix.ConcatenateTransform(translation_matrix);
|
||||
|
||||
if(!sprite.IsSetInit())
|
||||
const_cast<Sprite&>(sprite).UpdateDescriptorSet(p_texture_set);
|
||||
const_cast<Sprite&>(sprite).Bind(frame_index, cmd);
|
||||
std::array<VkDescriptorSet, 2> sets = { p_viewer_data_set->GetSet(frame_index), sprite.GetSet(frame_index) };
|
||||
RenderCore::Get().vkCmdBindDescriptorSets(cmd, m_pipeline.GetPipelineBindPoint(), m_pipeline.GetPipelineLayout(), 0, sets.size(), sets.data(), 0, nullptr);
|
||||
RenderCore::Get().vkCmdPushConstants(cmd, m_pipeline.GetPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(SpriteData), &sprite_data);
|
||||
sprite.GetMesh()->Draw(cmd, renderer.GetDrawCallsCounterRef(), renderer.GetPolygonDrawnCounterRef());
|
||||
}
|
||||
for(const auto& [_, text] : scene.GetTexts())
|
||||
{
|
||||
SpriteData sprite_data;
|
||||
sprite_data.color = text.GetColor();
|
||||
|
||||
Mat4f translation_matrix = Mat4f::Identity().ApplyTranslation(Vec3f{ Vec2f(text.GetPosition()), 0.0f });
|
||||
Mat4f scale_matrix = Mat4f::Identity().ApplyScale(Vec3f{ text.GetScale(), 1.0f });
|
||||
|
||||
sprite_data.model_matrix = Mat4f::Identity();
|
||||
sprite_data.model_matrix.ConcatenateTransform(scale_matrix);
|
||||
sprite_data.model_matrix.ConcatenateTransform(translation_matrix);
|
||||
|
||||
if(!text.IsSetInit())
|
||||
const_cast<Text&>(text).UpdateDescriptorSet(p_texture_set);
|
||||
const_cast<Text&>(text).Bind(frame_index, cmd);
|
||||
std::array<VkDescriptorSet, 2> sets = { p_viewer_data_set->GetSet(frame_index), text.GetSet(frame_index) };
|
||||
RenderCore::Get().vkCmdBindDescriptorSets(cmd, m_pipeline.GetPipelineBindPoint(), m_pipeline.GetPipelineLayout(), 0, sets.size(), sets.data(), 0, nullptr);
|
||||
RenderCore::Get().vkCmdPushConstants(cmd, m_pipeline.GetPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(SpriteData), &sprite_data);
|
||||
text.GetMesh()->Draw(cmd, renderer.GetDrawCallsCounterRef(), renderer.GetPolygonDrawnCounterRef());
|
||||
}
|
||||
m_pipeline.EndPipeline(cmd);
|
||||
}
|
||||
|
||||
void Render2DPass::Destroy()
|
||||
{
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
m_pipeline.Destroy();
|
||||
p_vertex_shader.reset();
|
||||
p_fragment_shader.reset();
|
||||
p_viewer_data_set.reset();
|
||||
p_viewer_data_buffer->Destroy();
|
||||
p_texture_set.reset();
|
||||
}
|
||||
}
|
||||
73
Runtime/Sources/Renderer/RenderPasses/FinalPass.cpp
git.filemode.normal_file
73
Runtime/Sources/Renderer/RenderPasses/FinalPass.cpp
git.filemode.normal_file
@@ -0,0 +1,73 @@
|
||||
#include <Renderer/RenderPasses/FinalPass.h>
|
||||
#include <Renderer/Pipelines/Graphics.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Graphics/Scene.h>
|
||||
#include <Core/EventBus.h>
|
||||
#include <Core/Engine.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
void FinalPass::Init()
|
||||
{
|
||||
ShaderLayout vertex_shader_layout(
|
||||
{}, {}
|
||||
);
|
||||
p_vertex_shader = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/ScreenVertex.spv", ShaderType::Vertex, std::move(vertex_shader_layout));
|
||||
ShaderLayout fragment_shader_layout(
|
||||
{
|
||||
{ 0,
|
||||
ShaderSetLayout({
|
||||
{ 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER }
|
||||
})
|
||||
}
|
||||
}, {}
|
||||
);
|
||||
p_fragment_shader = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/ScreenFragment.spv", ShaderType::Fragment, std::move(fragment_shader_layout));
|
||||
|
||||
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
|
||||
{
|
||||
if(event.What() == Event::ResizeEventCode)
|
||||
m_pipeline.Destroy();
|
||||
};
|
||||
EventBus::RegisterListener({ functor, "__ScopFinalPass" });
|
||||
|
||||
p_set = RenderCore::Get().GetDescriptorPoolManager().GetAvailablePool().RequestDescriptorSet(p_fragment_shader->GetShaderLayout().set_layouts.at(0), ShaderType::Fragment);
|
||||
}
|
||||
|
||||
void FinalPass::Pass(Scene& scene, Renderer& renderer, Texture& render_target)
|
||||
{
|
||||
if(m_pipeline.GetPipeline() == VK_NULL_HANDLE)
|
||||
{
|
||||
GraphicPipelineDescriptor pipeline_descriptor;
|
||||
pipeline_descriptor.vertex_shader = p_vertex_shader;
|
||||
pipeline_descriptor.fragment_shader = p_fragment_shader;
|
||||
pipeline_descriptor.renderer = &renderer;
|
||||
pipeline_descriptor.culling = CullMode::None;
|
||||
pipeline_descriptor.no_vertex_inputs = true;
|
||||
pipeline_descriptor.name = "final_pass_pipeline";
|
||||
m_pipeline.Init(std::move(pipeline_descriptor));
|
||||
}
|
||||
|
||||
VkCommandBuffer cmd = renderer.GetActiveCommandBuffer();
|
||||
|
||||
p_set->SetImage(renderer.GetCurrentFrameIndex(), 0, render_target);
|
||||
p_set->Update(renderer.GetCurrentFrameIndex(), cmd);
|
||||
|
||||
m_pipeline.BindPipeline(cmd, renderer.GetSwapchain().GetImageIndex(), { 0.0f, 0.0f, 0.0f, 1.0f });
|
||||
VkDescriptorSet set = p_set->GetSet(renderer.GetCurrentFrameIndex());
|
||||
RenderCore::Get().vkCmdBindDescriptorSets(cmd, m_pipeline.GetPipelineBindPoint(), m_pipeline.GetPipelineLayout(), 0, 1, &set, 0, nullptr);
|
||||
RenderCore::Get().vkCmdDraw(cmd, 3, 1, 0, 0);
|
||||
renderer.GetDrawCallsCounterRef()++;
|
||||
renderer.GetPolygonDrawnCounterRef()++;
|
||||
m_pipeline.EndPipeline(cmd);
|
||||
}
|
||||
|
||||
void FinalPass::Destroy()
|
||||
{
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
m_pipeline.Destroy();
|
||||
p_vertex_shader.reset();
|
||||
p_fragment_shader.reset();
|
||||
p_set.reset();
|
||||
}
|
||||
}
|
||||
118
Runtime/Sources/Renderer/RenderPasses/ForwardPass.cpp
git.filemode.normal_file
118
Runtime/Sources/Renderer/RenderPasses/ForwardPass.cpp
git.filemode.normal_file
@@ -0,0 +1,118 @@
|
||||
#include <Renderer/RenderPasses/ForwardPass.h>
|
||||
#include <Renderer/Pipelines/Graphics.h>
|
||||
#include <Renderer/ViewerData.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Graphics/Scene.h>
|
||||
#include <Maths/Mat4.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
struct ModelData
|
||||
{
|
||||
Mat4f model_mat;
|
||||
Mat4f normal_mat;
|
||||
};
|
||||
|
||||
void ForwardPass::Pass(Scene& scene, Renderer& renderer, class Texture& render_target)
|
||||
{
|
||||
NonOwningPtr<GraphicPipeline> pipeline = &scene.GetPipeline();
|
||||
if(scene.GetPipeline().GetPipeline() == VK_NULL_HANDLE)
|
||||
{
|
||||
GraphicPipelineDescriptor pipeline_descriptor;
|
||||
pipeline_descriptor.vertex_shader = RenderCore::Get().GetDefaultVertexShader();
|
||||
pipeline_descriptor.fragment_shader = scene.GetFragmentShader();
|
||||
pipeline_descriptor.color_attachments = { &render_target };
|
||||
pipeline_descriptor.depth = &scene.GetDepth();
|
||||
pipeline_descriptor.clear_color_attachments = false;
|
||||
pipeline_descriptor.name = "forward_pass_pipeline";
|
||||
scene.GetPipeline().Init(std::move(pipeline_descriptor));
|
||||
}
|
||||
|
||||
auto render_actor = [&render_target, &renderer, &scene, &pipeline](Actor& actor)
|
||||
{
|
||||
Scene::ForwardData& data = scene.GetForwardData();
|
||||
VkCommandBuffer cmd = renderer.GetActiveCommandBuffer();
|
||||
std::shared_ptr<GraphicPipeline> custom_pipeline = (actor.GetCustomPipeline().has_value() ? actor.GetCustomPipeline()->pipeline : nullptr);
|
||||
|
||||
if(custom_pipeline && !custom_pipeline->IsPipelineBound())
|
||||
{
|
||||
pipeline->EndPipeline(cmd);
|
||||
pipeline = actor.GetCustomPipeline()->pipeline.get();
|
||||
if(pipeline->GetDescription().depth != NonOwningPtr<DepthImage>{ &scene.GetDepth() })
|
||||
{
|
||||
GraphicPipelineDescriptor descriptor = pipeline->GetDescription();
|
||||
descriptor.color_attachments = { &render_target };
|
||||
descriptor.depth = &scene.GetDepth();
|
||||
descriptor.renderer = nullptr;
|
||||
descriptor.clear_color_attachments = false;
|
||||
pipeline->Init(std::move(descriptor));
|
||||
}
|
||||
pipeline->BindPipeline(cmd, 0, {});
|
||||
}
|
||||
else if(!custom_pipeline && !scene.GetPipeline().IsPipelineBound())
|
||||
{
|
||||
pipeline->EndPipeline(cmd);
|
||||
pipeline = &scene.GetPipeline();
|
||||
pipeline->BindPipeline(cmd, 0, {});
|
||||
}
|
||||
|
||||
ModelData model_data;
|
||||
model_data.model_mat = Mat4f::Identity();
|
||||
model_data.model_mat.SetTranslation(actor.GetPosition() - actor.GetModel().GetCenter());
|
||||
model_data.model_mat.SetScale(actor.GetScale());
|
||||
model_data.model_mat = Mat4f::Translate(-actor.GetModel().GetCenter()) * Mat4f::Rotate(actor.GetOrientation()) * model_data.model_mat;
|
||||
model_data.normal_mat = model_data.model_mat;
|
||||
model_data.normal_mat.Inverse().Transpose();
|
||||
|
||||
RenderCore::Get().vkCmdPushConstants(cmd, pipeline->GetPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(ModelData), &model_data);
|
||||
|
||||
if(custom_pipeline)
|
||||
{
|
||||
if(!actor.GetCustomPipeline()->data_uniform_buffer)
|
||||
{
|
||||
actor.GetCustomPipeline()->data_uniform_buffer = std::make_shared<UniformBuffer>();
|
||||
actor.GetCustomPipeline()->data_uniform_buffer->Init(actor.GetCustomPipeline()->data.GetSize(), "custom_data_buffer");
|
||||
}
|
||||
else
|
||||
actor.GetCustomPipeline()->data_uniform_buffer->SetData(actor.GetCustomPipeline()->data, renderer.GetCurrentFrameIndex());
|
||||
if(!actor.GetCustomPipeline()->set)
|
||||
{
|
||||
actor.GetCustomPipeline()->set = RenderCore::Get().GetDescriptorPoolManager().GetAvailablePool().RequestDescriptorSet(custom_pipeline->GetDescription().vertex_shader->GetShaderLayout().set_layouts.at(0), ShaderType::Vertex);
|
||||
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||
{
|
||||
actor.GetCustomPipeline()->set->SetUniformBuffer(i, 0, data.matrices_buffer->Get(i));
|
||||
actor.GetCustomPipeline()->set->SetUniformBuffer(i, 1, actor.GetCustomPipeline()->data_uniform_buffer->Get(i));
|
||||
actor.GetCustomPipeline()->set->Update(i);
|
||||
}
|
||||
}
|
||||
actor.GetModel().Draw(cmd, actor.GetCustomPipeline()->set, *pipeline, data.albedo_set, renderer.GetDrawCallsCounterRef(), renderer.GetPolygonDrawnCounterRef(), renderer.GetCurrentFrameIndex());
|
||||
}
|
||||
else
|
||||
actor.GetModel().Draw(cmd, data.matrices_set, *pipeline, data.albedo_set, renderer.GetDrawCallsCounterRef(), renderer.GetPolygonDrawnCounterRef(), renderer.GetCurrentFrameIndex());
|
||||
};
|
||||
|
||||
std::multimap<float, Actor&> sorted_actors;
|
||||
for(auto& [_, actor] : scene.GetActors())
|
||||
{
|
||||
if(!actor.IsVisible())
|
||||
continue;
|
||||
if(!actor.IsOpaque())
|
||||
{
|
||||
float distance = Vec3f::Distance(actor.GetPosition(), scene.GetCamera()->GetPosition());
|
||||
sorted_actors.emplace(distance, const_cast<Actor&>(actor));
|
||||
}
|
||||
else
|
||||
render_actor(const_cast<Actor&>(actor));
|
||||
}
|
||||
for(auto it = sorted_actors.rbegin(); it != sorted_actors.rend(); ++it)
|
||||
{
|
||||
if(!it->second.IsVisible())
|
||||
continue;
|
||||
render_actor(it->second);
|
||||
}
|
||||
if(pipeline)
|
||||
pipeline->EndPipeline(renderer.GetActiveCommandBuffer());
|
||||
}
|
||||
}
|
||||
62
Runtime/Sources/Renderer/RenderPasses/Passes.cpp
git.filemode.normal_file
62
Runtime/Sources/Renderer/RenderPasses/Passes.cpp
git.filemode.normal_file
@@ -0,0 +1,62 @@
|
||||
#include <Renderer/RenderPasses/Passes.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Graphics/Scene.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
void RenderPasses::Init()
|
||||
{
|
||||
m_skybox.Init();
|
||||
m_2Dpass.Init();
|
||||
m_post_process.Init();
|
||||
m_final.Init();
|
||||
|
||||
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
|
||||
{
|
||||
if(event.What() == Event::ResizeEventCode)
|
||||
m_main_render_texture.Destroy();
|
||||
};
|
||||
EventBus::RegisterListener({ functor, "__ScopRenderPasses" });
|
||||
}
|
||||
|
||||
void RenderPasses::Pass(Scene& scene, Renderer& renderer)
|
||||
{
|
||||
if(!m_main_render_texture.IsInit())
|
||||
{
|
||||
auto extent = kvfGetSwapchainImagesSize(renderer.GetSwapchain().Get());
|
||||
m_main_render_texture.Init({}, extent.width, extent.height, VK_FORMAT_R8G8B8A8_UNORM, false, "scop_main_render_texture", false);
|
||||
}
|
||||
|
||||
scene.GetDepth().Clear(renderer.GetActiveCommandBuffer(), {});
|
||||
m_main_render_texture.Clear(renderer.GetActiveCommandBuffer(), Vec4f{ 0.0f, 0.0f, 0.0f, 1.0f });
|
||||
m_main_render_texture.TransitionLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, renderer.GetActiveCommandBuffer());
|
||||
|
||||
if(scene.GetDescription().render_3D_enabled)
|
||||
m_forward.Pass(scene, renderer, m_main_render_texture);
|
||||
if(scene.GetDescription().render_skybox_enabled)
|
||||
m_skybox.Pass(scene, renderer, m_main_render_texture);
|
||||
if(scene.GetDescription().render_post_process_enabled && scene.GetDescription().post_process_shader)
|
||||
{
|
||||
m_post_process.Pass(scene, renderer, m_main_render_texture);
|
||||
if(scene.GetDescription().render_2D_enabled)
|
||||
m_2Dpass.Pass(scene, renderer, m_post_process.GetProcessTexture());
|
||||
m_final.Pass(scene, renderer, m_post_process.GetProcessTexture());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(scene.GetDescription().render_2D_enabled)
|
||||
m_2Dpass.Pass(scene, renderer, m_main_render_texture);
|
||||
m_final.Pass(scene, renderer, m_main_render_texture);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPasses::Destroy()
|
||||
{
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
m_skybox.Destroy();
|
||||
m_2Dpass.Destroy();
|
||||
m_post_process.Destroy();
|
||||
m_final.Destroy();
|
||||
m_main_render_texture.Destroy();
|
||||
}
|
||||
}
|
||||
74
Runtime/Sources/Renderer/RenderPasses/PostProcessPass.cpp
git.filemode.normal_file
74
Runtime/Sources/Renderer/RenderPasses/PostProcessPass.cpp
git.filemode.normal_file
@@ -0,0 +1,74 @@
|
||||
#include <Renderer/RenderPasses/PostProcessPass.h>
|
||||
#include <Renderer/Pipelines/Graphics.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Graphics/Scene.h>
|
||||
#include <Core/EventBus.h>
|
||||
#include <Core/Engine.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
void PostProcessPass::Init()
|
||||
{
|
||||
ShaderLayout vertex_shader_layout(
|
||||
{}, {}
|
||||
);
|
||||
p_vertex_shader = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/ScreenVertex.spv", ShaderType::Vertex, std::move(vertex_shader_layout));
|
||||
|
||||
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
|
||||
{
|
||||
if(event.What() == Event::ResizeEventCode)
|
||||
{
|
||||
m_render_texture.Destroy();
|
||||
m_pipeline.Destroy();
|
||||
}
|
||||
};
|
||||
EventBus::RegisterListener({ functor, "__ScopPostProcessPass" });
|
||||
}
|
||||
|
||||
void PostProcessPass::Pass(Scene& scene, Renderer& renderer, Texture& render_target)
|
||||
{
|
||||
Scene::PostProcessData& data = scene.GetPostProcessData();
|
||||
|
||||
if(!m_render_texture.IsInit())
|
||||
{
|
||||
auto extent = kvfGetSwapchainImagesSize(renderer.GetSwapchain().Get());
|
||||
m_render_texture.Init({}, extent.width, extent.height, VK_FORMAT_R8G8B8A8_UNORM, false, "scop_post_process_render_texture", false);
|
||||
}
|
||||
|
||||
if(m_pipeline.GetPipeline() == VK_NULL_HANDLE)
|
||||
{
|
||||
GraphicPipelineDescriptor pipeline_descriptor;
|
||||
pipeline_descriptor.vertex_shader = p_vertex_shader;
|
||||
pipeline_descriptor.fragment_shader = scene.GetDescription().post_process_shader;
|
||||
pipeline_descriptor.color_attachments = { &m_render_texture };
|
||||
pipeline_descriptor.culling = CullMode::None;
|
||||
pipeline_descriptor.clear_color_attachments = false;
|
||||
pipeline_descriptor.no_vertex_inputs = true;
|
||||
pipeline_descriptor.name = "post_process_pass_pipeline";
|
||||
m_pipeline.Init(std::move(pipeline_descriptor));
|
||||
}
|
||||
|
||||
VkCommandBuffer cmd = renderer.GetActiveCommandBuffer();
|
||||
|
||||
data.set->SetImage(renderer.GetCurrentFrameIndex(), 0, render_target);
|
||||
data.set->SetImage(renderer.GetCurrentFrameIndex(), 1, scene.GetDepth());
|
||||
data.set->SetUniformBuffer(renderer.GetCurrentFrameIndex(), 2, data.data_buffer->Get(renderer.GetCurrentFrameIndex()));
|
||||
data.set->Update(renderer.GetCurrentFrameIndex(), cmd);
|
||||
|
||||
m_pipeline.BindPipeline(cmd, 0, {});
|
||||
VkDescriptorSet set = data.set->GetSet(renderer.GetCurrentFrameIndex());
|
||||
RenderCore::Get().vkCmdBindDescriptorSets(cmd, m_pipeline.GetPipelineBindPoint(), m_pipeline.GetPipelineLayout(), 0, 1, &set, 0, nullptr);
|
||||
RenderCore::Get().vkCmdDraw(cmd, 3, 1, 0, 0);
|
||||
renderer.GetDrawCallsCounterRef()++;
|
||||
renderer.GetPolygonDrawnCounterRef()++;
|
||||
m_pipeline.EndPipeline(cmd);
|
||||
}
|
||||
|
||||
void PostProcessPass::Destroy()
|
||||
{
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
m_render_texture.Destroy();
|
||||
m_pipeline.Destroy();
|
||||
p_vertex_shader.reset();
|
||||
}
|
||||
}
|
||||
86
Runtime/Sources/Renderer/RenderPasses/SkyboxPass.cpp
git.filemode.normal_file
86
Runtime/Sources/Renderer/RenderPasses/SkyboxPass.cpp
git.filemode.normal_file
@@ -0,0 +1,86 @@
|
||||
#include <Renderer/RenderPasses/SkyboxPass.h>
|
||||
#include <Renderer/Pipelines/Graphics.h>
|
||||
#include <Graphics/MeshFactory.h>
|
||||
#include <Renderer/ViewerData.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Graphics/Scene.h>
|
||||
#include <Core/EventBus.h>
|
||||
#include <Core/Engine.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
void SkyboxPass::Init()
|
||||
{
|
||||
ShaderLayout vertex_shader_layout(
|
||||
{
|
||||
{ 0,
|
||||
ShaderSetLayout({
|
||||
{ 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }
|
||||
})
|
||||
}
|
||||
}, {}
|
||||
);
|
||||
p_vertex_shader = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/SkyboxVertex.spv", ShaderType::Vertex, std::move(vertex_shader_layout));
|
||||
ShaderLayout fragment_shader_layout(
|
||||
{
|
||||
{ 1,
|
||||
ShaderSetLayout({
|
||||
{ 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER }
|
||||
})
|
||||
}
|
||||
}, {}
|
||||
);
|
||||
p_fragment_shader = LoadShaderFromFile(ScopEngine::Get().GetAssetsPath() / "Shaders/Build/SkyboxFragment.spv", ShaderType::Fragment, std::move(fragment_shader_layout));
|
||||
|
||||
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
|
||||
{
|
||||
if(event.What() == Event::ResizeEventCode || event.What() == Event::SceneHasChangedEventCode)
|
||||
m_pipeline.Destroy();
|
||||
};
|
||||
EventBus::RegisterListener({ functor, "__ScopSkyboxPass" });
|
||||
|
||||
m_cube = CreateCube();
|
||||
p_set = RenderCore::Get().GetDescriptorPoolManager().GetAvailablePool().RequestDescriptorSet(p_fragment_shader->GetShaderLayout().set_layouts.at(1), ShaderType::Fragment);
|
||||
}
|
||||
|
||||
void SkyboxPass::Pass(Scene& scene, Renderer& renderer, class Texture& render_target)
|
||||
{
|
||||
if(!scene.GetSkybox())
|
||||
return;
|
||||
|
||||
if(m_pipeline.GetPipeline() == VK_NULL_HANDLE)
|
||||
{
|
||||
GraphicPipelineDescriptor pipeline_descriptor;
|
||||
pipeline_descriptor.vertex_shader = p_vertex_shader;
|
||||
pipeline_descriptor.fragment_shader = p_fragment_shader;
|
||||
pipeline_descriptor.color_attachments = { &render_target };
|
||||
pipeline_descriptor.depth = &scene.GetDepth();
|
||||
pipeline_descriptor.culling = CullMode::None;
|
||||
pipeline_descriptor.depth_test_equal = true;
|
||||
pipeline_descriptor.clear_color_attachments = false;
|
||||
pipeline_descriptor.name = "skybox_pass_pipeline";
|
||||
m_pipeline.Init(std::move(pipeline_descriptor));
|
||||
}
|
||||
|
||||
VkCommandBuffer cmd = renderer.GetActiveCommandBuffer();
|
||||
|
||||
p_set->SetImage(renderer.GetCurrentFrameIndex(), 0, *scene.GetSkybox());
|
||||
p_set->Update(renderer.GetCurrentFrameIndex(), cmd);
|
||||
|
||||
m_pipeline.BindPipeline(cmd, 0, {});
|
||||
std::array<VkDescriptorSet, 2> sets = { scene.GetForwardData().matrices_set->GetSet(renderer.GetCurrentFrameIndex()), p_set->GetSet(renderer.GetCurrentFrameIndex()) };
|
||||
RenderCore::Get().vkCmdBindDescriptorSets(cmd, m_pipeline.GetPipelineBindPoint(), m_pipeline.GetPipelineLayout(), 0, sets.size(), sets.data(), 0, nullptr);
|
||||
m_cube->Draw(cmd, renderer.GetDrawCallsCounterRef(), renderer.GetPolygonDrawnCounterRef());
|
||||
m_pipeline.EndPipeline(cmd);
|
||||
}
|
||||
|
||||
void SkyboxPass::Destroy()
|
||||
{
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
m_pipeline.Destroy();
|
||||
p_vertex_shader.reset();
|
||||
p_fragment_shader.reset();
|
||||
m_cube.reset();
|
||||
p_set.reset();
|
||||
}
|
||||
}
|
||||
96
Runtime/Sources/Renderer/Renderer.cpp
git.filemode.normal_file
96
Runtime/Sources/Renderer/Renderer.cpp
git.filemode.normal_file
@@ -0,0 +1,96 @@
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Core/Logs.h>
|
||||
#include <Core/Enums.h>
|
||||
#include <Core/Engine.h>
|
||||
#include <Core/EventBus.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
struct FrameBeginEventBroadcast : public EventBase
|
||||
{
|
||||
Event What() const override { return Event::FrameBeginEventCode; }
|
||||
};
|
||||
}
|
||||
|
||||
void Renderer::Init(NonOwningPtr<Window> window)
|
||||
{
|
||||
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
|
||||
{
|
||||
if(event.What() == Event::ResizeEventCode)
|
||||
{
|
||||
for(std::size_t i = 0; i < m_render_finished_semaphores.size(); i++)
|
||||
{
|
||||
kvfDestroySemaphore(RenderCore::Get().GetDevice(), m_render_finished_semaphores[i]);
|
||||
Message("Vulkan: render finished semaphore destroyed");
|
||||
}
|
||||
m_render_finished_semaphores.clear();
|
||||
for(std::size_t i = 0; i < m_swapchain.GetImagesCount(); i++)
|
||||
{
|
||||
m_render_finished_semaphores.push_back(kvfCreateSemaphore(RenderCore::Get().GetDevice()));
|
||||
Message("Vulkan: render finished semaphore created");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EventBus::RegisterListener({ functor, "__ScopRenderer" });
|
||||
p_window = window;
|
||||
m_swapchain.Init(p_window);
|
||||
for(std::size_t i = 0; i < m_swapchain.GetImagesCount(); i++)
|
||||
{
|
||||
m_render_finished_semaphores.push_back(kvfCreateSemaphore(RenderCore::Get().GetDevice()));
|
||||
Message("Vulkan: render finished semaphore created");
|
||||
}
|
||||
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||
{
|
||||
m_cmd_buffers[i] = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
|
||||
Message("Vulkan: command buffer created");
|
||||
m_cmd_fences[i] = kvfCreateFence(RenderCore::Get().GetDevice());
|
||||
Message("Vulkan: fence created");
|
||||
m_image_available_semaphores[i] = kvfCreateSemaphore(RenderCore::Get().GetDevice());
|
||||
Message("Vulkan: image available semaphore created");
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::BeginFrame()
|
||||
{
|
||||
kvfWaitForFence(RenderCore::Get().GetDevice(), m_cmd_fences[m_current_frame_index]);
|
||||
m_swapchain.AquireFrame(m_image_available_semaphores[m_current_frame_index]);
|
||||
RenderCore::Get().vkResetCommandBuffer(m_cmd_buffers[m_current_frame_index], 0);
|
||||
kvfBeginCommandBuffer(m_cmd_buffers[m_current_frame_index], 0);
|
||||
m_drawcalls = 0;
|
||||
m_polygons_drawn = 0;
|
||||
EventBus::SendBroadcast(Internal::FrameBeginEventBroadcast{});
|
||||
}
|
||||
|
||||
void Renderer::EndFrame()
|
||||
{
|
||||
VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
|
||||
kvfEndCommandBuffer(m_cmd_buffers[m_current_frame_index]);
|
||||
kvfSubmitCommandBuffer(RenderCore::Get().GetDevice(), m_cmd_buffers[m_current_frame_index], KVF_GRAPHICS_QUEUE, m_render_finished_semaphores[m_swapchain.GetImageIndex()], m_image_available_semaphores[m_current_frame_index], m_cmd_fences[m_current_frame_index], wait_stages);
|
||||
m_swapchain.Present(m_render_finished_semaphores[m_swapchain.GetImageIndex()]);
|
||||
m_current_frame_index = (m_current_frame_index + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||
}
|
||||
|
||||
void Renderer::Destroy() noexcept
|
||||
{
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
for(std::size_t i = 0; i < m_render_finished_semaphores.size(); i++)
|
||||
{
|
||||
kvfDestroySemaphore(RenderCore::Get().GetDevice(), m_render_finished_semaphores[i]);
|
||||
Message("Vulkan: render finished semaphore destroyed");
|
||||
}
|
||||
m_render_finished_semaphores.clear();
|
||||
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||
{
|
||||
kvfDestroySemaphore(RenderCore::Get().GetDevice(), m_image_available_semaphores[i]);
|
||||
Message("Vulkan: image available semaphore destroyed");
|
||||
kvfDestroyCommandBuffer(RenderCore::Get().GetDevice(), m_cmd_buffers[i]);
|
||||
Message("Vulkan: command buffer destroyed");
|
||||
kvfDestroyFence(RenderCore::Get().GetDevice(), m_cmd_fences[i]);
|
||||
Message("Vulkan: fence destroyed");
|
||||
}
|
||||
m_swapchain.Destroy();
|
||||
}
|
||||
}
|
||||
43
Runtime/Sources/Renderer/SceneRenderer.cpp
git.filemode.normal_file
43
Runtime/Sources/Renderer/SceneRenderer.cpp
git.filemode.normal_file
@@ -0,0 +1,43 @@
|
||||
#include <Renderer/ScenesRenderer.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Graphics/Scene.h>
|
||||
#include <Renderer/ViewerData.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
void SceneRenderer::Init()
|
||||
{
|
||||
m_passes.Init();
|
||||
}
|
||||
|
||||
void SceneRenderer::Render(Scene& scene, Renderer& renderer)
|
||||
{
|
||||
if(scene.GetCamera())
|
||||
{
|
||||
ViewerData data;
|
||||
data.projection_matrix = scene.GetCamera()->GetProj();
|
||||
data.projection_matrix.GetInverse(&data.inv_projection_matrix);
|
||||
data.view_matrix = scene.GetCamera()->GetView();
|
||||
data.view_matrix.GetInverse(&data.inv_view_matrix);
|
||||
data.view_proj_matrix = data.view_matrix * data.projection_matrix;
|
||||
data.view_proj_matrix.GetInverse(&data.inv_view_proj_matrix);
|
||||
data.camera_position = scene.GetCamera()->GetPosition();
|
||||
|
||||
static CPUBuffer buffer(sizeof(ViewerData));
|
||||
std::memcpy(buffer.GetData(), &data, buffer.GetSize());
|
||||
scene.GetForwardData().matrices_buffer->SetData(buffer, renderer.GetCurrentFrameIndex());
|
||||
}
|
||||
if(scene.GetDescription().render_post_process_enabled && scene.GetDescription().post_process_shader)
|
||||
scene.GetPostProcessData().data_buffer->SetData(scene.GetPostProcessData().data, renderer.GetCurrentFrameIndex());
|
||||
|
||||
m_passes.Pass(scene, renderer);
|
||||
}
|
||||
|
||||
void SceneRenderer::Destroy()
|
||||
{
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
m_passes.Destroy();
|
||||
}
|
||||
}
|
||||
254
Runtime/Sources/Renderer/Swapchain.cpp
git.filemode.normal_file
254
Runtime/Sources/Renderer/Swapchain.cpp
git.filemode.normal_file
@@ -0,0 +1,254 @@
|
||||
#include <Platform/Window.h>
|
||||
#include <Renderer/Swapchain.h>
|
||||
#include <Renderer/RenderCore.h>
|
||||
#include <Core/Logs.h>
|
||||
#include <Core/EventBus.h>
|
||||
#include <Maths/Vec2.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
struct ResizeEventBroadcast : public EventBase
|
||||
{
|
||||
Event What() const override { return Event::ResizeEventCode; }
|
||||
};
|
||||
}
|
||||
|
||||
std::string VulkanFormatName(VkFormat format)
|
||||
{
|
||||
#define STRINGIFY(x) case x: return #x
|
||||
|
||||
switch(format)
|
||||
{
|
||||
STRINGIFY(VK_FORMAT_UNDEFINED);
|
||||
STRINGIFY(VK_FORMAT_R4G4_UNORM_PACK8);
|
||||
STRINGIFY(VK_FORMAT_R4G4B4A4_UNORM_PACK16);
|
||||
STRINGIFY(VK_FORMAT_B4G4R4A4_UNORM_PACK16);
|
||||
STRINGIFY(VK_FORMAT_R5G6B5_UNORM_PACK16);
|
||||
STRINGIFY(VK_FORMAT_B5G6R5_UNORM_PACK16);
|
||||
STRINGIFY(VK_FORMAT_R5G5B5A1_UNORM_PACK16);
|
||||
STRINGIFY(VK_FORMAT_B5G5R5A1_UNORM_PACK16);
|
||||
STRINGIFY(VK_FORMAT_A1R5G5B5_UNORM_PACK16);
|
||||
STRINGIFY(VK_FORMAT_R8_UNORM);
|
||||
STRINGIFY(VK_FORMAT_R8_SNORM);
|
||||
STRINGIFY(VK_FORMAT_R8_USCALED);
|
||||
STRINGIFY(VK_FORMAT_R8_SSCALED);
|
||||
STRINGIFY(VK_FORMAT_R8_UINT);
|
||||
STRINGIFY(VK_FORMAT_R8_SINT);
|
||||
STRINGIFY(VK_FORMAT_R8_SRGB);
|
||||
STRINGIFY(VK_FORMAT_R8G8_UNORM);
|
||||
STRINGIFY(VK_FORMAT_R8G8_SNORM);
|
||||
STRINGIFY(VK_FORMAT_R8G8_USCALED);
|
||||
STRINGIFY(VK_FORMAT_R8G8_SSCALED);
|
||||
STRINGIFY(VK_FORMAT_R8G8_UINT);
|
||||
STRINGIFY(VK_FORMAT_R8G8_SINT);
|
||||
STRINGIFY(VK_FORMAT_R8G8_SRGB);
|
||||
STRINGIFY(VK_FORMAT_R8G8B8_UNORM);
|
||||
STRINGIFY(VK_FORMAT_R8G8B8_SNORM);
|
||||
STRINGIFY(VK_FORMAT_R8G8B8_USCALED);
|
||||
STRINGIFY(VK_FORMAT_R8G8B8_SSCALED);
|
||||
STRINGIFY(VK_FORMAT_R8G8B8_UINT);
|
||||
STRINGIFY(VK_FORMAT_R8G8B8_SINT);
|
||||
STRINGIFY(VK_FORMAT_R8G8B8_SRGB);
|
||||
STRINGIFY(VK_FORMAT_B8G8R8_UNORM);
|
||||
STRINGIFY(VK_FORMAT_B8G8R8_SNORM);
|
||||
STRINGIFY(VK_FORMAT_B8G8R8_USCALED);
|
||||
STRINGIFY(VK_FORMAT_B8G8R8_SSCALED);
|
||||
STRINGIFY(VK_FORMAT_B8G8R8_UINT);
|
||||
STRINGIFY(VK_FORMAT_B8G8R8_SINT);
|
||||
STRINGIFY(VK_FORMAT_B8G8R8_SRGB);
|
||||
STRINGIFY(VK_FORMAT_R8G8B8A8_UNORM);
|
||||
STRINGIFY(VK_FORMAT_R8G8B8A8_SNORM);
|
||||
STRINGIFY(VK_FORMAT_R8G8B8A8_USCALED);
|
||||
STRINGIFY(VK_FORMAT_R8G8B8A8_SSCALED);
|
||||
STRINGIFY(VK_FORMAT_R8G8B8A8_UINT);
|
||||
STRINGIFY(VK_FORMAT_R8G8B8A8_SINT);
|
||||
STRINGIFY(VK_FORMAT_R8G8B8A8_SRGB);
|
||||
STRINGIFY(VK_FORMAT_B8G8R8A8_UNORM);
|
||||
STRINGIFY(VK_FORMAT_B8G8R8A8_SNORM);
|
||||
STRINGIFY(VK_FORMAT_B8G8R8A8_USCALED);
|
||||
STRINGIFY(VK_FORMAT_B8G8R8A8_SSCALED);
|
||||
STRINGIFY(VK_FORMAT_B8G8R8A8_UINT);
|
||||
STRINGIFY(VK_FORMAT_B8G8R8A8_SINT);
|
||||
STRINGIFY(VK_FORMAT_B8G8R8A8_SRGB);
|
||||
STRINGIFY(VK_FORMAT_A8B8G8R8_UNORM_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A8B8G8R8_SNORM_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A8B8G8R8_USCALED_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A8B8G8R8_SSCALED_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A8B8G8R8_UINT_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A8B8G8R8_SINT_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A8B8G8R8_SRGB_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A2R10G10B10_UNORM_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A2R10G10B10_SNORM_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A2R10G10B10_USCALED_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A2R10G10B10_SSCALED_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A2R10G10B10_UINT_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A2R10G10B10_SINT_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A2B10G10R10_UNORM_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A2B10G10R10_SNORM_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A2B10G10R10_USCALED_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A2B10G10R10_SSCALED_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A2B10G10R10_UINT_PACK32);
|
||||
STRINGIFY(VK_FORMAT_A2B10G10R10_SINT_PACK32);
|
||||
STRINGIFY(VK_FORMAT_R16_UNORM);
|
||||
STRINGIFY(VK_FORMAT_R16_SNORM);
|
||||
STRINGIFY(VK_FORMAT_R16_USCALED);
|
||||
STRINGIFY(VK_FORMAT_R16_SSCALED);
|
||||
STRINGIFY(VK_FORMAT_R16_UINT);
|
||||
STRINGIFY(VK_FORMAT_R16_SINT);
|
||||
STRINGIFY(VK_FORMAT_R16_SFLOAT);
|
||||
STRINGIFY(VK_FORMAT_R16G16_UNORM);
|
||||
STRINGIFY(VK_FORMAT_R16G16_SNORM);
|
||||
STRINGIFY(VK_FORMAT_R16G16_USCALED);
|
||||
STRINGIFY(VK_FORMAT_R16G16_SSCALED);
|
||||
STRINGIFY(VK_FORMAT_R16G16_UINT);
|
||||
STRINGIFY(VK_FORMAT_R16G16_SINT);
|
||||
STRINGIFY(VK_FORMAT_R16G16_SFLOAT);
|
||||
STRINGIFY(VK_FORMAT_R16G16B16_UNORM);
|
||||
STRINGIFY(VK_FORMAT_R16G16B16_SNORM);
|
||||
STRINGIFY(VK_FORMAT_R16G16B16_USCALED);
|
||||
STRINGIFY(VK_FORMAT_R16G16B16_SSCALED);
|
||||
STRINGIFY(VK_FORMAT_R16G16B16_UINT);
|
||||
STRINGIFY(VK_FORMAT_R16G16B16_SINT);
|
||||
STRINGIFY(VK_FORMAT_R16G16B16_SFLOAT);
|
||||
STRINGIFY(VK_FORMAT_R16G16B16A16_UNORM);
|
||||
STRINGIFY(VK_FORMAT_R16G16B16A16_SNORM);
|
||||
STRINGIFY(VK_FORMAT_R16G16B16A16_USCALED);
|
||||
STRINGIFY(VK_FORMAT_R16G16B16A16_SSCALED);
|
||||
STRINGIFY(VK_FORMAT_R16G16B16A16_UINT);
|
||||
STRINGIFY(VK_FORMAT_R16G16B16A16_SINT);
|
||||
STRINGIFY(VK_FORMAT_R16G16B16A16_SFLOAT);
|
||||
STRINGIFY(VK_FORMAT_R32_UINT);
|
||||
STRINGIFY(VK_FORMAT_R32_SINT);
|
||||
STRINGIFY(VK_FORMAT_R32_SFLOAT);
|
||||
STRINGIFY(VK_FORMAT_R32G32_UINT);
|
||||
STRINGIFY(VK_FORMAT_R32G32_SINT);
|
||||
STRINGIFY(VK_FORMAT_R32G32_SFLOAT);
|
||||
STRINGIFY(VK_FORMAT_R32G32B32_UINT);
|
||||
STRINGIFY(VK_FORMAT_R32G32B32_SINT);
|
||||
STRINGIFY(VK_FORMAT_R32G32B32_SFLOAT);
|
||||
STRINGIFY(VK_FORMAT_R32G32B32A32_UINT);
|
||||
STRINGIFY(VK_FORMAT_R32G32B32A32_SINT);
|
||||
STRINGIFY(VK_FORMAT_R32G32B32A32_SFLOAT);
|
||||
STRINGIFY(VK_FORMAT_R64_UINT);
|
||||
STRINGIFY(VK_FORMAT_R64_SINT);
|
||||
STRINGIFY(VK_FORMAT_R64_SFLOAT);
|
||||
STRINGIFY(VK_FORMAT_R64G64_UINT);
|
||||
STRINGIFY(VK_FORMAT_R64G64_SINT);
|
||||
STRINGIFY(VK_FORMAT_R64G64_SFLOAT);
|
||||
STRINGIFY(VK_FORMAT_R64G64B64_UINT);
|
||||
STRINGIFY(VK_FORMAT_R64G64B64_SINT);
|
||||
STRINGIFY(VK_FORMAT_R64G64B64_SFLOAT);
|
||||
STRINGIFY(VK_FORMAT_R64G64B64A64_UINT);
|
||||
STRINGIFY(VK_FORMAT_R64G64B64A64_SINT);
|
||||
STRINGIFY(VK_FORMAT_R64G64B64A64_SFLOAT);
|
||||
STRINGIFY(VK_FORMAT_B10G11R11_UFLOAT_PACK32);
|
||||
STRINGIFY(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32);
|
||||
|
||||
default: return "Unknown format";
|
||||
}
|
||||
|
||||
#undef STRINGIFY
|
||||
|
||||
return "Unknown format"; // To avoid warnings
|
||||
}
|
||||
|
||||
void Swapchain::Init(NonOwningPtr<Window> window)
|
||||
{
|
||||
p_window = window;
|
||||
|
||||
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
|
||||
{
|
||||
if(event.What() == Event::ResizeEventCode && !m_resize)
|
||||
m_resize = true;
|
||||
};
|
||||
EventBus::RegisterListener({ functor, "__ScopSwapchain" });
|
||||
|
||||
CreateSwapchain();
|
||||
m_resize = false;
|
||||
}
|
||||
|
||||
void Swapchain::AquireFrame(VkSemaphore signal)
|
||||
{
|
||||
if(m_resize)
|
||||
{
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
Destroy();
|
||||
CreateSwapchain();
|
||||
EventBus::SendBroadcast(Internal::ResizeEventBroadcast{});
|
||||
m_resize = false;
|
||||
}
|
||||
|
||||
VkResult result = RenderCore::Get().vkAcquireNextImageKHR(RenderCore::Get().GetDevice(), m_swapchain, UINT64_MAX, signal, VK_NULL_HANDLE, &m_current_image_index);
|
||||
if(result == VK_SUBOPTIMAL_KHR)
|
||||
m_resize = true; // Recreate Swapchain next time
|
||||
else if(result == VK_ERROR_OUT_OF_DATE_KHR)
|
||||
{
|
||||
m_resize = true;
|
||||
AquireFrame(signal);
|
||||
}
|
||||
else if(result != VK_SUCCESS)
|
||||
FatalError("Vulkan: failed to acquire swapchain image, %", kvfVerbaliseVkResult(result));
|
||||
}
|
||||
|
||||
void Swapchain::Present(VkSemaphore wait) noexcept
|
||||
{
|
||||
if(!kvfQueuePresentKHR(RenderCore::Get().GetDevice(), wait, m_swapchain, m_current_image_index))
|
||||
m_resize = true;
|
||||
}
|
||||
|
||||
void Swapchain::Destroy()
|
||||
{
|
||||
if(m_swapchain == VK_NULL_HANDLE)
|
||||
return;
|
||||
RenderCore::Get().WaitDeviceIdle();
|
||||
|
||||
for(Image& img : m_swapchain_images)
|
||||
img.DestroyImageView();
|
||||
m_swapchain_images.clear();
|
||||
kvfDestroySwapchainKHR(RenderCore::Get().GetDevice(), m_swapchain);
|
||||
m_swapchain = VK_NULL_HANDLE;
|
||||
Message("Vulkan: swapchain destroyed");
|
||||
|
||||
RenderCore::Get().vkDestroySurfaceKHR(RenderCore::Get().GetInstance(), m_surface, nullptr);
|
||||
m_surface = VK_NULL_HANDLE;
|
||||
Message("Vulkan: surface destroyed");
|
||||
}
|
||||
|
||||
void Swapchain::CreateSwapchain()
|
||||
{
|
||||
VkExtent2D extent;
|
||||
do
|
||||
{
|
||||
Vec2ui size = p_window->GetVulkanDrawableSize();
|
||||
extent = { size.x, size.y };
|
||||
} while(extent.width == 0 || extent.height == 0);
|
||||
|
||||
m_surface = p_window->CreateVulkanSurface(RenderCore::Get().GetInstance());
|
||||
Message("Vulkan: surface created");
|
||||
m_swapchain = kvfCreateSwapchainKHR(RenderCore::Get().GetDevice(), RenderCore::Get().GetPhysicalDevice(), m_surface, extent, VK_NULL_HANDLE, false, true);
|
||||
|
||||
m_images_count = kvfGetSwapchainImagesCount(m_swapchain);
|
||||
m_min_images_count = kvfGetSwapchainMinImagesCount(m_swapchain);
|
||||
std::vector<VkImage> tmp(m_images_count);
|
||||
m_swapchain_images.resize(m_images_count);
|
||||
RenderCore::Get().vkGetSwapchainImagesKHR(RenderCore::Get().GetDevice(), m_swapchain, &m_images_count, tmp.data());
|
||||
VkCommandBuffer cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
|
||||
kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
|
||||
extent = kvfGetSwapchainImagesSize(m_swapchain); // fix the extent
|
||||
VkFormat format = kvfGetSwapchainImagesFormat(m_swapchain);
|
||||
for(std::size_t i = 0; i < m_images_count; i++)
|
||||
{
|
||||
m_swapchain_images[i].Init(tmp[i], format, extent.width, extent.height, VK_IMAGE_LAYOUT_UNDEFINED, std::string{ "swapchain_" + std::to_string(i) });
|
||||
m_swapchain_images[i].TransitionLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, cmd);
|
||||
m_swapchain_images[i].CreateImageView(VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
}
|
||||
kvfEndCommandBuffer(cmd);
|
||||
VkFence fence = kvfCreateFence(RenderCore::Get().GetDevice());
|
||||
kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, fence);
|
||||
kvfDestroyFence(RenderCore::Get().GetDevice(), fence);
|
||||
kvfDestroyCommandBuffer(RenderCore::Get().GetDevice(), cmd);
|
||||
Message("Vulkan: swapchain created with format %", VulkanFormatName(format));
|
||||
}
|
||||
}
|
||||
119
Runtime/Sources/Renderer/Vulkan/VulkanLoader.cpp
git.filemode.normal_file
119
Runtime/Sources/Renderer/Vulkan/VulkanLoader.cpp
git.filemode.normal_file
@@ -0,0 +1,119 @@
|
||||
#include <Renderer/Vulkan/VulkanLoader.h>
|
||||
#include <Renderer/RenderCore.h>
|
||||
#include <Core/Logs.h>
|
||||
|
||||
#include <array>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define DISABLE_GCC_PEDANTIC_WARNINGS \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wpedantic\"")
|
||||
#define RESTORE_GCC_PEDANTIC_WARNINGS \
|
||||
_Pragma("GCC diagnostic pop")
|
||||
#else
|
||||
#define DISABLE_GCC_PEDANTIC_WARNINGS
|
||||
#define RESTORE_GCC_PEDANTIC_WARNINGS
|
||||
#endif
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
static inline PFN_vkVoidFunction vkGetInstanceProcAddrStub(void* context, const char* name)
|
||||
{
|
||||
PFN_vkVoidFunction function = RenderCore::Get().vkGetInstanceProcAddr(static_cast<VkInstance>(context), name);
|
||||
if(!function)
|
||||
FatalError("Vulkan Loader: could not load '%'", name);
|
||||
//Message("Vulkan Loader: loaded %", name);
|
||||
return function;
|
||||
}
|
||||
|
||||
static inline PFN_vkVoidFunction vkGetDeviceProcAddrStub(void* context, const char* name)
|
||||
{
|
||||
PFN_vkVoidFunction function = RenderCore::Get().vkGetDeviceProcAddr(static_cast<VkDevice>(context), name);
|
||||
if(!function)
|
||||
FatalError("Vulkan Loader: could not load '%'", name);
|
||||
//Message("Vulkan Loader: loaded %", name);
|
||||
return function;
|
||||
}
|
||||
|
||||
static inline void* LoadLib(const char* libname)
|
||||
{
|
||||
return dlopen(libname, RTLD_NOW | RTLD_LOCAL);
|
||||
}
|
||||
|
||||
static inline void* GetSymbol(void* module, const char* name)
|
||||
{
|
||||
return dlsym(module, name);
|
||||
}
|
||||
}
|
||||
|
||||
VulkanLoader::VulkanLoader()
|
||||
{
|
||||
std::array libnames{
|
||||
"libvulkan.so.1",
|
||||
"libvulkan.so"
|
||||
};
|
||||
|
||||
for(auto libname : libnames)
|
||||
{
|
||||
p_module = Internal::LoadLib(libname);
|
||||
if(p_module != nullptr)
|
||||
{
|
||||
DISABLE_GCC_PEDANTIC_WARNINGS;
|
||||
RenderCore::Get().vkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(Internal::GetSymbol(p_module, "vkGetInstanceProcAddr"));
|
||||
RESTORE_GCC_PEDANTIC_WARNINGS;
|
||||
if(RenderCore::Get().vkGetInstanceProcAddr)
|
||||
{
|
||||
Message("Vulkan Loader: libvulkan loaded using '%'", libname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!p_module || !RenderCore::Get().vkGetInstanceProcAddr)
|
||||
FatalError("Vulkan Loader: failed to load libvulkan");
|
||||
LoadGlobalFunctions(nullptr, Internal::vkGetInstanceProcAddrStub);
|
||||
}
|
||||
|
||||
void VulkanLoader::LoadInstance(VkInstance instance)
|
||||
{
|
||||
LoadInstanceFunctions(instance, Internal::vkGetInstanceProcAddrStub);
|
||||
}
|
||||
|
||||
void VulkanLoader::LoadDevice(VkDevice device)
|
||||
{
|
||||
LoadDeviceFunctions(device, Internal::vkGetDeviceProcAddrStub);
|
||||
}
|
||||
|
||||
void VulkanLoader::LoadGlobalFunctions(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) noexcept
|
||||
{
|
||||
#define SCOP_VULKAN_GLOBAL_FUNCTION(fn) RenderCore::Get().fn = reinterpret_cast<PFN_##fn>(load(context, #fn));
|
||||
#include <Renderer/Vulkan/VulkanDefs.h>
|
||||
#undef SCOP_VULKAN_GLOBAL_FUNCTION
|
||||
Message("Vulkan Loader: global functions loaded");
|
||||
}
|
||||
|
||||
void VulkanLoader::LoadInstanceFunctions(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) noexcept
|
||||
{
|
||||
#define SCOP_VULKAN_INSTANCE_FUNCTION(fn) RenderCore::Get().fn = reinterpret_cast<PFN_##fn>(load(context, #fn));
|
||||
#include <Renderer/Vulkan/VulkanDefs.h>
|
||||
#undef SCOP_VULKAN_INSTANCE_FUNCTION
|
||||
Message("Vulkan Loader: instance functions loaded");
|
||||
}
|
||||
|
||||
void VulkanLoader::LoadDeviceFunctions(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) noexcept
|
||||
{
|
||||
#define SCOP_VULKAN_DEVICE_FUNCTION(fn) RenderCore::Get().fn = reinterpret_cast<PFN_##fn>(load(context, #fn));
|
||||
#include <Renderer/Vulkan/VulkanDefs.h>
|
||||
#undef SCOP_VULKAN_DEVICE_FUNCTION
|
||||
Message("Vulkan Loader: device functions loaded");
|
||||
}
|
||||
|
||||
VulkanLoader::~VulkanLoader()
|
||||
{
|
||||
dlclose(p_module);
|
||||
p_module = nullptr;
|
||||
Message("Vulkan Loader: libvulkan unloaded");
|
||||
}
|
||||
}
|
||||
26
Runtime/Sources/Renderer/Vulkan/VulkanLoader.h
git.filemode.normal_file
26
Runtime/Sources/Renderer/Vulkan/VulkanLoader.h
git.filemode.normal_file
@@ -0,0 +1,26 @@
|
||||
#ifndef __SCOP_VULKAN_LOADER__
|
||||
#define __SCOP_VULKAN_LOADER__
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
namespace Scop
|
||||
{
|
||||
class VulkanLoader
|
||||
{
|
||||
public:
|
||||
VulkanLoader();
|
||||
void LoadInstance(VkInstance instance);
|
||||
void LoadDevice(VkDevice device);
|
||||
~VulkanLoader();
|
||||
|
||||
private:
|
||||
void LoadGlobalFunctions(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) noexcept;
|
||||
void LoadInstanceFunctions(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) noexcept;
|
||||
void LoadDeviceFunctions(void* context, PFN_vkVoidFunction (*load)(void*, const char*)) noexcept;
|
||||
|
||||
private:
|
||||
void* p_module = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user