begenning the refactor

This commit is contained in:
2024-03-27 23:03:54 +01:00
parent e5ff232065
commit 6bbf1e196d
131 changed files with 2135 additions and 1192 deletions

116
runtime/Sources/Core/Application.cpp git.filemode.normal_file
View File

@@ -0,0 +1,116 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Application.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/04 22:10:52 by maldavid #+# #+# */
/* Updated: 2024/03/27 17:43:18 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <PreCompiled.h>
#include <Core/Application.h>
#include <renderer/texts/text_library.h>
#include <renderer/texts/font_library.h>
#include <renderer/images/texture.h>
#include <renderer/core/render_core.h>
#include <core/errors.h>
#include <mlx_profile.h>
#include <core/memory.h>
#include <Core/EventBus.h>
namespace mlx::core
{
Application::Application() : _fps(), _in(std::make_unique<Input>())
{
EventBus::RegisterListener({[](const EventBase& event)
{
}, "__internal_application" });
_fps.init();
glfwSetErrorCallback([]([[maybe_unused]] int code, const char* desc)
{
error::report(e_kind::fatal_error, "GLFW error : %s", desc);
});
glfwInit();
}
void Application::run() noexcept
{
while(_in->isRunning())
{
if(!_fps.update())
continue;
_in->update();
if(_loop_hook)
_loop_hook(_param);
for(auto& gs : _graphics)
gs->render();
}
Render_Core::get().getSingleTimeCmdManager().updateSingleTimesCmdBuffersSubmitState();
for(auto& gs : _graphics)
{
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
gs->getRenderer().getCmdBuffer(i).waitForExecution();
}
}
void* Application::newTexture(int w, int h)
{
MLX_PROFILE_FUNCTION();
#ifdef DEBUG
_textures.emplace_front().create(nullptr, w, h, VK_FORMAT_R8G8B8A8_UNORM, "__mlx_unamed_user_texture");
#else
_textures.emplace_front().create(nullptr, w, h, VK_FORMAT_R8G8B8A8_UNORM, nullptr);
#endif
return &_textures.front();
}
void* Application::newStbTexture(char* file, int* w, int* h)
{
MLX_PROFILE_FUNCTION();
_textures.emplace_front(stbTextureLoad(file, w, h));
return &_textures.front();
}
void Application::destroyTexture(void* ptr)
{
MLX_PROFILE_FUNCTION();
vkDeviceWaitIdle(Render_Core::get().getDevice().get()); // TODO : synchronize with another method than waiting for GPU to be idle
if(ptr == nullptr)
{
core::error::report(e_kind::error, "invalid image ptr (NULL)");
return;
}
auto it = std::find_if(_textures.begin(), _textures.end(), [=](const Texture& texture) { return &texture == ptr; });
if(it == _textures.end())
{
core::error::report(e_kind::error, "invalid image ptr");
return;
}
Texture* texture = static_cast<Texture*>(ptr);
if(!texture->isInit())
core::error::report(e_kind::error, "trying to destroy a texture that has already been destroyed");
else
texture->destroy();
for(auto& gs : _graphics)
gs->tryEraseTextureFromManager(texture);
_textures.erase(it);
}
Application::~Application()
{
TextLibrary::get().clearLibrary();
FontLibrary::get().clearLibrary();
glfwTerminate();
}
}

304
runtime/Sources/Core/Bridge.cpp git.filemode.normal_file
View File

@@ -0,0 +1,304 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* bridge.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/04 17:35:20 by maldavid #+# #+# */
/* Updated: 2024/03/25 23:05:46 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <pre_compiled.h>
#include "errors.h"
#include "application.h"
#include <renderer/core/render_core.h>
#include <mlx.h>
#include <core/memory.h>
static void* __mlx_ptr = nullptr;
#define MLX_CHECK_APPLICATION_POINTER(ptr) \
if(ptr != __mlx_ptr || ptr == NULL) \
mlx::core::error::report(e_kind::fatal_error, "invalid mlx pointer passed to '%s'", MLX_FUNC_SIG); \
else {} // just to avoid issues with possible if-else statements outside this macro
extern "C"
{
void* mlx_init()
{
if(__mlx_ptr != nullptr)
{
mlx::core::error::report(e_kind::error, "MLX cannot be initialized multiple times");
return NULL; // not nullptr for the C compatibility
}
mlx::MemManager::get(); // just to initialize the C garbage collector
mlx::core::Application* app = new mlx::core::Application;
mlx::Render_Core::get().init();
if(app == nullptr)
mlx::core::error::report(e_kind::fatal_error, "Tout a pété");
__mlx_ptr = static_cast<void*>(app);
return __mlx_ptr;
}
void* mlx_new_window(void* mlx, int w, int h, const char* title)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
if(w <= 0 || h <= 0)
{
mlx::core::error::report(e_kind::fatal_error, "invalid window size (%d x %d)", w, h);
return NULL; // not nullptr for the C compatibility
}
return static_cast<mlx::core::Application*>(mlx)->newGraphicsSuport(w, h, title);
}
int mlx_loop_hook(void* mlx, int (*f)(void*), void* param)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
static_cast<mlx::core::Application*>(mlx)->loopHook(f, param);
return 0;
}
int mlx_loop(void* mlx)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
static_cast<mlx::core::Application*>(mlx)->run();
return 0;
}
int mlx_loop_end(void* mlx)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
static_cast<mlx::core::Application*>(mlx)->loopEnd();
return 0;
}
int mlx_mouse_show()
{
return 0;
}
int mlx_mouse_hide()
{
return 0;
}
int mlx_mouse_move(void* mlx, void* win, int x, int y)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
static_cast<mlx::core::Application*>(mlx)->mouseMove(win, x, y);
return 0;
}
int mlx_mouse_get_pos(void* mlx, int* x, int* y)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
static_cast<mlx::core::Application*>(mlx)->getMousePos(x, y);
return 0;
}
int mlx_on_event(void* mlx, void* win, mlx_event_type event, int (*funct_ptr)(int, void*), void* param)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
static_cast<mlx::core::Application*>(mlx)->onEvent(win, static_cast<int>(event), funct_ptr, param);
return 0;
}
void* mlx_new_image(void* mlx, int width, int height)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
if (width <= 0 || height <= 0)
mlx::core::error::report(e_kind::fatal_error, "invalid image size (%d x %d)", width, height);
return static_cast<mlx::core::Application*>(mlx)->newTexture(width, height);
}
int mlx_get_image_pixel(void* mlx, void* img, int x, int y)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
int color = static_cast<mlx::core::Application*>(mlx)->getTexturePixel(img, x, y);
unsigned char color_bits[4];
color_bits[0] = (color & 0x000000FF);
color_bits[1] = (color & 0x0000FF00) >> 8;
color_bits[2] = (color & 0x00FF0000) >> 16;
color_bits[3] = (color & 0xFF000000) >> 24;
return *reinterpret_cast<int*>(color_bits);
}
void mlx_set_image_pixel(void* mlx, void* img, int x, int y, int color)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
unsigned char color_bits[4];
color_bits[0] = (color & 0x00FF0000) >> 16;
color_bits[1] = (color & 0x0000FF00) >> 8;
color_bits[2] = (color & 0x000000FF);
color_bits[3] = (color & 0xFF000000) >> 24;
static_cast<mlx::core::Application*>(mlx)->setTexturePixel(img, x, y, *reinterpret_cast<unsigned int*>(color_bits));
}
int mlx_put_image_to_window(void* mlx, void* win, void* img, int x, int y)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
static_cast<mlx::core::Application*>(mlx)->texturePut(win, img, x, y);
return 0;
}
int mlx_destroy_image(void* mlx, void* img)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
static_cast<mlx::core::Application*>(mlx)->destroyTexture(img);
return 0;
}
void* mlx_png_file_to_image(void* mlx, char* filename, int* width, int* height)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
if (filename == nullptr)
mlx::core::error::report(e_kind::fatal_error, "PNG loader : filename is NULL");
std::filesystem::path file(filename);
if(file.extension() != ".png")
{
mlx::core::error::report(e_kind::error, "PNG loader : not a png file '%s'", filename);
return nullptr;
}
return static_cast<mlx::core::Application*>(mlx)->newStbTexture(filename, width, height);
}
void* mlx_jpg_file_to_image(void* mlx, char* filename, int* width, int* height)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
if (filename == nullptr)
mlx::core::error::report(e_kind::fatal_error, "JPG loader : filename is NULL");
std::filesystem::path file(filename);
if(file.extension() != ".jpg" && file.extension() != ".jpeg")
{
mlx::core::error::report(e_kind::error, "JPG loader : not a jpg file '%s'", filename);
return nullptr;
}
return static_cast<mlx::core::Application*>(mlx)->newStbTexture(filename, width, height);
}
void* mlx_bmp_file_to_image(void* mlx, char* filename, int* width, int* height)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
if (filename == nullptr)
mlx::core::error::report(e_kind::fatal_error, "BMP loader : filename is NULL");
std::filesystem::path file(filename);
if(file.extension() != ".bmp" && file.extension() != ".dib")
{
mlx::core::error::report(e_kind::error, "BMP loader : not a bmp file '%s'", filename);
return nullptr;
}
return static_cast<mlx::core::Application*>(mlx)->newStbTexture(filename, width, height);
}
int mlx_pixel_put(void* mlx, void* win, int x, int y, int color)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
unsigned char color_bits[4];
color_bits[0] = (color & 0x00FF0000) >> 16;
color_bits[1] = (color & 0x0000FF00) >> 8;
color_bits[2] = (color & 0x000000FF);
color_bits[3] = (color & 0xFF000000) >> 24;
static_cast<mlx::core::Application*>(mlx)->pixelPut(win, x, y, *reinterpret_cast<unsigned int*>(color_bits));
return 0;
}
int mlx_string_put(void* mlx, void* win, int x, int y, int color, char* str)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
unsigned char color_bits[4];
color_bits[0] = (color & 0x00FF0000) >> 16;
color_bits[1] = (color & 0x0000FF00) >> 8;
color_bits[2] = (color & 0x000000FF);
color_bits[3] = (color & 0xFF000000) >> 24;
static_cast<mlx::core::Application*>(mlx)->stringPut(win, x, y, *reinterpret_cast<unsigned int*>(color_bits), str);
return 0;
}
void mlx_set_font(void* mlx, void* win, char* filepath)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
if (filepath == nullptr)
{
mlx::core::error::report(e_kind::error, "Font loader : filepath is NULL");
return;
}
std::filesystem::path file(filepath);
if(std::strcmp(filepath, "default") != 0 && file.extension() != ".ttf" && file.extension() != ".tte")
{
mlx::core::error::report(e_kind::error, "TTF loader : not a truetype font file '%s'", filepath);
return;
}
if(std::strcmp(filepath, "default") == 0)
static_cast<mlx::core::Application*>(mlx)->loadFont(win, file, 6.f);
else
static_cast<mlx::core::Application*>(mlx)->loadFont(win, file, 16.f);
}
void mlx_set_font_scale(void* mlx, void* win, char* filepath, float scale)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
if (filepath == nullptr)
{
mlx::core::error::report(e_kind::error, "Font loader : filepath is NULL");
return;
}
std::filesystem::path file(filepath);
if(std::strcmp(filepath, "default") != 0 && file.extension() != ".ttf" && file.extension() != ".tte")
{
mlx::core::error::report(e_kind::error, "TTF loader : not a truetype font file '%s'", filepath);
return;
}
static_cast<mlx::core::Application*>(mlx)->loadFont(win, file, scale);
}
int mlx_clear_window(void* mlx, void* win)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
static_cast<mlx::core::Application*>(mlx)->clearGraphicsSupport(win);
return 0;
}
int mlx_destroy_window(void* mlx, void* win)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
static_cast<mlx::core::Application*>(mlx)->destroyGraphicsSupport(win);
return 0;
}
int mlx_destroy_display(void* mlx)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
delete static_cast<mlx::core::Application*>(mlx);
mlx::Render_Core::get().destroy();
__mlx_ptr = nullptr;
return 0;
}
int mlx_get_screens_size(void* mlx, void* win, int* w, int* h)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
static_cast<mlx::core::Application*>(mlx)->getScreenSize(win, w, h);
return 0;
}
int mlx_set_fps_goal(void* mlx, int fps)
{
MLX_CHECK_APPLICATION_POINTER(mlx);
if(fps < 0)
{
mlx::core::error::report(e_kind::error, "You cannot set a negative FPS cap (nice try)");
fps = -fps;
}
if(fps == 0)
{
mlx::core::error::report(e_kind::error, "You cannot set a FPS cap to 0 (nice try)");
return 0;
}
static_cast<mlx::core::Application*>(mlx)->setFPSCap(static_cast<std::uint32_t>(fps));
return 0;
}
}

37
runtime/Sources/Core/EventBus.cpp git.filemode.normal_file
View File

@@ -0,0 +1,37 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* EventBus.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/03/27 17:36:05 by maldavid #+# #+# */
/* Updated: 2024/03/27 17:37:01 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <PreCompiled.h>
#include <Core/EventBus.h>
#include <Core/Logs.h>
namespace mlx
{
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);
}
}

21
runtime/Sources/Core/EventListener.cpp git.filemode.normal_file
View File

@@ -0,0 +1,21 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* EventListener.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/03/27 17:37:09 by maldavid #+# #+# */
/* Updated: 2024/03/27 17:37:38 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <PreCompiled.h>
#include <Core/EventListener.h>
namespace mlx
{
EventListener::EventListener(func::function<void(const EventBase&)> functor, std::string name)
: m_listen_functor(std::move(functor)), m_name(std::move(name))
{}
}

42
runtime/Sources/Core/Fps.cpp git.filemode.normal_file
View File

@@ -0,0 +1,42 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Fps.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/01/18 14:56:17 by maldavid #+# #+# */
/* Updated: 2024/03/27 20:53:11 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <PreCompiled.h>
#include <Core/Fps.h>
namespace mlx
{
void FpsManager::Init()
{
m_timer = static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
m_fps_before = m_timer;
m_fps_now = m_timer;
}
bool FpsManager::Update()
{
using namespace std::chrono_literals;
m_fps_now = static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
if(std::chrono::duration<std::uint64_t>{m_fps_now - m_timer} >= 1s)
m_timer += m_fps_now;
m_fps_elapsed_time = m_fps_now - m_fps_before;
if(m_fps_elapsed_time >= m_ns)
{
m_fps_before += m_ns;
return true;
}
std::this_thread::sleep_for(std::chrono::duration<double, std::nano>(m_ns - 1));
return false;
}
}

94
runtime/Sources/Core/Graphics.cpp git.filemode.normal_file
View File

@@ -0,0 +1,94 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* graphics.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/04/02 15:13:55 by maldavid #+# #+# */
/* Updated: 2024/03/27 00:32:34 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <core/graphics.h>
namespace mlx
{
GraphicsSupport::GraphicsSupport(std::size_t w, std::size_t h, Texture* render_target, int id) :
_window(nullptr),
_renderer(std::make_unique<Renderer>()),
_width(w),
_height(h),
_id(id),
_has_window(false)
{
MLX_PROFILE_FUNCTION();
_renderer->setWindow(nullptr);
_renderer->init(render_target);
_pixel_put_pipeline.init(w, h, *_renderer);
_text_manager.init(*_renderer);
}
GraphicsSupport::GraphicsSupport(std::size_t w, std::size_t h, std::string title, int id) :
_window(std::make_shared<Window>(w, h, title)),
_renderer(std::make_unique<Renderer>()),
_width(w),
_height(h),
_id(id),
_has_window(true)
{
MLX_PROFILE_FUNCTION();
_renderer->setWindow(_window.get());
_renderer->init(nullptr);
_pixel_put_pipeline.init(w, h, *_renderer);
_text_manager.init(*_renderer);
}
void GraphicsSupport::render() noexcept
{
MLX_PROFILE_FUNCTION();
if(!_renderer->beginFrame())
return;
_proj = glm::ortho<float>(0, _width, 0, _height);
_renderer->getUniformBuffer()->setData(sizeof(_proj), &_proj);
static std::array<VkDescriptorSet, 2> sets = {
_renderer->getVertDescriptorSet().get(),
VK_NULL_HANDLE
};
for(auto& data : _drawlist)
data->render(sets, *_renderer);
_pixel_put_pipeline.render(sets, *_renderer);
_renderer->endFrame();
for(auto& data : _drawlist)
data->resetUpdate();
#ifdef GRAPHICS_MEMORY_DUMP
// dump memory to file every two seconds
using namespace std::chrono_literals;
static std::int64_t timer = static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
if(std::chrono::duration<std::uint64_t>{static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count()) - timer} >= 1s)
{
Render_Core::get().getAllocator().dumpMemoryToJson();
timer = static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
}
#endif
}
GraphicsSupport::~GraphicsSupport()
{
MLX_PROFILE_FUNCTION();
vkDeviceWaitIdle(Render_Core::get().getDevice().get());
_text_manager.destroy();
_pixel_put_pipeline.destroy();
_renderer->destroy();
if(_window)
_window->destroy();
}
}

68
runtime/Sources/Core/Logs.cpp git.filemode.normal_file
View File

@@ -0,0 +1,68 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Logs.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/03/27 17:20:55 by maldavid #+# #+# */
/* Updated: 2024/03/27 17:26:59 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <Core/Logs.h>
#include <Utils/Ansi.h>
#include <Core/EventBase.h>
#include <Core/EventBus.h>
namespace mlx
{
namespace Internal
{
struct FatalErrorEvent : public EventBase
{
std::uint32_t What() const override { return 167; }
};
}
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 DEBUG
if(type == LogType::Debug)
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 << "[Akel Debug] " << Ansi::def << code_infos << message << '\n'; break;
case LogType::Message: std::cout << Ansi::blue << "[Akel Message] " << Ansi::def << code_infos << message << '\n'; break;
case LogType::Warning: std::cout << Ansi::magenta << "[Akel Warning] " << Ansi::def << code_infos << message << '\n'; break;
case LogType::Error: std::cerr << Ansi::red << "[Akel Error] " << Ansi::def << code_infos << message << '\n'; break;
case LogType::FatalError: std::cerr << Ansi::red << "[Akel Fatal Error] " << Ansi::def << code_infos << message << '\n'; break;
default: break;
}
if(type == LogType::FatalError)
{
std::cout << Ansi::bg_red << "Fatal Error: emergency exit" << Ansi::bg_def << std::endl;
EventBus::Send("__internal_application", Internal::FatalErrorEvent{});
}
}
}

66
runtime/Sources/Core/Memory.cpp git.filemode.normal_file
View File

@@ -0,0 +1,66 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* memory.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/12/07 16:32:01 by kbz_8 #+# #+# */
/* Updated: 2024/03/25 19:01:02 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <core/memory.h>
#include <core/errors.h>
namespace mlx
{
void* MemManager::malloc(std::size_t size)
{
void* ptr = std::malloc(size);
if(ptr != nullptr)
_blocks.push_back(ptr);
return ptr;
}
void* MemManager::calloc(std::size_t n, std::size_t size)
{
void* ptr = std::calloc(n, size);
if(ptr != nullptr)
_blocks.push_back(ptr);
return ptr;
}
void* MemManager::realloc(void* ptr, std::size_t size)
{
void* ptr2 = std::realloc(ptr, size);
if(ptr2 != nullptr)
_blocks.push_back(ptr2);
auto it = std::find(_blocks.begin(), _blocks.end(), ptr);
if(it != _blocks.end())
_blocks.erase(it);
return ptr2;
}
void MemManager::free(void* ptr)
{
auto it = std::find(_blocks.begin(), _blocks.end(), ptr);
if(it == _blocks.end())
{
core::error::report(e_kind::error, "Memory Manager : trying to free a pointer not allocated by the memory manager");
return;
}
std::free(*it);
_blocks.erase(it);
}
MemManager::~MemManager()
{
std::for_each(_blocks.begin(), _blocks.end(), [](void* ptr)
{
std::free(ptr);
});
}
}

81
runtime/Sources/Core/Profiler.cpp git.filemode.normal_file
View File

@@ -0,0 +1,81 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* profiler.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/01/10 13:56:21 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:01:06 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <core/profiler.h>
#include <core/errors.h>
#include <iostream>
namespace mlx
{
void Profiler::beginRuntimeSession()
{
std::lock_guard lock(_mutex);
if(_runtime_session_began)
return;
_output_stream.open("./runtime_profile.mlx.json", std::ofstream::out | std::ofstream::trunc);
if(_output_stream.is_open())
writeHeader();
else
core::error::report(e_kind::error, "Profiler : cannot open runtime profile file");
_runtime_session_began = true;
}
void Profiler::appendProfileData(ProfileResult&& result)
{
std::lock_guard lock(_mutex);
auto it = _profile_data.find(result.name);
if(it != _profile_data.end())
{
result.elapsed_time = (result.elapsed_time + it->second.second.elapsed_time) / it->second.first;
_profile_data[result.name].first++;
_profile_data[result.name].second = result;
}
else
_profile_data[result.name] = std::make_pair(1, result);
}
void Profiler::writeProfile(const ProfileResult& result)
{
std::stringstream json;
json << std::setprecision(9) << std::fixed;
json << ",\n{\n";
json << "\t\"type\" : \"function\"," << '\n';
json << "\t\"name\" : \"" << result.name << "\"," << '\n';
json << "\t\"thread id\" : " << result.thread_id << "," << '\n';
json << "\t\"average duration\" : \"" << result.elapsed_time.count() << "ms\"\n";
json << "}";
_output_stream << json.str();
}
void Profiler::endRuntimeSession()
{
std::lock_guard lock(_mutex);
if(!_runtime_session_began)
return;
for(auto& [_, pair] : _profile_data)
writeProfile(pair.second);
writeFooter();
_output_stream.close();
_profile_data.clear();
_runtime_session_began = false;
}
Profiler::~Profiler()
{
if(!_runtime_session_began)
return;
endRuntimeSession();
}
}

25
runtime/Sources/Core/UUID.cpp git.filemode.normal_file
View File

@@ -0,0 +1,25 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* UUID.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/01/06 11:26:37 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:00:36 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <core/UUID.h>
namespace mlx
{
static std::random_device random_device;
static std::mt19937_64 engine(random_device());
static std::uniform_int_distribution<std::uint64_t> uniform_distribution;
UUID::UUID() : _uuid(uniform_distribution(engine)) {}
UUID::UUID(std::uint64_t uuid) : _uuid(uuid) {}
}

22
runtime/Sources/Drivers/GLFW/GLFWInputs.cpp git.filemode.normal_file
View File

@@ -0,0 +1,22 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* GLFWInputs.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/03/27 18:39:32 by maldavid #+# #+# */
/* Updated: 2024/03/27 18:42:18 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <PreCompiled.h>
#include <Drivers/GLFW/GLFWInputs.h>
namespace mlx
{
void GLFWInputs::Update() noexcept
{
glfwPollEvents();
}
}

39
runtime/Sources/Platform/Inputs.cpp git.filemode.normal_file
View File

@@ -0,0 +1,39 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* inputs.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/05 16:30:19 by maldavid #+# #+# */
/* Updated: 2024/03/27 15:50:07 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "inputs.h"
#include <mlx.h>
#include <core/profiler.h>
namespace mlx
{
void Input::update()
{
MLX_PROFILE_FUNCTION();
_xRel = 0;
_yRel = 0;
glfwPollEvents();
static int i = 0;
i++;
if(i >= 500)
{
auto& hooks = _events_hooks[0];
auto& win_hook = hooks[MLX_WINDOW_EVENT];
if(win_hook.hook)
win_hook.hook(0, win_hook.param);
}
}
}

40
runtime/Sources/Platform/Window.cpp git.filemode.normal_file
View File

@@ -0,0 +1,40 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* window.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/04 17:36:44 by maldavid #+# #+# */
/* Updated: 2024/03/26 23:03:59 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <platform/window.h>
#include <core/errors.h>
#include <utils/icon_mlx.h>
namespace mlx
{
Window::Window(std::size_t w, std::size_t h, const std::string& title) : _width(w), _height(h)
{
static std::uint64_t ids = 0;
if(title.find("vvaas") != std::string::npos)
core::error::report(e_kind::message, "vvaas est mauvais");
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
_win = glfwCreateWindow(_width, _height, title.c_str(), NULL, NULL);;
_id = ids++;
}
void Window::destroy() noexcept
{
if(_win != nullptr)
{
glfwDestroyWindow(_win);
_win = nullptr;
}
}
}

167
runtime/Sources/Renderer/Buffers/Buffer.cpp git.filemode.normal_file
View File

@@ -0,0 +1,167 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_buffer.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/08 18:55:57 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:01:18 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "vk_buffer.h"
#include <renderer/command/vk_cmd_pool.h>
#include <renderer/command/vk_cmd_buffer.h>
#include <renderer/core/render_core.h>
#include <core/profiler.h>
namespace mlx
{
void Buffer::create(Buffer::kind type, VkDeviceSize size, VkBufferUsageFlags usage, const char* name, const void* data)
{
MLX_PROFILE_FUNCTION();
_usage = usage;
if(type == Buffer::kind::constant || type == Buffer::kind::dynamic_device_local)
{
if(data == nullptr && type == Buffer::kind::constant)
{
core::error::report(e_kind::warning, "Vulkan : trying to create constant buffer without data (constant buffers cannot be modified after creation)");
return;
}
_usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
}
VmaAllocationCreateInfo alloc_info{};
alloc_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
alloc_info.usage = VMA_MEMORY_USAGE_AUTO;
createBuffer(_usage, alloc_info, size, name);
if(data != nullptr)
{
void* mapped = nullptr;
mapMem(&mapped);
std::memcpy(mapped, data, size);
unmapMem();
if(type == Buffer::kind::constant || type == Buffer::kind::dynamic_device_local)
pushToGPU();
}
}
void Buffer::destroy() noexcept
{
MLX_PROFILE_FUNCTION();
if(_is_mapped)
unmapMem();
if(_buffer != VK_NULL_HANDLE)
Render_Core::get().getAllocator().destroyBuffer(_allocation, _buffer);
_buffer = VK_NULL_HANDLE;
}
void Buffer::createBuffer(VkBufferUsageFlags usage, VmaAllocationCreateInfo info, VkDeviceSize size, [[maybe_unused]] const char* name)
{
MLX_PROFILE_FUNCTION();
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
bufferInfo.usage = usage;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
#ifdef DEBUG
_name = name;
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("_buffer");
_allocation = Render_Core::get().getAllocator().createBuffer(&bufferInfo, &info, _buffer, alloc_name.c_str());
#else
_allocation = Render_Core::get().getAllocator().createBuffer(&bufferInfo, &info, _buffer, nullptr);
#endif
_size = size;
}
bool Buffer::copyFromBuffer(const Buffer& buffer) noexcept
{
MLX_PROFILE_FUNCTION();
if(!(_usage & VK_BUFFER_USAGE_TRANSFER_DST_BIT))
{
core::error::report(e_kind::error, "Vulkan : buffer cannot be the destination of a copy because it does not have the correct usage flag");
return false;
}
if(!(buffer._usage & VK_BUFFER_USAGE_TRANSFER_SRC_BIT))
{
core::error::report(e_kind::error, "Vulkan : buffer cannot be the source of a copy because it does not have the correct usage flag");
return false;
}
CmdBuffer& cmd = Render_Core::get().getSingleTimeCmdBuffer();
cmd.beginRecord();
cmd.copyBuffer(*this, const_cast<Buffer&>(buffer));
cmd.endRecord();
cmd.submitIdle();
return true;
}
void Buffer::pushToGPU() noexcept
{
MLX_PROFILE_FUNCTION();
VmaAllocationCreateInfo alloc_info{};
alloc_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
Buffer newBuffer;
newBuffer._usage = (_usage & 0xFFFFFFFC) | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
#ifdef DEBUG
std::string new_name = _name + "_GPU";
newBuffer.createBuffer(newBuffer._usage, alloc_info, _size, new_name.c_str());
#else
newBuffer.createBuffer(newBuffer._usage, alloc_info, _size, nullptr);
#endif
if(newBuffer.copyFromBuffer(*this)) // if the copy succeded we swap the buffers, otherwise the new one is deleted
this->swap(newBuffer);
newBuffer.destroy();
}
void Buffer::swap(Buffer& buffer) noexcept
{
VkBuffer temp_b = _buffer;
_buffer = buffer._buffer;
buffer._buffer = temp_b;
VmaAllocation temp_a = buffer._allocation;
buffer._allocation = _allocation;
_allocation = temp_a;
VkDeviceSize temp_size = buffer._size;
buffer._size = _size;
_size = temp_size;
VkDeviceSize temp_offset = buffer._offset;
buffer._offset = _offset;
_offset = temp_offset;
VkBufferUsageFlags temp_u = _usage;
_usage = buffer._usage;
buffer._usage = temp_u;
#ifdef DEBUG
std::string temp_n = _name;
_name = buffer._name;
buffer._name = temp_n;
#endif
}
void Buffer::flush(VkDeviceSize size, VkDeviceSize offset)
{
Render_Core::get().getAllocator().flush(_allocation, size, offset);
}
}

View File

@@ -0,0 +1,79 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_ubo.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/06 18:45:52 by maldavid #+# #+# */
/* Updated: 2024/03/25 17:48:07 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "vk_ubo.h"
#include <renderer/renderer.h>
#include <core/profiler.h>
namespace mlx
{
void UBO::create(Renderer* renderer, std::uint32_t size, [[maybe_unused]] const char* name)
{
MLX_PROFILE_FUNCTION();
_renderer = renderer;
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
#ifdef DEBUG
std::string name_frame = name;
name_frame.append(std::to_string(i));
_buffers[i].create(Buffer::kind::uniform, size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, name_frame.c_str());
#else
_buffers[i].create(Buffer::kind::uniform, size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, nullptr);
#endif
_buffers[i].mapMem(&_maps[i]);
if(_maps[i] == nullptr)
core::error::report(e_kind::fatal_error, "Vulkan : unable to map a uniform buffer");
}
}
void UBO::setData(std::uint32_t size, const void* data)
{
MLX_PROFILE_FUNCTION();
std::memcpy(_maps[_renderer->getActiveImageIndex()], data, static_cast<std::size_t>(size));
}
void UBO::setDynamicData(std::uint32_t size, const void* data)
{
MLX_PROFILE_FUNCTION();
std::memcpy(_maps[_renderer->getActiveImageIndex()], data, static_cast<std::size_t>(size));
_buffers[_renderer->getActiveImageIndex()].flush();
}
unsigned int UBO::getSize() noexcept
{
return _buffers[_renderer->getActiveImageIndex()].getSize();
}
unsigned int UBO::getOffset() noexcept
{
return _buffers[_renderer->getActiveImageIndex()].getOffset();
}
VkBuffer& UBO::operator()() noexcept
{
return _buffers[_renderer->getActiveImageIndex()].get();
}
VkBuffer& UBO::get() noexcept
{
return _buffers[_renderer->getActiveImageIndex()].get();
}
void UBO::destroy() noexcept
{
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
_buffers[i].destroy();
}
}

View File

@@ -0,0 +1,56 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_vbo.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/06 18:28:08 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:01:25 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "vk_vbo.h"
namespace mlx
{
void VBO::setData(std::uint32_t size, const void* data)
{
if(size > getSize())
{
core::error::report(e_kind::error, "Vulkan : trying to store to much data in a vertex buffer (%d bytes in %d bytes)", size, getSize());
return;
}
if(data == nullptr)
core::error::report(e_kind::warning, "Vulkan : mapping null data in a vertex buffer");
void* temp = nullptr;
mapMem(&temp);
std::memcpy(temp, data, static_cast<std::size_t>(size));
unmapMem();
}
void D_VBO::setData(std::uint32_t size, const void* data)
{
if(size > getSize())
{
core::error::report(e_kind::error, "Vulkan : trying to store to much data in a vertex buffer (%d bytes in %d bytes)", size, getSize());
return;
}
if(data == nullptr)
core::error::report(e_kind::warning, "Vulkan : mapping null data in a vertex buffer");
Buffer tmp_buf;
#ifdef DEBUG
tmp_buf.create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, "tmp_buffer", data);
#else
tmp_buf.create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, nullptr, data);
#endif
copyFromBuffer(tmp_buf);
tmp_buf.destroy();
}
}

View File

@@ -0,0 +1,370 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_cmd_buffer.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/06 18:26:06 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:01:37 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "vk_cmd_buffer.h"
#include <renderer/core/cmd_resource.h>
#include <renderer/core/render_core.h>
#include <renderer/command/cmd_manager.h>
#include <renderer/core/vk_semaphore.h>
#include <renderer/buffers/vk_buffer.h>
#include <renderer/images/vk_image.h>
#include <core/profiler.h>
namespace mlx
{
bool vector_push_back_if_not_found(std::vector<CmdResource*>& vector, CmdResource* res)
{
auto it = std::find_if(vector.begin(), vector.end(), [=](const CmdResource* vres)
{
return vres->getUUID() == res->getUUID();
});
if(it == vector.end())
{
vector.push_back(res);
return true;
}
return false;
}
void CmdBuffer::init(kind type, CmdManager* manager)
{
init(type, &manager->getCmdPool());
}
void CmdBuffer::init(kind type, CmdPool* pool)
{
MLX_PROFILE_FUNCTION();
_type = type;
_pool = pool;
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = pool->get();
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1;
VkResult res = vkAllocateCommandBuffers(Render_Core::get().getDevice().get(), &allocInfo, &_cmd_buffer);
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to allocate command buffer, %s", RCore::verbaliseResultVk(res));
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : created new command buffer");
#endif
_fence.init();
_state = state::idle;
}
void CmdBuffer::beginRecord(VkCommandBufferUsageFlags usage)
{
MLX_PROFILE_FUNCTION();
if(!isInit())
core::error::report(e_kind::fatal_error, "Vulkan : begenning record on un uninit command buffer");
if(_state == state::recording)
return;
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = usage;
if(vkBeginCommandBuffer(_cmd_buffer, &beginInfo) != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to begin recording command buffer");
_state = state::recording;
}
void CmdBuffer::bindVertexBuffer(Buffer& buffer) noexcept
{
MLX_PROFILE_FUNCTION();
if(!isRecording())
{
core::error::report(e_kind::warning, "Vulkan : trying to bind a vertex buffer to a non recording command buffer");
return;
}
VkDeviceSize offset[] = { buffer.getOffset() };
vkCmdBindVertexBuffers(_cmd_buffer, 0, 1, &buffer.get(), offset);
buffer.recordedInCmdBuffer();
vector_push_back_if_not_found(_cmd_resources, &buffer);
}
void CmdBuffer::bindIndexBuffer(Buffer& buffer) noexcept
{
MLX_PROFILE_FUNCTION();
if(!isRecording())
{
core::error::report(e_kind::warning, "Vulkan : trying to bind a index buffer to a non recording command buffer");
return;
}
vkCmdBindIndexBuffer(_cmd_buffer, buffer.get(), buffer.getOffset(), VK_INDEX_TYPE_UINT16);
buffer.recordedInCmdBuffer();
vector_push_back_if_not_found(_cmd_resources, &buffer);
}
void CmdBuffer::copyBuffer(Buffer& dst, Buffer& src) noexcept
{
MLX_PROFILE_FUNCTION();
if(!isRecording())
{
core::error::report(e_kind::warning, "Vulkan : trying to do a buffer to buffer copy in a non recording command buffer");
return;
}
preTransferBarrier();
VkBufferCopy copyRegion{};
copyRegion.size = src.getSize();
vkCmdCopyBuffer(_cmd_buffer, src.get(), dst.get(), 1, &copyRegion);
postTransferBarrier();
dst.recordedInCmdBuffer();
src.recordedInCmdBuffer();
vector_push_back_if_not_found(_cmd_resources, &dst);
vector_push_back_if_not_found(_cmd_resources, &src);
}
void CmdBuffer::copyBufferToImage(Buffer& buffer, Image& image) noexcept
{
MLX_PROFILE_FUNCTION();
if(!isRecording())
{
core::error::report(e_kind::warning, "Vulkan : trying to do a buffer to image copy in a non recording command buffer");
return;
}
preTransferBarrier();
VkBufferImageCopy region{};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = { 0, 0, 0 };
region.imageExtent = { image.getWidth(), image.getHeight(), 1 };
vkCmdCopyBufferToImage(_cmd_buffer, buffer.get(), image.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
postTransferBarrier();
image.recordedInCmdBuffer();
buffer.recordedInCmdBuffer();
vector_push_back_if_not_found(_cmd_resources, &image);
vector_push_back_if_not_found(_cmd_resources, &buffer);
}
void CmdBuffer::copyImagetoBuffer(Image& image, Buffer& buffer) noexcept
{
MLX_PROFILE_FUNCTION();
if(!isRecording())
{
core::error::report(e_kind::warning, "Vulkan : trying to do an image to buffer copy in a non recording command buffer");
return;
}
preTransferBarrier();
VkBufferImageCopy region{};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = { 0, 0, 0 };
region.imageExtent = { image.getWidth(), image.getHeight(), 1 };
vkCmdCopyImageToBuffer(_cmd_buffer, image.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer.get(), 1, &region);
postTransferBarrier();
image.recordedInCmdBuffer();
buffer.recordedInCmdBuffer();
vector_push_back_if_not_found(_cmd_resources, &buffer);
vector_push_back_if_not_found(_cmd_resources, &image);
}
void CmdBuffer::transitionImageLayout(Image& image, VkImageLayout new_layout) noexcept
{
MLX_PROFILE_FUNCTION();
if(!isRecording())
{
core::error::report(e_kind::warning, "Vulkan : trying to do an image layout transition in a non recording command buffer");
return;
}
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = image.getLayout();
barrier.newLayout = new_layout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image.get();
barrier.subresourceRange.aspectMask = isDepthFormat(image.getFormat()) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.srcAccessMask = layoutToAccessMask(image.getLayout(), false);
barrier.dstAccessMask = layoutToAccessMask(new_layout, true);
if(isStencilFormat(image.getFormat()))
barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
VkPipelineStageFlags sourceStage = 0;
if(barrier.oldLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
sourceStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
else if(barrier.srcAccessMask != 0)
sourceStage = RCore::accessFlagsToPipelineStage(barrier.srcAccessMask, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
else
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkPipelineStageFlags destinationStage = 0;
if(barrier.newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
destinationStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
else if(barrier.dstAccessMask != 0)
destinationStage = RCore::accessFlagsToPipelineStage(barrier.dstAccessMask, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
else
destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
vkCmdPipelineBarrier(_cmd_buffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
image.recordedInCmdBuffer();
vector_push_back_if_not_found(_cmd_resources, &image);
}
void CmdBuffer::endRecord()
{
MLX_PROFILE_FUNCTION();
if(!isInit())
core::error::report(e_kind::fatal_error, "Vulkan : ending record on un uninit command buffer");
if(_state != state::recording)
return;
if(vkEndCommandBuffer(_cmd_buffer) != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to end recording command buffer");
_state = state::idle;
}
void CmdBuffer::submitIdle(bool shouldWaitForExecution) noexcept
{
MLX_PROFILE_FUNCTION();
if(_type != kind::single_time)
{
core::error::report(e_kind::error, "Vulkan : try to perform an idle submit on a command buffer that is not single-time, this is not allowed");
return;
}
_fence.reset();
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &_cmd_buffer;
VkResult res = vkQueueSubmit(Render_Core::get().getQueue().getGraphic(), 1, &submitInfo, _fence.get());
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan error : failed to submit a single time command buffer, %s", RCore::verbaliseResultVk(res));
_state = state::submitted;
if(shouldWaitForExecution)
waitForExecution();
}
void CmdBuffer::submit(Semaphore* semaphores) noexcept
{
MLX_PROFILE_FUNCTION();
std::array<VkSemaphore, 1> signalSemaphores;
std::array<VkSemaphore, 1> waitSemaphores;
if(semaphores != nullptr)
{
signalSemaphores[0] = semaphores->getRenderImageSemaphore();
waitSemaphores[0] = semaphores->getImageSemaphore();
}
else
{
signalSemaphores[0] = VK_NULL_HANDLE;
waitSemaphores[0] = VK_NULL_HANDLE;
}
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
_fence.reset();
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.waitSemaphoreCount = (semaphores == nullptr ? 0 : waitSemaphores.size());
submitInfo.pWaitSemaphores = waitSemaphores.data();
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &_cmd_buffer;
submitInfo.signalSemaphoreCount = (semaphores == nullptr ? 0 : signalSemaphores.size());
submitInfo.pSignalSemaphores = signalSemaphores.data();
VkResult res = vkQueueSubmit(Render_Core::get().getQueue().getGraphic(), 1, &submitInfo, _fence.get());
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan error : failed to submit draw command buffer, %s", RCore::verbaliseResultVk(res));
_state = state::submitted;
}
void CmdBuffer::updateSubmitState() noexcept
{
MLX_PROFILE_FUNCTION();
if(!_fence.isReady())
return;
for(CmdResource* res : _cmd_resources)
res->removedFromCmdBuffer();
_cmd_resources.clear();
_state = state::ready;
}
void CmdBuffer::preTransferBarrier() noexcept
{
MLX_PROFILE_FUNCTION();
VkMemoryBarrier memoryBarrier{};
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
memoryBarrier.pNext = nullptr;
memoryBarrier.srcAccessMask = 0U;
memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
vkCmdPipelineBarrier(_cmd_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, &memoryBarrier, 0, nullptr, 0, nullptr);
}
void CmdBuffer::postTransferBarrier() noexcept
{
MLX_PROFILE_FUNCTION();
VkMemoryBarrier memoryBarrier{};
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
memoryBarrier.pNext = nullptr;
memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_UNIFORM_READ_BIT;
vkCmdPipelineBarrier(_cmd_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 0, 1, &memoryBarrier, 0, nullptr, 0, nullptr);
}
void CmdBuffer::destroy() noexcept
{
MLX_PROFILE_FUNCTION();
_fence.destroy();
_cmd_buffer = VK_NULL_HANDLE;
_state = state::uninit;
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : destroyed command buffer");
#endif
}
}

View File

@@ -0,0 +1,42 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmd_manager.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/04/02 17:50:52 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:01:30 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <renderer/command/cmd_manager.h>
namespace mlx
{
void CmdManager::init() noexcept
{
_cmd_pool.init();
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
_cmd_buffers[i].init(CmdBuffer::kind::long_time, this);
}
void CmdManager::beginRecord(int active_image_index)
{
_cmd_buffers[active_image_index].beginRecord();
}
void CmdManager::endRecord(int active_image_index)
{
_cmd_buffers[active_image_index].endRecord();
}
void CmdManager::destroy() noexcept
{
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
_cmd_buffers[i].destroy();
_cmd_pool.destroy();
}
}

View File

@@ -0,0 +1,37 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_cmd_pool.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/06 18:24:33 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:01:41 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "vk_cmd_pool.h"
#include <renderer/core/render_core.h>
namespace mlx
{
void CmdPool::init()
{
VkCommandPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
poolInfo.queueFamilyIndex = Render_Core::get().getQueue().getFamilies().graphics_family.value();
VkResult res = vkCreateCommandPool(Render_Core::get().getDevice().get(), &poolInfo, nullptr, &_cmd_pool);
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create command pool, %s", RCore::verbaliseResultVk(res));
}
void CmdPool::destroy() noexcept
{
vkDestroyCommandPool(Render_Core::get().getDevice().get(), _cmd_pool, nullptr);
_cmd_pool = VK_NULL_HANDLE;
}
}

View File

@@ -0,0 +1,64 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* single_time_cmd_manager.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/12/15 19:57:49 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:01:33 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <renderer/command/single_time_cmd_manager.h>
#include <renderer/core/render_core.h>
namespace mlx
{
void SingleTimeCmdManager::init() noexcept
{
_pool.init();
for(int i = 0; i < BASE_POOL_SIZE; i++)
{
_buffers.emplace_back();
_buffers.back().init(CmdBuffer::kind::single_time, &_pool);
}
}
CmdBuffer& SingleTimeCmdManager::getCmdBuffer() noexcept
{
for(CmdBuffer& buf : _buffers)
{
if(buf.isReadyToBeUsed())
{
buf.reset();
return buf;
}
}
_buffers.emplace_back().init(CmdBuffer::kind::single_time, &_pool);
return _buffers.back();
}
void SingleTimeCmdManager::updateSingleTimesCmdBuffersSubmitState() noexcept
{
for(CmdBuffer& cmd : _buffers)
cmd.updateSubmitState();
}
void SingleTimeCmdManager::waitForAllExecutions() noexcept
{
for(CmdBuffer& cmd : _buffers)
cmd.waitForExecution();
}
void SingleTimeCmdManager::destroy() noexcept
{
std::for_each(_buffers.begin(), _buffers.end(), [](CmdBuffer& buf)
{
buf.destroy();
});
_pool.destroy();
}
}

149
runtime/Sources/Renderer/Core/Device.cpp git.filemode.normal_file
View File

@@ -0,0 +1,149 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_device.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/08 19:14:29 by maldavid #+# #+# */
/* Updated: 2024/03/25 22:31:54 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "render_core.h"
namespace mlx
{
const std::vector<const char*> deviceExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
void Device::init()
{
pickPhysicalDevice();
Queues::QueueFamilyIndices indices = Render_Core::get().getQueue().getFamilies();
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<std::uint32_t> uniqueQueueFamilies = { indices.graphics_family.value(), indices.present_family.value() };
float queuePriority = 1.0f;
for(std::uint32_t queueFamily : uniqueQueueFamilies)
{
VkDeviceQueueCreateInfo queueCreateInfo{};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = queueFamily;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueCreateInfo);
}
VkPhysicalDeviceFeatures deviceFeatures{};
VkDeviceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.queueCreateInfoCount = static_cast<std::uint32_t>(queueCreateInfos.size());
createInfo.pQueueCreateInfos = queueCreateInfos.data();
createInfo.pEnabledFeatures = &deviceFeatures;
createInfo.enabledExtensionCount = static_cast<std::uint32_t>(deviceExtensions.size());
createInfo.ppEnabledExtensionNames = deviceExtensions.data();
createInfo.enabledLayerCount = 0;
VkResult res;
if((res = vkCreateDevice(_physical_device, &createInfo, nullptr, &_device)) != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create logcal device, %s", RCore::verbaliseResultVk(res));
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : created new logical device");
#endif
}
void Device::pickPhysicalDevice()
{
std::uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(Render_Core::get().getInstance().get(), &deviceCount, nullptr);
if(deviceCount == 0)
core::error::report(e_kind::fatal_error, "Vulkan : failed to find GPUs with Vulkan support");
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(Render_Core::get().getInstance().get(), &deviceCount, devices.data());
std::multimap<int, VkPhysicalDevice> devices_score;
for(const auto& device : devices)
{
int score = deviceScore(device);
devices_score.insert(std::make_pair(score, device));
}
if(devices_score.rbegin()->first > 0)
_physical_device = devices_score.rbegin()->second;
else
core::error::report(e_kind::fatal_error, "Vulkan : failed to find a suitable GPU");
#ifdef DEBUG
VkPhysicalDeviceProperties props;
vkGetPhysicalDeviceProperties(_physical_device, &props);
core::error::report(e_kind::message, "Vulkan : picked a physical device, %s", props.deviceName);
#endif
Render_Core::get().getQueue().findQueueFamilies(_physical_device); // update queue indicies to current physical device
}
int Device::deviceScore(VkPhysicalDevice device)
{
Queues::QueueFamilyIndices indices = Render_Core::get().getQueue().findQueueFamilies(device);
bool extensionsSupported = checkDeviceExtensionSupport(device);
VkPhysicalDeviceProperties props;
vkGetPhysicalDeviceProperties(device, &props);
if(!indices.isComplete() || !extensionsSupported)
return -1;
VkPhysicalDeviceFeatures features;
vkGetPhysicalDeviceFeatures(device, &features);
int score = 0;
#ifndef FORCE_INTEGRATED_GPU
if(props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
score += 1000;
#else
if(props.deviceType != VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)
return -1;
#endif
if(!features.geometryShader)
return -1;
score += props.limits.maxImageDimension2D;
score += props.limits.maxBoundDescriptorSets;
return score;
}
bool Device::checkDeviceExtensionSupport(VkPhysicalDevice device)
{
std::uint32_t extensionCount;
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
for(const auto& extension : availableExtensions)
requiredExtensions.erase(extension.extensionName);
return requiredExtensions.empty();
}
void Device::destroy() noexcept
{
vkDestroyDevice(_device, nullptr);
_device = VK_NULL_HANDLE;
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : destroyed a logical device");
#endif
}
}

58
runtime/Sources/Renderer/Core/Fence.cpp git.filemode.normal_file
View File

@@ -0,0 +1,58 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_fence.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/04/02 17:53:06 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:02:14 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <renderer/core/vk_fence.h>
#include <renderer/core/render_core.h>
namespace mlx
{
void Fence::init()
{
VkFenceCreateInfo fenceInfo{};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
VkResult res;
if((res = vkCreateFence(Render_Core::get().getDevice().get(), &fenceInfo, nullptr, &_fence)) != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create a synchronization object (fence), %s", RCore::verbaliseResultVk(res));
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : created new fence");
#endif
}
void Fence::wait() noexcept
{
vkWaitForFences(Render_Core::get().getDevice().get(), 1, &_fence, VK_TRUE, UINT64_MAX);
}
void Fence::reset() noexcept
{
vkResetFences(Render_Core::get().getDevice().get(), 1, &_fence);
}
bool Fence::isReady() const noexcept
{
return vkGetFenceStatus(Render_Core::get().getDevice().get(), _fence) == VK_SUCCESS;
}
void Fence::destroy() noexcept
{
if(_fence != VK_NULL_HANDLE)
vkDestroyFence(Render_Core::get().getDevice().get(), _fence, nullptr);
_fence = VK_NULL_HANDLE;
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : destroyed fence");
#endif
}
}

86
runtime/Sources/Renderer/Core/Instance.cpp git.filemode.normal_file
View File

@@ -0,0 +1,86 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_instance.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/08 19:04:21 by maldavid #+# #+# */
/* Updated: 2024/03/25 23:10:37 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "vk_instance.h"
#include "render_core.h"
#include <platform/window.h>
namespace mlx
{
void Instance::init()
{
VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pEngineName = "MacroLibX";
appInfo.engineVersion = VK_MAKE_VERSION(1, 3, 1);
appInfo.apiVersion = VK_API_VERSION_1_2;
auto extensions = getRequiredExtensions();
VkInstanceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
createInfo.enabledExtensionCount = static_cast<std::uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
createInfo.enabledLayerCount = 0; // will be replaced if validation layers are enabled
createInfo.pNext = nullptr;
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
if constexpr(enableValidationLayers)
{
if(Render_Core::get().getLayers().checkValidationLayerSupport())
{
createInfo.enabledLayerCount = static_cast<std::uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
Render_Core::get().getLayers().populateDebugMessengerCreateInfo(debugCreateInfo);
createInfo.pNext = static_cast<VkDebugUtilsMessengerCreateInfoEXT*>(&debugCreateInfo);
}
}
VkResult res;
if((res = vkCreateInstance(&createInfo, nullptr, &_instance)) != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create Vulkan instance, %s", RCore::verbaliseResultVk(res));
volkLoadInstance(_instance);
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : created new instance");
#endif
}
std::vector<const char*> Instance::getRequiredExtensions()
{
std::uint32_t glfw_extension_count = 0;
const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count);
std::vector<const char*> extensions(glfw_extensions, glfw_extensions + glfw_extension_count);
extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
if constexpr(enableValidationLayers)
{
extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
return extensions;
}
void Instance::destroy() noexcept
{
vkDestroyInstance(_instance, nullptr);
_instance = VK_NULL_HANDLE;
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : destroyed an instance");
#endif
}
}

205
runtime/Sources/Renderer/Core/Memory.cpp git.filemode.normal_file
View File

@@ -0,0 +1,205 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* memory.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kbz_8 <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/10/20 22:02:37 by kbz_8 #+# #+# */
/* Updated: 2024/03/25 19:27:44 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <mlx_profile.h>
#include <cstdio>
#define VK_NO_PROTOTYPES
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0
#define VMA_VULKAN_VERSION 1002000
#define VMA_ASSERT(expr) ((void)0)
#define VMA_IMPLEMENTATION
#ifdef MLX_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Weverything"
#include <vma.h>
#pragma clang diagnostic pop
#elif defined(MLX_COMPILER_GCC)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wparentheses"
#include <vma.h>
#pragma GCC diagnostic pop
#else
#include <vma.h>
#endif
#include <pre_compiled.h>
#include <core/errors.h>
#include <core/profiler.h>
#include <renderer/core/render_core.h>
namespace mlx
{
void GPUallocator::init() noexcept
{
VmaVulkanFunctions vma_vulkan_func{};
vma_vulkan_func.vkAllocateMemory = vkAllocateMemory;
vma_vulkan_func.vkBindBufferMemory = vkBindBufferMemory;
vma_vulkan_func.vkBindImageMemory = vkBindImageMemory;
vma_vulkan_func.vkCreateBuffer = vkCreateBuffer;
vma_vulkan_func.vkCreateImage = vkCreateImage;
vma_vulkan_func.vkDestroyBuffer = vkDestroyBuffer;
vma_vulkan_func.vkDestroyImage = vkDestroyImage;
vma_vulkan_func.vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges;
vma_vulkan_func.vkFreeMemory = vkFreeMemory;
vma_vulkan_func.vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements;
vma_vulkan_func.vkGetImageMemoryRequirements = vkGetImageMemoryRequirements;
vma_vulkan_func.vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties;
vma_vulkan_func.vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties;
vma_vulkan_func.vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges;
vma_vulkan_func.vkMapMemory = vkMapMemory;
vma_vulkan_func.vkUnmapMemory = vkUnmapMemory;
vma_vulkan_func.vkCmdCopyBuffer = vkCmdCopyBuffer;
vma_vulkan_func.vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2;
vma_vulkan_func.vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2;
vma_vulkan_func.vkBindBufferMemory2KHR = vkBindBufferMemory2;
vma_vulkan_func.vkBindImageMemory2KHR = vkBindImageMemory2;
vma_vulkan_func.vkGetPhysicalDeviceMemoryProperties2KHR = vkGetPhysicalDeviceMemoryProperties2;
VmaAllocatorCreateInfo allocatorCreateInfo{};
allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2;
allocatorCreateInfo.physicalDevice = Render_Core::get().getDevice().getPhysicalDevice();
allocatorCreateInfo.device = Render_Core::get().getDevice().get();
allocatorCreateInfo.instance = Render_Core::get().getInstance().get();
allocatorCreateInfo.pVulkanFunctions = &vma_vulkan_func;
VkResult res = vmaCreateAllocator(&allocatorCreateInfo, &_allocator);
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Graphics allocator : failed to create graphics memory allocator, %s", RCore::verbaliseResultVk(res));
#ifdef DEBUG
core::error::report(e_kind::message, "Graphics allocator : created new allocator");
#endif
}
VmaAllocation GPUallocator::createBuffer(const VkBufferCreateInfo* binfo, const VmaAllocationCreateInfo* vinfo, VkBuffer& buffer, const char* name) noexcept
{
MLX_PROFILE_FUNCTION();
VmaAllocation allocation;
VkResult res = vmaCreateBuffer(_allocator, binfo, vinfo, &buffer, &allocation, nullptr);
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Graphics allocator : failed to allocate a buffer, %s", RCore::verbaliseResultVk(res));
if(name != nullptr)
{
Render_Core::get().getLayers().setDebugUtilsObjectNameEXT(VK_OBJECT_TYPE_BUFFER, (std::uint64_t)buffer, name);
vmaSetAllocationName(_allocator, allocation, name);
}
#ifdef DEBUG
core::error::report(e_kind::message, "Graphics Allocator : created new buffer '%s'", name);
#endif
_active_buffers_allocations++;
return allocation;
}
void GPUallocator::destroyBuffer(VmaAllocation allocation, VkBuffer buffer) noexcept
{
MLX_PROFILE_FUNCTION();
vkDeviceWaitIdle(Render_Core::get().getDevice().get());
vmaDestroyBuffer(_allocator, buffer, allocation);
#ifdef DEBUG
core::error::report(e_kind::message, "Graphics Allocator : destroyed buffer");
#endif
_active_buffers_allocations--;
}
VmaAllocation GPUallocator::createImage(const VkImageCreateInfo* iminfo, const VmaAllocationCreateInfo* vinfo, VkImage& image, const char* name) noexcept
{
MLX_PROFILE_FUNCTION();
VmaAllocation allocation;
VkResult res = vmaCreateImage(_allocator, iminfo, vinfo, &image, &allocation, nullptr);
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Graphics allocator : failed to allocate an image, %s", RCore::verbaliseResultVk(res));
if(name != nullptr)
{
Render_Core::get().getLayers().setDebugUtilsObjectNameEXT(VK_OBJECT_TYPE_IMAGE, (std::uint64_t)image, name);
vmaSetAllocationName(_allocator, allocation, name);
}
#ifdef DEBUG
core::error::report(e_kind::message, "Graphics Allocator : created new image '%s'", name);
#endif
_active_images_allocations++;
return allocation;
}
void GPUallocator::destroyImage(VmaAllocation allocation, VkImage image) noexcept
{
MLX_PROFILE_FUNCTION();
vkDeviceWaitIdle(Render_Core::get().getDevice().get());
vmaDestroyImage(_allocator, image, allocation);
#ifdef DEBUG
core::error::report(e_kind::message, "Graphics Allocator : destroyed image");
#endif
_active_images_allocations--;
}
void GPUallocator::mapMemory(VmaAllocation allocation, void** data) noexcept
{
MLX_PROFILE_FUNCTION();
VkResult res = vmaMapMemory(_allocator, allocation, data);
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Graphics allocator : unable to map GPU memory to CPU memory, %s", RCore::verbaliseResultVk(res));
}
void GPUallocator::unmapMemory(VmaAllocation allocation) noexcept
{
MLX_PROFILE_FUNCTION();
vmaUnmapMemory(_allocator, allocation);
}
void GPUallocator::dumpMemoryToJson()
{
static std::uint32_t id = 0;
std::string name("memory_dump");
name.append(std::to_string(id) + ".json");
std::ofstream file(name);
if(!file.is_open())
{
core::error::report(e_kind::error, "Graphics allocator : unable to dump memory to a json file");
return;
}
char* str = nullptr;
vmaBuildStatsString(_allocator, &str, true);
file << str;
vmaFreeStatsString(_allocator, str);
file.close();
id++;
}
void GPUallocator::flush(VmaAllocation allocation, VkDeviceSize size, VkDeviceSize offset) noexcept
{
MLX_PROFILE_FUNCTION();
vmaFlushAllocation(_allocator, allocation, offset, size);
}
void GPUallocator::destroy() noexcept
{
if(_active_images_allocations != 0)
core::error::report(e_kind::error, "Graphics allocator : some user-dependant allocations were not freed before destroying the display (%d active allocations). You may have not destroyed all the MLX resources you've created", _active_images_allocations);
else if(_active_buffers_allocations != 0)
core::error::report(e_kind::error, "Graphics allocator : some MLX-dependant allocations were not freed before destroying the display (%d active allocations). This is an error in the MLX, please report this should not happen", _active_buffers_allocations);
if(_active_images_allocations < 0 || _active_buffers_allocations < 0)
core::error::report(e_kind::warning, "Graphics allocator : the impossible happened, the MLX has freed more allocations than it has made (wtf)");
vmaDestroyAllocator(_allocator);
_active_buffers_allocations = 0;
_active_images_allocations = 0;
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : destroyed a graphics allocator");
#endif
}
}

55
runtime/Sources/Renderer/Core/Queues.cpp git.filemode.normal_file
View File

@@ -0,0 +1,55 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_queues.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/08 19:02:42 by maldavid #+# #+# */
/* Updated: 2024/03/25 22:29:19 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "render_core.h"
namespace mlx
{
Queues::QueueFamilyIndices Queues::findQueueFamilies(VkPhysicalDevice device)
{
std::uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
_families = Queues::QueueFamilyIndices{};
int i = 0;
for(const auto& queueFamily : queueFamilies)
{
if(queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
_families->graphics_family = i;
if(glfwGetPhysicalDevicePresentationSupport(Render_Core::get().getInstance().get(), device, i))
_families->present_family = i;
if(_families->isComplete())
return *_families;
i++;
}
return *_families;
}
void Queues::init()
{
if(!_families.has_value())
findQueueFamilies(Render_Core::get().getDevice().getPhysicalDevice());
vkGetDeviceQueue(Render_Core::get().getDevice().get(), _families->graphics_family.value(), 0, &_graphics_queue);
vkGetDeviceQueue(Render_Core::get().getDevice().get(), _families->present_family.value(), 0, &_present_queue);
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : got graphics and present queues");
#endif
}
}

152
runtime/Sources/Renderer/Core/RenderCore.cpp git.filemode.normal_file
View File

@@ -0,0 +1,152 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* render_core.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/12/17 23:33:34 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:02:06 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#define VK_NO_PROTOTYPES
#define VOLK_IMPLEMENTATION
#include <volk.h>
#include <pre_compiled.h>
#include <renderer/core/render_core.h>
#include <renderer/command/vk_cmd_buffer.h>
#ifdef DEBUG
#ifdef MLX_COMPILER_MSVC
#pragma NOTE("MLX is being compiled in debug mode, this activates Vulkan's validation layers and debug messages which may impact rendering performances")
#else
#warning "MLX is being compiled in debug mode, this activates Vulkan's validation layers and debug messages which may impact rendering performances"
#endif
#endif
namespace mlx
{
namespace RCore
{
std::optional<std::uint32_t> findMemoryType(std::uint32_t typeFilter, VkMemoryPropertyFlags properties, bool error)
{
VkPhysicalDeviceMemoryProperties memProperties;
vkGetPhysicalDeviceMemoryProperties(Render_Core::get().getDevice().getPhysicalDevice(), &memProperties);
for(std::uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
{
if((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
return i;
}
if(error)
core::error::report(e_kind::fatal_error, "Vulkan : failed to find suitable memory type");
return std::nullopt;
}
const char* verbaliseResultVk(VkResult result)
{
switch(result)
{
case VK_SUCCESS: return "Success";
case VK_NOT_READY: return "A fence or query has not yet completed";
case VK_TIMEOUT: return "A wait operation has not completed in the specified time";
case VK_EVENT_SET: return "An event is signaled";
case VK_EVENT_RESET: return "An event is unsignaled";
case VK_INCOMPLETE: return "A return array was too small for the result";
case VK_ERROR_OUT_OF_HOST_MEMORY: return "A host memory allocation has failed";
case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "A device memory allocation has failed";
case VK_ERROR_INITIALIZATION_FAILED: return "Initialization of an object could not be completed for implementation-specific reasons";
case VK_ERROR_DEVICE_LOST: return "The logical or physical device has been lost";
case VK_ERROR_MEMORY_MAP_FAILED: return "Mapping of a memory object has failed";
case VK_ERROR_LAYER_NOT_PRESENT: return "A requested layer is not present or could not be loaded";
case VK_ERROR_EXTENSION_NOT_PRESENT: return "A requested extension is not supported";
case VK_ERROR_FEATURE_NOT_PRESENT: return "A requested feature is not supported";
case VK_ERROR_INCOMPATIBLE_DRIVER: return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible";
case VK_ERROR_TOO_MANY_OBJECTS: return "Too many objects of the type have already been created";
case VK_ERROR_FORMAT_NOT_SUPPORTED: return "A requested format is not supported on this device";
case VK_ERROR_SURFACE_LOST_KHR: return "A surface is no longer available";
case VK_SUBOPTIMAL_KHR: return "A swapchain no longer matches the surface properties exactly, but can still be used";
case VK_ERROR_OUT_OF_DATE_KHR: return "A surface has changed in such a way that it is no longer compatible with the swapchain";
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: return "The display used by a swapchain does not use the same presentable image layout";
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return "The requested window is already connected to a VkSurfaceKHR, or to some other non-Vulkan API";
case VK_ERROR_VALIDATION_FAILED_EXT: return "A validation layer found an error";
default: return "Unknown Vulkan error";
}
return nullptr;
}
VkPipelineStageFlags accessFlagsToPipelineStage(VkAccessFlags accessFlags, VkPipelineStageFlags stageFlags)
{
VkPipelineStageFlags stages = 0;
while(accessFlags != 0)
{
VkAccessFlagBits AccessFlag = static_cast<VkAccessFlagBits>(accessFlags & (~(accessFlags - 1)));
if(AccessFlag == 0 || (AccessFlag & (AccessFlag - 1)) != 0)
core::error::report(e_kind::fatal_error, "Vulkan : an error has been caught during access flag to pipeline stage operation");
accessFlags &= ~AccessFlag;
switch(AccessFlag)
{
case VK_ACCESS_INDIRECT_COMMAND_READ_BIT: stages |= VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; break;
case VK_ACCESS_INDEX_READ_BIT: stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; break;
case VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT: stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; break;
case VK_ACCESS_UNIFORM_READ_BIT: stages |= stageFlags | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; break;
case VK_ACCESS_INPUT_ATTACHMENT_READ_BIT: stages |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break;
case VK_ACCESS_SHADER_READ_BIT: stages |= stageFlags | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; break;
case VK_ACCESS_SHADER_WRITE_BIT: stages |= stageFlags | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; break;
case VK_ACCESS_COLOR_ATTACHMENT_READ_BIT: stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; break;
case VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT: stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; break;
case VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT: stages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break;
case VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT: stages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break;
case VK_ACCESS_TRANSFER_READ_BIT: stages |= VK_PIPELINE_STAGE_TRANSFER_BIT; break;
case VK_ACCESS_TRANSFER_WRITE_BIT: stages |= VK_PIPELINE_STAGE_TRANSFER_BIT; break;
case VK_ACCESS_HOST_READ_BIT: stages |= VK_PIPELINE_STAGE_HOST_BIT; break;
case VK_ACCESS_HOST_WRITE_BIT: stages |= VK_PIPELINE_STAGE_HOST_BIT; break;
case VK_ACCESS_MEMORY_READ_BIT: break;
case VK_ACCESS_MEMORY_WRITE_BIT: break;
default: core::error::report(e_kind::error, "Vulkan : unknown access flag"); break;
}
}
return stages;
}
}
void Render_Core::init()
{
if(volkInitialize() != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan loader : cannot load %s, are you sure Vulkan is installed on your system ?", VULKAN_LIB_NAME);
_instance.init();
volkLoadInstance(_instance.get());
_layers.init();
_device.init();
volkLoadDevice(_device.get());
_queues.init();
_allocator.init();
_cmd_manager.init();
_is_init = true;
}
void Render_Core::destroy()
{
if(!_is_init)
return;
vkDeviceWaitIdle(_device());
_pool_manager.destroyAllPools();
_cmd_manager.destroy();
_allocator.destroy();
_device.destroy();
_layers.destroy();
_instance.destroy();
_is_init = false;
}
}

45
runtime/Sources/Renderer/Core/Semaphore.cpp git.filemode.normal_file
View File

@@ -0,0 +1,45 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_semaphore.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/08 19:01:08 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:02:25 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "vk_semaphore.h"
#include "render_core.h"
#include <renderer/renderer.h>
namespace mlx
{
void Semaphore::init()
{
VkSemaphoreCreateInfo semaphoreInfo{};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkResult res;
if( (res = vkCreateSemaphore(Render_Core::get().getDevice().get(), &semaphoreInfo, nullptr, &_image_available_semaphore)) != VK_SUCCESS ||
(res = vkCreateSemaphore(Render_Core::get().getDevice().get(), &semaphoreInfo, nullptr, &_render_finished_semaphore)) != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create a synchronization object (semaphore), %s", RCore::verbaliseResultVk(res));
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : created new semaphores");
#endif
}
void Semaphore::destroy() noexcept
{
vkDestroySemaphore(Render_Core::get().getDevice().get(), _render_finished_semaphore, nullptr);
_render_finished_semaphore = VK_NULL_HANDLE;
vkDestroySemaphore(Render_Core::get().getDevice().get(), _image_available_semaphore, nullptr);
_image_available_semaphore = VK_NULL_HANDLE;
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : destroyed semaphores");
#endif
}
}

47
runtime/Sources/Renderer/Core/Surface.cpp git.filemode.normal_file
View File

@@ -0,0 +1,47 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_surface.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/08 18:58:49 by maldavid #+# #+# */
/* Updated: 2024/03/25 22:25:55 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "render_core.h"
#include <platform/window.h>
#include <renderer/renderer.h>
namespace mlx
{
void Surface::create(Renderer& renderer)
{
if(glfwCreateWindowSurface(Render_Core::get().getInstance().get(), renderer.getWindow()->getNativeWindow(), NULL, &_surface) != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create a surface");
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : created new surface");
#endif
}
VkSurfaceFormatKHR Surface::chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats)
{
auto it = std::find_if(availableFormats.begin(), availableFormats.end(), [](VkSurfaceFormatKHR format)
{
return format.format == VK_FORMAT_R8G8B8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
});
return (it == availableFormats.end() ? availableFormats[0] : *it);
}
void Surface::destroy() noexcept
{
vkDestroySurfaceKHR(Render_Core::get().getInstance().get(), _surface, nullptr);
_surface = VK_NULL_HANDLE;
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : destroyed a surface");
#endif
}
}

View File

@@ -0,0 +1,129 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_validation_layers.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/12/19 14:05:25 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:00:06 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "render_core.h"
#include "vulkan/vulkan_core.h"
#include <core/errors.h>
namespace mlx
{
void ValidationLayers::init()
{
if constexpr(!enableValidationLayers)
return;
std::uint32_t extensionCount;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> extensions(extensionCount);
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
if(!std::any_of(extensions.begin(), extensions.end(), [=](VkExtensionProperties ext) { return std::strcmp(ext.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0; }))
{
core::error::report(e_kind::warning , "Vulkan : %s not present, debug utils are disabled", VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
return;
}
VkDebugUtilsMessengerCreateInfoEXT createInfo{};
populateDebugMessengerCreateInfo(createInfo);
VkResult res = createDebugUtilsMessengerEXT(&createInfo, nullptr);
if(res != VK_SUCCESS)
core::error::report(e_kind::warning, "Vulkan : failed to set up debug messenger, %s", RCore::verbaliseResultVk(res));
#ifdef DEBUG
else
core::error::report(e_kind::message, "Vulkan : enabled validation layers");
#endif
_vkSetDebugUtilsObjectNameEXT = (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(Render_Core::get().getInstance().get(), "vkSetDebugUtilsObjectNameEXT");
if(!_vkSetDebugUtilsObjectNameEXT)
core::error::report(e_kind::warning, "Vulkan : failed to set up debug object names, %s", RCore::verbaliseResultVk(VK_ERROR_EXTENSION_NOT_PRESENT));
#ifdef DEBUG
else
core::error::report(e_kind::message, "Vulkan : enabled debug object names");
#endif
}
bool ValidationLayers::checkValidationLayerSupport()
{
std::uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
std::vector<VkLayerProperties> availableLayers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
return std::all_of(validationLayers.begin(), validationLayers.end(), [&](const char* layerName)
{
if(!std::any_of(availableLayers.begin(), availableLayers.end(), [=](VkLayerProperties props) { return std::strcmp(layerName, props.layerName) == 0; }))
{
core::error::report(e_kind::error, "Vulkan : a validation layer was requested but was not found ('%s')", layerName);
return false;
}
return true;
});
}
VkResult ValidationLayers::setDebugUtilsObjectNameEXT(VkObjectType object_type, std::uint64_t object_handle, const char* object_name)
{
if(!_vkSetDebugUtilsObjectNameEXT)
return VK_ERROR_EXTENSION_NOT_PRESENT;
VkDebugUtilsObjectNameInfoEXT name_info{};
name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
name_info.objectType = object_type;
name_info.objectHandle = object_handle;
name_info.pObjectName = object_name;
return _vkSetDebugUtilsObjectNameEXT(Render_Core::get().getDevice().get(), &name_info);
}
void ValidationLayers::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo)
{
createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
createInfo.pfnUserCallback = ValidationLayers::debugCallback;
}
void ValidationLayers::destroy()
{
if constexpr(enableValidationLayers)
{
destroyDebugUtilsMessengerEXT(nullptr);
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : destroyed validation layers");
#endif
}
}
VkResult ValidationLayers::createDebugUtilsMessengerEXT(const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator)
{
auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(Render_Core::get().getInstance().get(), "vkCreateDebugUtilsMessengerEXT");
return func != nullptr ? func(Render_Core::get().getInstance().get(), pCreateInfo, pAllocator, &_debug_messenger) : VK_ERROR_EXTENSION_NOT_PRESENT;
}
VKAPI_ATTR VkBool32 VKAPI_CALL ValidationLayers::debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, [[maybe_unused]] VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, [[maybe_unused]] void* pUserData)
{
if(messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
core::error::report(e_kind::error, pCallbackData->pMessage);
else if(messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
core::error::report(e_kind::warning, pCallbackData->pMessage);
return VK_FALSE;
}
void ValidationLayers::destroyDebugUtilsMessengerEXT(const VkAllocationCallbacks* pAllocator)
{
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(Render_Core::get().getInstance().get(), "vkDestroyDebugUtilsMessengerEXT");
if(func != nullptr)
func(Render_Core::get().getInstance().get(), _debug_messenger, pAllocator);
}
}

View File

@@ -0,0 +1,41 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* descriptor_pool_manager.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/01/20 06:51:47 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:02:29 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <renderer/core/render_core.h>
#include <renderer/descriptors/descriptor_pool_manager.h>
namespace mlx
{
DescriptorPool& DescriptorPoolManager::getAvailablePool()
{
for(auto& pool : _pools)
{
if(pool.getNumberOfSetsAllocated() < MAX_SETS_PER_POOL)
return pool;
}
VkDescriptorPoolSize pool_sizes[] = {
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, (MAX_FRAMES_IN_FLIGHT * NUMBER_OF_UNIFORM_BUFFERS) },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, MAX_SETS_PER_POOL - (MAX_FRAMES_IN_FLIGHT * NUMBER_OF_UNIFORM_BUFFERS) }
};
_pools.emplace_front().init((sizeof(pool_sizes) / sizeof(VkDescriptorPoolSize)), pool_sizes);
return _pools.front();
}
void DescriptorPoolManager::destroyAllPools()
{
for(auto& pool : _pools)
pool.destroy();
_pools.clear();
}
}

View File

@@ -0,0 +1,57 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_descriptor_pool.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/01/23 18:34:23 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:02:37 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "vk_descriptor_pool.h"
#include <renderer/descriptors/vk_descriptor_set.h>
#include <renderer/core/render_core.h>
namespace mlx
{
void DescriptorPool::init(std::size_t n, VkDescriptorPoolSize* size)
{
VkDescriptorPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = n;
poolInfo.pPoolSizes = size;
poolInfo.maxSets = MAX_SETS_PER_POOL;
poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
VkResult res = vkCreateDescriptorPool(Render_Core::get().getDevice().get(), &poolInfo, nullptr, &_pool);
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create descriptor pool, %s", RCore::verbaliseResultVk(res));
_allocated_sets++;
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : created new descriptor pool");
#endif
}
void DescriptorPool::freeDescriptor(const DescriptorSet& set)
{
if(!isInit())
return;
const auto& sets = set.getAllFramesDescriptorSets();
vkFreeDescriptorSets(Render_Core::get().getDevice().get(), _pool, sets.size(), sets.data());
_allocated_sets--; // if this goes in underflow I quit
}
void DescriptorPool::destroy() noexcept
{
if(_pool != VK_NULL_HANDLE)
vkDestroyDescriptorPool(Render_Core::get().getDevice().get(), _pool, nullptr);
_pool = VK_NULL_HANDLE;
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : destroyed a descriptor pool");
#endif
}
}

View File

@@ -0,0 +1,131 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_descriptor_set.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/01/23 18:40:44 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:02:43 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "vk_descriptor_set.h"
#include "renderer/core/render_core.h"
#include "vk_descriptor_pool.h"
#include "vk_descriptor_set_layout.h"
#include <renderer/buffers/vk_ubo.h>
#include <renderer/renderer.h>
#include <renderer/images/vk_image.h>
#include <core/profiler.h>
namespace mlx
{
void DescriptorSet::init(Renderer* renderer, DescriptorPool* pool, DescriptorSetLayout* layout)
{
MLX_PROFILE_FUNCTION();
_renderer = renderer;
_layout = layout;
_pool = pool;
auto device = Render_Core::get().getDevice().get();
std::array<VkDescriptorSetLayout, MAX_FRAMES_IN_FLIGHT> layouts;
layouts.fill(layout->get());
VkDescriptorSetAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = _pool->get();
allocInfo.descriptorSetCount = static_cast<std::uint32_t>(MAX_FRAMES_IN_FLIGHT);
allocInfo.pSetLayouts = layouts.data();
VkResult res = vkAllocateDescriptorSets(device, &allocInfo, _desc_set.data());
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to allocate descriptor set, %s", RCore::verbaliseResultVk(res));
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : created new descriptor set");
#endif
}
void DescriptorSet::writeDescriptor(int binding, UBO* ubo) const noexcept
{
MLX_PROFILE_FUNCTION();
auto device = Render_Core::get().getDevice().get();
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
VkDescriptorBufferInfo bufferInfo{};
bufferInfo.buffer = ubo->get(i);
bufferInfo.offset = ubo->getOffset(i);
bufferInfo.range = ubo->getSize(i);
VkWriteDescriptorSet descriptorWrite{};
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.dstSet = _desc_set[i];
descriptorWrite.dstBinding = binding;
descriptorWrite.dstArrayElement = 0;
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrite.descriptorCount = 1;
descriptorWrite.pBufferInfo = &bufferInfo;
vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr);
}
}
void DescriptorSet::writeDescriptor(int binding, const Image& image) const noexcept
{
MLX_PROFILE_FUNCTION();
auto device = Render_Core::get().getDevice().get();
VkDescriptorImageInfo imageInfo{};
imageInfo.imageLayout = image.getLayout();
imageInfo.imageView = image.getImageView();
imageInfo.sampler = image.getSampler();
VkWriteDescriptorSet descriptorWrite{};
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.dstSet = _desc_set[_renderer->getActiveImageIndex()];
descriptorWrite.dstBinding = binding;
descriptorWrite.dstArrayElement = 0;
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite.descriptorCount = 1;
descriptorWrite.pImageInfo = &imageInfo;
vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr);
}
DescriptorSet DescriptorSet::duplicate()
{
MLX_PROFILE_FUNCTION();
DescriptorSet set;
set.init(_renderer, &Render_Core::get().getDescriptorPool(), _layout);
return set;
}
VkDescriptorSet& DescriptorSet::operator()() noexcept
{
return _desc_set[_renderer->getActiveImageIndex()];
}
VkDescriptorSet& DescriptorSet::get() noexcept
{
return _desc_set[_renderer->getActiveImageIndex()];
}
void DescriptorSet::destroy() noexcept
{
MLX_PROFILE_FUNCTION();
if(_pool != nullptr && Render_Core::get().isInit()) // checks if the render core is still init (it should always be init but just in case)
_pool->freeDescriptor(*this);
for(auto& set : _desc_set)
{
if(set != VK_NULL_HANDLE)
set = VK_NULL_HANDLE;
}
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : destroyed descriptor set");
#endif
}
}

View File

@@ -0,0 +1,49 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_descriptor_set_layout.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/01/23 18:37:28 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:02:47 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "vk_descriptor_set_layout.h"
#include <renderer/core/render_core.h>
namespace mlx
{
void DescriptorSetLayout::init(std::vector<std::pair<int, VkDescriptorType>> binds, VkShaderStageFlagBits stage)
{
std::vector<VkDescriptorSetLayoutBinding> bindings(binds.size());
for(std::size_t i = 0; i < binds.size(); i++)
{
bindings[i].binding = binds[i].first;
bindings[i].descriptorCount = 1;
bindings[i].descriptorType = binds[i].second;
bindings[i].pImmutableSamplers = nullptr;
bindings[i].stageFlags = stage;
}
_bindings = std::move(binds);
VkDescriptorSetLayoutCreateInfo layoutInfo{};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.bindingCount = _bindings.size();
layoutInfo.pBindings = bindings.data();
VkResult res = vkCreateDescriptorSetLayout(Render_Core::get().getDevice().get(), &layoutInfo, nullptr, &_layout);
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create descriptor set layout, %s", RCore::verbaliseResultVk(res));
}
void DescriptorSetLayout::destroy() noexcept
{
vkDestroyDescriptorSetLayout(Render_Core::get().getDevice().get(), _layout, nullptr);
_layout = VK_NULL_HANDLE;
}
}

191
runtime/Sources/Renderer/Images/texture.cpp git.filemode.normal_file
View File

@@ -0,0 +1,191 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* texture.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/03/31 18:03:35 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:03:04 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <pre_compiled.h>
#include <core/errors.h>
#include <renderer/images/texture.h>
#include <renderer/buffers/vk_buffer.h>
#include <renderer/renderer.h>
#include <core/profiler.h>
#ifdef IMAGE_OPTIMIZED
#define TILING VK_IMAGE_TILING_OPTIMAL
#else
#define TILING VK_IMAGE_TILING_LINEAR
#endif
namespace mlx
{
void Texture::create(std::uint8_t* pixels, std::uint32_t width, std::uint32_t height, VkFormat format, const char* name, bool dedicated_memory)
{
MLX_PROFILE_FUNCTION();
Image::create(width, height, format, TILING, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, name, dedicated_memory);
Image::createImageView(VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_COLOR_BIT);
Image::createSampler();
transitionLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
std::vector<Vertex> vertexData = {
{{0, 0}, {1.f, 1.f, 1.f, 1.f}, {0.0f, 0.0f}},
{{width, 0}, {1.f, 1.f, 1.f, 1.f}, {1.0f, 0.0f}},
{{width, height}, {1.f, 1.f, 1.f, 1.f}, {1.0f, 1.0f}},
{{0, height}, {1.f, 1.f, 1.f, 1.f}, {0.0f, 1.0f}}
};
std::vector<std::uint16_t> indexData = { 0, 1, 2, 2, 3, 0 };
#ifdef DEBUG
_vbo.create(sizeof(Vertex) * vertexData.size(), vertexData.data(), name);
_ibo.create(sizeof(std::uint16_t) * indexData.size(), indexData.data(), name);
_name = name;
#else
_vbo.create(sizeof(Vertex) * vertexData.size(), vertexData.data(), nullptr);
_ibo.create(sizeof(std::uint16_t) * indexData.size(), indexData.data(), nullptr);
#endif
Buffer staging_buffer;
std::size_t size = width * height * formatSize(format);
if(pixels != nullptr)
{
#ifdef DEBUG
staging_buffer.create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, name, pixels);
#else
staging_buffer.create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, nullptr, pixels);
#endif
}
else
{
std::vector<std::uint32_t> default_pixels(width * height, 0x00000000);
#ifdef DEBUG
staging_buffer.create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, name, default_pixels.data());
#else
staging_buffer.create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, nullptr, default_pixels.data());
#endif
}
Image::copyFromBuffer(staging_buffer);
staging_buffer.destroy();
}
void Texture::setPixel(int x, int y, std::uint32_t color) noexcept
{
MLX_PROFILE_FUNCTION();
if(x < 0 || y < 0 || static_cast<std::uint32_t>(x) > getWidth() || static_cast<std::uint32_t>(y) > getHeight())
return;
if(_map == nullptr)
openCPUmap();
_cpu_map[(y * getWidth()) + x] = color;
_has_been_modified = true;
}
int Texture::getPixel(int x, int y) noexcept
{
MLX_PROFILE_FUNCTION();
if(x < 0 || y < 0 || static_cast<std::uint32_t>(x) > getWidth() || static_cast<std::uint32_t>(y) > getHeight())
return 0;
if(_map == nullptr)
openCPUmap();
std::uint32_t color = _cpu_map[(y * getWidth()) + x];
std::uint8_t* bytes = reinterpret_cast<std::uint8_t*>(&color);
std::uint8_t tmp = bytes[0];
bytes[0] = bytes[2];
bytes[2] = tmp;
return *reinterpret_cast<int*>(bytes);
}
void Texture::openCPUmap()
{
MLX_PROFILE_FUNCTION();
if(_map != nullptr)
return;
#ifdef DEBUG
core::error::report(e_kind::message, "Texture : enabling CPU mapping");
#endif
std::size_t size = getWidth() * getHeight() * formatSize(getFormat());
_buf_map.emplace();
#ifdef DEBUG
_buf_map->create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, _name.c_str());
#else
_buf_map->create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, nullptr);
#endif
Image::copyToBuffer(*_buf_map);
_buf_map->mapMem(&_map);
_cpu_map = std::vector<std::uint32_t>(getWidth() * getHeight(), 0);
std::memcpy(_cpu_map.data(), _map, size);
#ifdef DEBUG
core::error::report(e_kind::message, "Texture : mapped CPU memory using staging buffer");
#endif
}
void Texture::render(std::array<VkDescriptorSet, 2>& sets, Renderer& renderer, int x, int y)
{
MLX_PROFILE_FUNCTION();
if(_has_been_modified)
{
std::memcpy(_map, _cpu_map.data(), _cpu_map.size() * formatSize(getFormat()));
Image::copyFromBuffer(*_buf_map);
_has_been_modified = false;
}
if(!_set.isInit())
_set = renderer.getFragDescriptorSet().duplicate();
if(getLayout() != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
transitionLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
if(!_has_set_been_updated)
updateSet(0);
auto cmd = renderer.getActiveCmdBuffer();
_vbo.bind(renderer);
_ibo.bind(renderer);
glm::vec2 translate(x, y);
vkCmdPushConstants(cmd.get(), renderer.getPipeline().getPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(translate), &translate);
sets[1] = _set.get();
vkCmdBindDescriptorSets(renderer.getActiveCmdBuffer().get(), VK_PIPELINE_BIND_POINT_GRAPHICS, renderer.getPipeline().getPipelineLayout(), 0, sets.size(), sets.data(), 0, nullptr);
vkCmdDrawIndexed(cmd.get(), static_cast<std::uint32_t>(_ibo.getSize() / sizeof(std::uint16_t)), 1, 0, 0, 0);
}
void Texture::destroy() noexcept
{
MLX_PROFILE_FUNCTION();
Image::destroy();
_set.destroy();
if(_buf_map.has_value())
_buf_map->destroy();
_vbo.destroy();
_ibo.destroy();
}
Texture stbTextureLoad(std::filesystem::path file, int* w, int* h)
{
MLX_PROFILE_FUNCTION();
Texture texture;
int channels;
std::uint8_t* data = nullptr;
std::string filename = file.string();
if(!std::filesystem::exists(std::move(file)))
core::error::report(e_kind::fatal_error, "Image : file not found '%s'", filename.c_str());
if(stbi_is_hdr(filename.c_str()))
core::error::report(e_kind::fatal_error, "Texture : unsupported image format '%s'", filename.c_str());
int dummy_w;
int dummy_h;
data = stbi_load(filename.c_str(), (w == nullptr ? &dummy_w : w), (h == nullptr ? &dummy_h : h), &channels, 4);
#ifdef DEBUG
texture.create(data, (w == nullptr ? dummy_w : *w), (h == nullptr ? dummy_h : *h), VK_FORMAT_R8G8B8A8_UNORM, filename.c_str());
#else
texture.create(data, (w == nullptr ? dummy_w : *w), (h == nullptr ? dummy_h : *h), VK_FORMAT_R8G8B8A8_UNORM, nullptr);
#endif
stbi_image_free(data);
return texture;
}
}

View File

@@ -0,0 +1,58 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* texture_atlas.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/04/07 16:40:09 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:03:13 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <renderer/images/texture_atlas.h>
#ifdef IMAGE_OPTIMIZED
#define TILING VK_IMAGE_TILING_OPTIMAL
#else
#define TILING VK_IMAGE_TILING_LINEAR
#endif
namespace mlx
{
void TextureAtlas::create(std::uint8_t* pixels, std::uint32_t width, std::uint32_t height, VkFormat format, const char* name, bool dedicated_memory)
{
Image::create(width, height, format, TILING, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, name, dedicated_memory);
Image::createImageView(VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_COLOR_BIT);
Image::createSampler();
transitionLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
if(pixels == nullptr)
{
core::error::report(e_kind::warning, "Renderer : creating an empty texture atlas. They cannot be updated after creation, this might be a mistake or a bug, please report");
return;
}
Buffer staging_buffer;
std::size_t size = width * height * formatSize(format);
staging_buffer.create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, name, pixels);
Image::copyFromBuffer(staging_buffer);
staging_buffer.destroy();
}
void TextureAtlas::render(Renderer& renderer, int x, int y, std::uint32_t ibo_size) const
{
auto cmd = renderer.getActiveCmdBuffer().get();
glm::vec2 translate(x, y);
vkCmdPushConstants(cmd, renderer.getPipeline().getPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(translate), &translate);
vkCmdDrawIndexed(cmd, ibo_size / sizeof(std::uint16_t), 1, 0, 0, 0);
}
void TextureAtlas::destroy() noexcept
{
Image::destroy();
_set.destroy();
}
}

396
runtime/Sources/Renderer/Images/vk_image.cpp git.filemode.normal_file
View File

@@ -0,0 +1,396 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_image.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/01/25 11:59:07 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:03:27 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "vk_image.h"
#include <renderer/core/render_core.h>
#include <renderer/buffers/vk_buffer.h>
#include <renderer/command/vk_cmd_pool.h>
#include <renderer/core/vk_fence.h>
namespace mlx
{
bool isStencilFormat(VkFormat format)
{
switch(format)
{
case VK_FORMAT_D32_SFLOAT_S8_UINT:
case VK_FORMAT_D24_UNORM_S8_UINT:
return true;
default: return false;
}
}
bool isDepthFormat(VkFormat format)
{
switch(format)
{
case VK_FORMAT_D16_UNORM:
case VK_FORMAT_D32_SFLOAT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D16_UNORM_S8_UINT:
return true;
default: return false;
}
}
VkFormat bitsToFormat(std::uint32_t bits)
{
switch(bits)
{
case 8: return VK_FORMAT_R8_UNORM;
case 16: return VK_FORMAT_R8G8_UNORM;
case 24: return VK_FORMAT_R8G8B8_UNORM;
case 32: return VK_FORMAT_R8G8B8A8_UNORM;
case 48: return VK_FORMAT_R16G16B16_SFLOAT;
case 64: return VK_FORMAT_R16G16B16A16_SFLOAT;
case 96: return VK_FORMAT_R32G32B32_SFLOAT;
case 128: return VK_FORMAT_R32G32B32A32_SFLOAT;
default:
core::error::report(e_kind::fatal_error, "Vulkan : unsupported image bit-depth");
return VK_FORMAT_R8G8B8A8_UNORM;
}
}
VkPipelineStageFlags layoutToAccessMask(VkImageLayout layout, bool isDestination)
{
VkPipelineStageFlags accessMask = 0;
switch(layout)
{
case VK_IMAGE_LAYOUT_UNDEFINED:
if(isDestination)
core::error::report(e_kind::error, "Vulkan : the new layout used in a transition must not be VK_IMAGE_LAYOUT_UNDEFINED");
break;
case VK_IMAGE_LAYOUT_GENERAL: accessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: accessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: accessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
accessMask = VK_ACCESS_SHADER_READ_BIT; // VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: accessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: accessMask = VK_ACCESS_TRANSFER_READ_BIT; break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: accessMask = VK_ACCESS_TRANSFER_WRITE_BIT; break;
case VK_IMAGE_LAYOUT_PREINITIALIZED:
if(!isDestination)
accessMask = VK_ACCESS_HOST_WRITE_BIT;
else
core::error::report(e_kind::error, "Vulkan : the new layout used in a transition must not be VK_IMAGE_LAYOUT_PREINITIALIZED");
break;
case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL: accessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; break;
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL: accessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; break;
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: accessMask = VK_ACCESS_MEMORY_READ_BIT; break;
default: core::error::report(e_kind::error, "Vulkan : unexpected image layout"); break;
}
return accessMask;
}
void Image::create(std::uint32_t width, std::uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, const char* name, bool dedicated_memory)
{
_width = width;
_height = height;
_format = format;
_tiling = tiling;
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = width;
imageInfo.extent.height = height;
imageInfo.extent.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = format;
imageInfo.tiling = tiling;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = usage;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocationCreateInfo alloc_info{};
alloc_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
if(dedicated_memory)
{
alloc_info.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
alloc_info.priority = 1.0f;
}
_allocation = Render_Core::get().getAllocator().createImage(&imageInfo, &alloc_info, _image, name);
#ifdef DEBUG
_name = name;
#endif
}
void Image::createImageView(VkImageViewType type, VkImageAspectFlags aspectFlags) noexcept
{
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = _image;
viewInfo.viewType = type;
viewInfo.format = _format;
viewInfo.subresourceRange.aspectMask = aspectFlags;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VkResult res = vkCreateImageView(Render_Core::get().getDevice().get(), &viewInfo, nullptr, &_image_view);
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create an image view, %s", RCore::verbaliseResultVk(res));
#ifdef DEBUG
else
Render_Core::get().getLayers().setDebugUtilsObjectNameEXT(VK_OBJECT_TYPE_IMAGE_VIEW, (std::uint64_t)_image_view, _name.c_str());
#endif
}
void Image::createSampler() noexcept
{
VkSamplerCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
info.magFilter = VK_FILTER_NEAREST;
info.minFilter = VK_FILTER_NEAREST;
info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
info.minLod = -1000;
info.maxLod = 1000;
info.anisotropyEnable = VK_FALSE;
info.maxAnisotropy = 1.0f;
VkResult res = vkCreateSampler(Render_Core::get().getDevice().get(), &info, nullptr, &_sampler);
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create an image sampler, %s", RCore::verbaliseResultVk(res));
#ifdef DEBUG
else
Render_Core::get().getLayers().setDebugUtilsObjectNameEXT(VK_OBJECT_TYPE_SAMPLER, (std::uint64_t)_sampler, _name.c_str());
#endif
}
void Image::copyFromBuffer(Buffer& buffer)
{
CmdBuffer& cmd = Render_Core::get().getSingleTimeCmdBuffer();
cmd.beginRecord();
VkImageLayout layout_save = _layout;
transitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &cmd);
cmd.copyBufferToImage(buffer, *this);
transitionLayout(layout_save, &cmd);
cmd.endRecord();
cmd.submitIdle();
}
void Image::copyToBuffer(Buffer& buffer)
{
CmdBuffer& cmd = Render_Core::get().getSingleTimeCmdBuffer();
cmd.beginRecord();
VkImageLayout layout_save = _layout;
transitionLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, &cmd);
cmd.copyImagetoBuffer(*this, buffer);
transitionLayout(layout_save, &cmd);
cmd.endRecord();
cmd.submitIdle();
}
void Image::transitionLayout(VkImageLayout new_layout, CmdBuffer* cmd)
{
if(new_layout == _layout)
return;
bool singleTime = (cmd == nullptr);
if(singleTime)
{
cmd = &Render_Core::get().getSingleTimeCmdBuffer();
cmd->beginRecord();
}
cmd->transitionImageLayout(*this, new_layout);
if(singleTime)
{
cmd->endRecord();
cmd->submitIdle();
}
_layout = new_layout;
}
void Image::destroySampler() noexcept
{
if(_sampler != VK_NULL_HANDLE)
vkDestroySampler(Render_Core::get().getDevice().get(), _sampler, nullptr);
_sampler = VK_NULL_HANDLE;
}
void Image::destroyImageView() noexcept
{
if(_image_view != VK_NULL_HANDLE)
vkDestroyImageView(Render_Core::get().getDevice().get(), _image_view, nullptr);
_image_view = VK_NULL_HANDLE;
}
void Image::destroy() noexcept
{
destroySampler();
destroyImageView();
if(_image != VK_NULL_HANDLE)
Render_Core::get().getAllocator().destroyImage(_allocation, _image);
_image = VK_NULL_HANDLE;
}
std::uint32_t formatSize(VkFormat format)
{
switch(format)
{
case VK_FORMAT_UNDEFINED: return 0;
case VK_FORMAT_R4G4_UNORM_PACK8: return 1;
case VK_FORMAT_R4G4B4A4_UNORM_PACK16: return 2;
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: return 2;
case VK_FORMAT_R5G6B5_UNORM_PACK16: return 2;
case VK_FORMAT_B5G6R5_UNORM_PACK16: return 2;
case VK_FORMAT_R5G5B5A1_UNORM_PACK16: return 2;
case VK_FORMAT_B5G5R5A1_UNORM_PACK16: return 2;
case VK_FORMAT_A1R5G5B5_UNORM_PACK16: return 2;
case VK_FORMAT_R8_UNORM: return 1;
case VK_FORMAT_R8_SNORM: return 1;
case VK_FORMAT_R8_USCALED: return 1;
case VK_FORMAT_R8_SSCALED: return 1;
case VK_FORMAT_R8_UINT: return 1;
case VK_FORMAT_R8_SINT: return 1;
case VK_FORMAT_R8_SRGB: return 1;
case VK_FORMAT_R8G8_UNORM: return 2;
case VK_FORMAT_R8G8_SNORM: return 2;
case VK_FORMAT_R8G8_USCALED: return 2;
case VK_FORMAT_R8G8_SSCALED: return 2;
case VK_FORMAT_R8G8_UINT: return 2;
case VK_FORMAT_R8G8_SINT: return 2;
case VK_FORMAT_R8G8_SRGB: return 2;
case VK_FORMAT_R8G8B8_UNORM: return 3;
case VK_FORMAT_R8G8B8_SNORM: return 3;
case VK_FORMAT_R8G8B8_USCALED: return 3;
case VK_FORMAT_R8G8B8_SSCALED: return 3;
case VK_FORMAT_R8G8B8_UINT: return 3;
case VK_FORMAT_R8G8B8_SINT: return 3;
case VK_FORMAT_R8G8B8_SRGB: return 3;
case VK_FORMAT_B8G8R8_UNORM: return 3;
case VK_FORMAT_B8G8R8_SNORM: return 3;
case VK_FORMAT_B8G8R8_USCALED: return 3;
case VK_FORMAT_B8G8R8_SSCALED: return 3;
case VK_FORMAT_B8G8R8_UINT: return 3;
case VK_FORMAT_B8G8R8_SINT: return 3;
case VK_FORMAT_B8G8R8_SRGB: return 3;
case VK_FORMAT_R8G8B8A8_UNORM: return 4;
case VK_FORMAT_R8G8B8A8_SNORM: return 4;
case VK_FORMAT_R8G8B8A8_USCALED: return 4;
case VK_FORMAT_R8G8B8A8_SSCALED: return 4;
case VK_FORMAT_R8G8B8A8_UINT: return 4;
case VK_FORMAT_R8G8B8A8_SINT: return 4;
case VK_FORMAT_R8G8B8A8_SRGB: return 4;
case VK_FORMAT_B8G8R8A8_UNORM: return 4;
case VK_FORMAT_B8G8R8A8_SNORM: return 4;
case VK_FORMAT_B8G8R8A8_USCALED: return 4;
case VK_FORMAT_B8G8R8A8_SSCALED: return 4;
case VK_FORMAT_B8G8R8A8_UINT: return 4;
case VK_FORMAT_B8G8R8A8_SINT: return 4;
case VK_FORMAT_B8G8R8A8_SRGB: return 4;
case VK_FORMAT_A8B8G8R8_UNORM_PACK32: return 4;
case VK_FORMAT_A8B8G8R8_SNORM_PACK32: return 4;
case VK_FORMAT_A8B8G8R8_USCALED_PACK32: return 4;
case VK_FORMAT_A8B8G8R8_SSCALED_PACK32: return 4;
case VK_FORMAT_A8B8G8R8_UINT_PACK32: return 4;
case VK_FORMAT_A8B8G8R8_SINT_PACK32: return 4;
case VK_FORMAT_A8B8G8R8_SRGB_PACK32: return 4;
case VK_FORMAT_A2R10G10B10_UNORM_PACK32: return 4;
case VK_FORMAT_A2R10G10B10_SNORM_PACK32: return 4;
case VK_FORMAT_A2R10G10B10_USCALED_PACK32: return 4;
case VK_FORMAT_A2R10G10B10_SSCALED_PACK32: return 4;
case VK_FORMAT_A2R10G10B10_UINT_PACK32: return 4;
case VK_FORMAT_A2R10G10B10_SINT_PACK32: return 4;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return 4;
case VK_FORMAT_A2B10G10R10_SNORM_PACK32: return 4;
case VK_FORMAT_A2B10G10R10_USCALED_PACK32: return 4;
case VK_FORMAT_A2B10G10R10_SSCALED_PACK32: return 4;
case VK_FORMAT_A2B10G10R10_UINT_PACK32: return 4;
case VK_FORMAT_A2B10G10R10_SINT_PACK32: return 4;
case VK_FORMAT_R16_UNORM: return 2;
case VK_FORMAT_R16_SNORM: return 2;
case VK_FORMAT_R16_USCALED: return 2;
case VK_FORMAT_R16_SSCALED: return 2;
case VK_FORMAT_R16_UINT: return 2;
case VK_FORMAT_R16_SINT: return 2;
case VK_FORMAT_R16_SFLOAT: return 2;
case VK_FORMAT_R16G16_UNORM: return 4;
case VK_FORMAT_R16G16_SNORM: return 4;
case VK_FORMAT_R16G16_USCALED: return 4;
case VK_FORMAT_R16G16_SSCALED: return 4;
case VK_FORMAT_R16G16_UINT: return 4;
case VK_FORMAT_R16G16_SINT: return 4;
case VK_FORMAT_R16G16_SFLOAT: return 4;
case VK_FORMAT_R16G16B16_UNORM: return 6;
case VK_FORMAT_R16G16B16_SNORM: return 6;
case VK_FORMAT_R16G16B16_USCALED: return 6;
case VK_FORMAT_R16G16B16_SSCALED: return 6;
case VK_FORMAT_R16G16B16_UINT: return 6;
case VK_FORMAT_R16G16B16_SINT: return 6;
case VK_FORMAT_R16G16B16_SFLOAT: return 6;
case VK_FORMAT_R16G16B16A16_UNORM: return 8;
case VK_FORMAT_R16G16B16A16_SNORM: return 8;
case VK_FORMAT_R16G16B16A16_USCALED: return 8;
case VK_FORMAT_R16G16B16A16_SSCALED: return 8;
case VK_FORMAT_R16G16B16A16_UINT: return 8;
case VK_FORMAT_R16G16B16A16_SINT: return 8;
case VK_FORMAT_R16G16B16A16_SFLOAT: return 8;
case VK_FORMAT_R32_UINT: return 4;
case VK_FORMAT_R32_SINT: return 4;
case VK_FORMAT_R32_SFLOAT: return 4;
case VK_FORMAT_R32G32_UINT: return 8;
case VK_FORMAT_R32G32_SINT: return 8;
case VK_FORMAT_R32G32_SFLOAT: return 8;
case VK_FORMAT_R32G32B32_UINT: return 12;
case VK_FORMAT_R32G32B32_SINT: return 12;
case VK_FORMAT_R32G32B32_SFLOAT: return 12;
case VK_FORMAT_R32G32B32A32_UINT: return 16;
case VK_FORMAT_R32G32B32A32_SINT: return 16;
case VK_FORMAT_R32G32B32A32_SFLOAT: return 16;
case VK_FORMAT_R64_UINT: return 8;
case VK_FORMAT_R64_SINT: return 8;
case VK_FORMAT_R64_SFLOAT: return 8;
case VK_FORMAT_R64G64_UINT: return 16;
case VK_FORMAT_R64G64_SINT: return 16;
case VK_FORMAT_R64G64_SFLOAT: return 16;
case VK_FORMAT_R64G64B64_UINT: return 24;
case VK_FORMAT_R64G64B64_SINT: return 24;
case VK_FORMAT_R64G64B64_SFLOAT: return 24;
case VK_FORMAT_R64G64B64A64_UINT: return 32;
case VK_FORMAT_R64G64B64A64_SINT: return 32;
case VK_FORMAT_R64G64B64A64_SFLOAT: return 32;
case VK_FORMAT_B10G11R11_UFLOAT_PACK32: return 4;
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: return 4;
default: return 0;
}
}
}

View File

@@ -0,0 +1,330 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* pipeline.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/12/18 21:27:38 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:03:31 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "pipeline.h"
#include <renderer/renderer.h>
#include <renderer/core/render_core.h>
#include <renderer/descriptors/vk_descriptor_set_layout.h>
namespace mlx
{
/**
#version 450 core
layout(location = 0) in vec2 aPos;
layout(location = 1) in vec4 aColor;
layout(location = 2) in vec2 aUV;
layout(set = 0, binding = 0) uniform uProjection {
mat4 mat;
} uProj;
layout(push_constant) uniform uModelPushConstant {
vec2 vec;
} uTranslate;
out gl_PerVertex {
vec4 gl_Position;
};
layout(location = 0) out struct {
vec4 Color;
vec2 UV;
} Out;
void main()
{
Out.Color = aColor;
Out.UV = aUV;
vec2 pos = aPos + uTranslate.vec;
gl_Position = uProj.mat * vec4(pos.x, pos.y, 0.0, 1.0);
}
*/
const std::vector<std::uint32_t> vertex_shader = { // precompiled vertex shader
0x07230203,0x00010000,0x0008000b,0x0000003b,0x00000000,0x00020011,0x00000001,0x0006000b,
0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
0x000a000f,0x00000000,0x00000004,0x6e69616d,0x00000000,0x0000000b,0x0000000f,0x00000015,
0x0000001b,0x00000026,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d,
0x00000000,0x00030005,0x00000009,0x00000000,0x00050006,0x00000009,0x00000000,0x6f6c6f43,
0x00000072,0x00040006,0x00000009,0x00000001,0x00005655,0x00030005,0x0000000b,0x0074754f,
0x00040005,0x0000000f,0x6c6f4361,0x0000726f,0x00030005,0x00000015,0x00565561,0x00030005,
0x0000001a,0x00736f70,0x00040005,0x0000001b,0x736f5061,0x00000000,0x00070005,0x0000001d,
0x646f4d75,0x75506c65,0x6f436873,0x6174736e,0x0000746e,0x00040006,0x0000001d,0x00000000,
0x00636576,0x00050005,0x0000001f,0x61725475,0x616c736e,0x00006574,0x00060005,0x00000024,
0x505f6c67,0x65567265,0x78657472,0x00000000,0x00060006,0x00000024,0x00000000,0x505f6c67,
0x7469736f,0x006e6f69,0x00030005,0x00000026,0x00000000,0x00050005,0x00000028,0x6f725075,
0x7463656a,0x006e6f69,0x00040006,0x00000028,0x00000000,0x0074616d,0x00040005,0x0000002a,
0x6f725075,0x0000006a,0x00040047,0x0000000b,0x0000001e,0x00000000,0x00040047,0x0000000f,
0x0000001e,0x00000001,0x00040047,0x00000015,0x0000001e,0x00000002,0x00040047,0x0000001b,
0x0000001e,0x00000000,0x00050048,0x0000001d,0x00000000,0x00000023,0x00000000,0x00030047,
0x0000001d,0x00000002,0x00050048,0x00000024,0x00000000,0x0000000b,0x00000000,0x00030047,
0x00000024,0x00000002,0x00040048,0x00000028,0x00000000,0x00000005,0x00050048,0x00000028,
0x00000000,0x00000023,0x00000000,0x00050048,0x00000028,0x00000000,0x00000007,0x00000010,
0x00030047,0x00000028,0x00000002,0x00040047,0x0000002a,0x00000022,0x00000000,0x00040047,
0x0000002a,0x00000021,0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,
0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040017,
0x00000008,0x00000006,0x00000002,0x0004001e,0x00000009,0x00000007,0x00000008,0x00040020,
0x0000000a,0x00000003,0x00000009,0x0004003b,0x0000000a,0x0000000b,0x00000003,0x00040015,
0x0000000c,0x00000020,0x00000001,0x0004002b,0x0000000c,0x0000000d,0x00000000,0x00040020,
0x0000000e,0x00000001,0x00000007,0x0004003b,0x0000000e,0x0000000f,0x00000001,0x00040020,
0x00000011,0x00000003,0x00000007,0x0004002b,0x0000000c,0x00000013,0x00000001,0x00040020,
0x00000014,0x00000001,0x00000008,0x0004003b,0x00000014,0x00000015,0x00000001,0x00040020,
0x00000017,0x00000003,0x00000008,0x00040020,0x00000019,0x00000007,0x00000008,0x0004003b,
0x00000014,0x0000001b,0x00000001,0x0003001e,0x0000001d,0x00000008,0x00040020,0x0000001e,
0x00000009,0x0000001d,0x0004003b,0x0000001e,0x0000001f,0x00000009,0x00040020,0x00000020,
0x00000009,0x00000008,0x0003001e,0x00000024,0x00000007,0x00040020,0x00000025,0x00000003,
0x00000024,0x0004003b,0x00000025,0x00000026,0x00000003,0x00040018,0x00000027,0x00000007,
0x00000004,0x0003001e,0x00000028,0x00000027,0x00040020,0x00000029,0x00000002,0x00000028,
0x0004003b,0x00000029,0x0000002a,0x00000002,0x00040020,0x0000002b,0x00000002,0x00000027,
0x00040015,0x0000002e,0x00000020,0x00000000,0x0004002b,0x0000002e,0x0000002f,0x00000000,
0x00040020,0x00000030,0x00000007,0x00000006,0x0004002b,0x0000002e,0x00000033,0x00000001,
0x0004002b,0x00000006,0x00000036,0x00000000,0x0004002b,0x00000006,0x00000037,0x3f800000,
0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003b,
0x00000019,0x0000001a,0x00000007,0x0004003d,0x00000007,0x00000010,0x0000000f,0x00050041,
0x00000011,0x00000012,0x0000000b,0x0000000d,0x0003003e,0x00000012,0x00000010,0x0004003d,
0x00000008,0x00000016,0x00000015,0x00050041,0x00000017,0x00000018,0x0000000b,0x00000013,
0x0003003e,0x00000018,0x00000016,0x0004003d,0x00000008,0x0000001c,0x0000001b,0x00050041,
0x00000020,0x00000021,0x0000001f,0x0000000d,0x0004003d,0x00000008,0x00000022,0x00000021,
0x00050081,0x00000008,0x00000023,0x0000001c,0x00000022,0x0003003e,0x0000001a,0x00000023,
0x00050041,0x0000002b,0x0000002c,0x0000002a,0x0000000d,0x0004003d,0x00000027,0x0000002d,
0x0000002c,0x00050041,0x00000030,0x00000031,0x0000001a,0x0000002f,0x0004003d,0x00000006,
0x00000032,0x00000031,0x00050041,0x00000030,0x00000034,0x0000001a,0x00000033,0x0004003d,
0x00000006,0x00000035,0x00000034,0x00070050,0x00000007,0x00000038,0x00000032,0x00000035,
0x00000036,0x00000037,0x00050091,0x00000007,0x00000039,0x0000002d,0x00000038,0x00050041,
0x00000011,0x0000003a,0x00000026,0x0000000d,0x0003003e,0x0000003a,0x00000039,0x000100fd,
0x00010038
};
/**
#version 450 core
layout(location = 0) out vec4 fColor;
layout(set = 1, binding = 0) uniform sampler2D sTexture;
layout(location = 0) in struct {
vec4 Color;
vec2 UV;
} In;
void main()
{
vec4 process_color = In.Color * texture(sTexture, In.UV.st);
if(process_color.w == 0)
discard;
fColor = process_color;
}
*/
const std::vector<std::uint32_t> fragment_shader = { // pre compiled fragment shader
0x07230203,0x00010000,0x0008000b,0x0000002c,0x00000000,0x00020011,0x00000001,0x0006000b,
0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x0000000d,0x0000002a,0x00030010,
0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d,
0x00000000,0x00060005,0x00000009,0x636f7270,0x5f737365,0x6f6c6f63,0x00000072,0x00030005,
0x0000000b,0x00000000,0x00050006,0x0000000b,0x00000000,0x6f6c6f43,0x00000072,0x00040006,
0x0000000b,0x00000001,0x00005655,0x00030005,0x0000000d,0x00006e49,0x00050005,0x00000016,
0x78655473,0x65727574,0x00000000,0x00040005,0x0000002a,0x6c6f4366,0x0000726f,0x00040047,
0x0000000d,0x0000001e,0x00000000,0x00040047,0x00000016,0x00000022,0x00000001,0x00040047,
0x00000016,0x00000021,0x00000000,0x00040047,0x0000002a,0x0000001e,0x00000000,0x00020013,
0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,
0x00000007,0x00000006,0x00000004,0x00040020,0x00000008,0x00000007,0x00000007,0x00040017,
0x0000000a,0x00000006,0x00000002,0x0004001e,0x0000000b,0x00000007,0x0000000a,0x00040020,
0x0000000c,0x00000001,0x0000000b,0x0004003b,0x0000000c,0x0000000d,0x00000001,0x00040015,
0x0000000e,0x00000020,0x00000001,0x0004002b,0x0000000e,0x0000000f,0x00000000,0x00040020,
0x00000010,0x00000001,0x00000007,0x00090019,0x00000013,0x00000006,0x00000001,0x00000000,
0x00000000,0x00000000,0x00000001,0x00000000,0x0003001b,0x00000014,0x00000013,0x00040020,
0x00000015,0x00000000,0x00000014,0x0004003b,0x00000015,0x00000016,0x00000000,0x0004002b,
0x0000000e,0x00000018,0x00000001,0x00040020,0x00000019,0x00000001,0x0000000a,0x00040015,
0x0000001e,0x00000020,0x00000000,0x0004002b,0x0000001e,0x0000001f,0x00000003,0x00040020,
0x00000020,0x00000007,0x00000006,0x0004002b,0x00000006,0x00000023,0x00000000,0x00020014,
0x00000024,0x00040020,0x00000029,0x00000003,0x00000007,0x0004003b,0x00000029,0x0000002a,
0x00000003,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,
0x0004003b,0x00000008,0x00000009,0x00000007,0x00050041,0x00000010,0x00000011,0x0000000d,
0x0000000f,0x0004003d,0x00000007,0x00000012,0x00000011,0x0004003d,0x00000014,0x00000017,
0x00000016,0x00050041,0x00000019,0x0000001a,0x0000000d,0x00000018,0x0004003d,0x0000000a,
0x0000001b,0x0000001a,0x00050057,0x00000007,0x0000001c,0x00000017,0x0000001b,0x00050085,
0x00000007,0x0000001d,0x00000012,0x0000001c,0x0003003e,0x00000009,0x0000001d,0x00050041,
0x00000020,0x00000021,0x00000009,0x0000001f,0x0004003d,0x00000006,0x00000022,0x00000021,
0x000500b4,0x00000024,0x00000025,0x00000022,0x00000023,0x000300f7,0x00000027,0x00000000,
0x000400fa,0x00000025,0x00000026,0x00000027,0x000200f8,0x00000026,0x000100fc,0x000200f8,
0x00000027,0x0004003d,0x00000007,0x0000002b,0x00000009,0x0003003e,0x0000002a,0x0000002b,
0x000100fd,0x00010038
};
void GraphicPipeline::init(Renderer& renderer)
{
VkShaderModuleCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = vertex_shader.size() * sizeof(std::uint32_t);
createInfo.pCode = vertex_shader.data();
VkShaderModule vshader;
if(vkCreateShaderModule(Render_Core::get().getDevice().get(), &createInfo, nullptr, &vshader) != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create a vertex shader module");
VkPushConstantRange push_constant;
push_constant.offset = 0;
push_constant.size = sizeof(glm::vec2);
push_constant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = fragment_shader.size() * sizeof(std::uint32_t);
createInfo.pCode = fragment_shader.data();
VkShaderModule fshader;
if(vkCreateShaderModule(Render_Core::get().getDevice().get(), &createInfo, nullptr, &fshader) != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create a fragment shader module");
VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertShaderStageInfo.module = vshader;
vertShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragShaderStageInfo.module = fshader;
fragShaderStageInfo.pName = "main";
std::array<VkPipelineShaderStageCreateInfo, 2> stages = {vertShaderStageInfo, fragShaderStageInfo};
auto bindingDescription = Vertex::getBindingDescription();
auto attributeDescriptions = Vertex::getAttributeDescriptions();
VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo{};
vertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputStateCreateInfo.vertexBindingDescriptionCount = 1;
vertexInputStateCreateInfo.pVertexBindingDescriptions = &bindingDescription;
vertexInputStateCreateInfo.vertexAttributeDescriptionCount = static_cast<std::uint32_t>(attributeDescriptions.size());
vertexInputStateCreateInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
VkDynamicState states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
constexpr std::size_t statesCount = sizeof(states) / sizeof(VkDynamicState);
VkPipelineDynamicStateCreateInfo dynamicStates{};
dynamicStates.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicStates.dynamicStateCount = statesCount;
dynamicStates.pDynamicStates = states;
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)renderer.getFrameBuffer(0).getWidth();
viewport.height = (float)renderer.getFrameBuffer(0).getHeight();
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor{};
scissor.offset = { 0, 0 };
scissor.extent = { renderer.getFrameBuffer(0).getWidth(), renderer.getFrameBuffer(0).getHeight()};
VkPipelineViewportStateCreateInfo viewportState{};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizer{};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_NONE;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
VkPipelineMultisampleStateCreateInfo multisampling{};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
VkPipelineColorBlendStateCreateInfo colorBlending{};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 1.0f;
colorBlending.blendConstants[1] = 1.0f;
colorBlending.blendConstants[2] = 1.0f;
colorBlending.blendConstants[3] = 1.0f;
VkDescriptorSetLayout layouts[] = {
renderer.getVertDescriptorSetLayout().get(),
renderer.getFragDescriptorSetLayout().get()
};
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 2;
pipelineLayoutInfo.pSetLayouts = layouts;
pipelineLayoutInfo.pushConstantRangeCount = 1;
pipelineLayoutInfo.pPushConstantRanges = &push_constant;
if(vkCreatePipelineLayout(Render_Core::get().getDevice().get(), &pipelineLayoutInfo, nullptr, &_pipeline_layout) != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create a graphics pipeline layout");
VkGraphicsPipelineCreateInfo pipelineInfo{};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = stages.size();
pipelineInfo.pStages = stages.data();
pipelineInfo.pVertexInputState = &vertexInputStateCreateInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = &dynamicStates;
pipelineInfo.layout = _pipeline_layout;
pipelineInfo.renderPass = renderer.getRenderPass().get();
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
VkResult res = vkCreateGraphicsPipelines(Render_Core::get().getDevice().get(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &_graphics_pipeline);
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create a graphics pipeline, %s", RCore::verbaliseResultVk(res));
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : created new graphic pipeline");
#endif
vkDestroyShaderModule(Render_Core::get().getDevice().get(), fshader, nullptr);
vkDestroyShaderModule(Render_Core::get().getDevice().get(), vshader, nullptr);
}
void GraphicPipeline::destroy() noexcept
{
vkDestroyPipeline(Render_Core::get().getDevice().get(), _graphics_pipeline, nullptr);
vkDestroyPipelineLayout(Render_Core::get().getDevice().get(), _pipeline_layout, nullptr);
_graphics_pipeline = VK_NULL_HANDLE;
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : destroyed a graphics pipeline");
#endif
}
}

70
runtime/Sources/Renderer/PixelPut.cpp git.filemode.normal_file
View File

@@ -0,0 +1,70 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* pixel_put.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/03/31 15:14:50 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:03:45 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <renderer/pixel_put.h>
#include <core/profiler.h>
namespace mlx
{
void PixelPutPipeline::init(std::uint32_t width, std::uint32_t height, Renderer& renderer) noexcept
{
MLX_PROFILE_FUNCTION();
_texture.create(nullptr, width, height, VK_FORMAT_R8G8B8A8_UNORM, "__mlx_pixel_put_pipeline_texture", true);
_texture.setDescriptor(renderer.getFragDescriptorSet().duplicate());
_buffer.create(Buffer::kind::dynamic, sizeof(std::uint32_t) * (width * height), VK_BUFFER_USAGE_TRANSFER_SRC_BIT, "__mlx_pixel_put_pipeline_texture");
_buffer.mapMem(&_buffer_map);
_cpu_map = std::vector<std::uint32_t>(height * width + 1, 0);
_width = width;
_height = height;
}
void PixelPutPipeline::setPixel(int x, int y, std::uint32_t color) noexcept
{
MLX_PROFILE_FUNCTION();
if(x < 0 || y < 0 || x > static_cast<int>(_width) || y > static_cast<int>(_height))
return;
_cpu_map[(y * _width) + x] = color;
_has_been_modified = true;
}
void PixelPutPipeline::clear()
{
MLX_PROFILE_FUNCTION();
_cpu_map.assign(_width * _height, 0);
_has_been_modified = true;
}
void PixelPutPipeline::render(std::array<VkDescriptorSet, 2>& sets, Renderer& renderer) noexcept
{
MLX_PROFILE_FUNCTION();
if(_has_been_modified)
{
std::memcpy(_buffer_map, _cpu_map.data(), sizeof(std::uint32_t) * _cpu_map.size());
_texture.copyFromBuffer(_buffer);
_has_been_modified = false;
}
_texture.updateSet(0);
_texture.render(sets, renderer, 0, 0);
}
void PixelPutPipeline::destroy() noexcept
{
MLX_PROFILE_FUNCTION();
_buffer.destroy();
_texture.destroy();
}
PixelPutPipeline::~PixelPutPipeline() {}
}

192
runtime/Sources/Renderer/Renderer.cpp git.filemode.normal_file
View File

@@ -0,0 +1,192 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* renderer.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/12/18 17:25:16 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:03:49 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <renderer/renderer.h>
#include <renderer/images/texture.h>
#include <renderer/core/render_core.h>
#include <core/profiler.h>
namespace mlx
{
void Renderer::init(Texture* render_target)
{
MLX_PROFILE_FUNCTION();
if(render_target == nullptr)
{
_surface.create(*this);
_swapchain.init(this);
_pass.init(_swapchain.getImagesFormat(), VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
for(std::size_t i = 0; i < _swapchain.getImagesNumber(); i++)
_framebuffers.emplace_back().init(_pass, _swapchain.getImage(i));
}
else
{
_render_target = render_target;
_render_target->transitionLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
_pass.init(_render_target->getFormat(), _render_target->getLayout());
_framebuffers.emplace_back().init(_pass, *static_cast<Image*>(_render_target));
}
_cmd.init();
for(std::size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
_semaphores[i].init();
_uniform_buffer.reset(new UBO);
#ifdef DEBUG
_uniform_buffer->create(this, sizeof(glm::mat4), "__mlx_matrices_uniform_buffer_");
#else
_uniform_buffer->create(this, sizeof(glm::mat4), nullptr);
#endif
_vert_layout.init({
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER}
}, VK_SHADER_STAGE_VERTEX_BIT);
_frag_layout.init({
{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}
}, VK_SHADER_STAGE_FRAGMENT_BIT);
_vert_set.init(this, &Render_Core::get().getDescriptorPool(), &_vert_layout);
_frag_set.init(this, &Render_Core::get().getDescriptorPool(), &_frag_layout);
_vert_set.writeDescriptor(0, _uniform_buffer.get());
_pipeline.init(*this);
_framebuffer_resized = false;
}
bool Renderer::beginFrame()
{
MLX_PROFILE_FUNCTION();
auto device = Render_Core::get().getDevice().get();
if(_render_target == nullptr)
{
_cmd.getCmdBuffer(_current_frame_index).waitForExecution();
VkResult result = vkAcquireNextImageKHR(device, _swapchain(), UINT64_MAX, _semaphores[_current_frame_index].getImageSemaphore(), VK_NULL_HANDLE, &_image_index);
if(result == VK_ERROR_OUT_OF_DATE_KHR)
{
recreateRenderData();
return false;
}
else if(result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
core::error::report(e_kind::fatal_error, "Vulkan error : failed to acquire swapchain image");
}
else
{
_image_index = 0;
if(_render_target->getLayout() != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
_render_target->transitionLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
}
_cmd.getCmdBuffer(_current_frame_index).reset();
_cmd.getCmdBuffer(_current_frame_index).beginRecord();
auto& fb = _framebuffers[_image_index];
_pass.begin(getActiveCmdBuffer(), fb);
_pipeline.bindPipeline(_cmd.getCmdBuffer(_current_frame_index));
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(fb.getWidth());
viewport.height = static_cast<float>(fb.getHeight());
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(_cmd.getCmdBuffer(_current_frame_index).get(), 0, 1, &viewport);
VkRect2D scissor{};
scissor.offset = { 0, 0 };
scissor.extent = { fb.getWidth(), fb.getHeight()};
vkCmdSetScissor(_cmd.getCmdBuffer(_current_frame_index).get(), 0, 1, &scissor);
return true;
}
void Renderer::endFrame()
{
MLX_PROFILE_FUNCTION();
_pass.end(getActiveCmdBuffer());
_cmd.getCmdBuffer(_current_frame_index).endRecord();
if(_render_target == nullptr)
{
_cmd.getCmdBuffer(_current_frame_index).submit(&_semaphores[_current_frame_index]);
VkSwapchainKHR swapchain = _swapchain();
VkSemaphore signalSemaphores[] = { _semaphores[_current_frame_index].getRenderImageSemaphore() };
VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &swapchain;
presentInfo.pImageIndices = &_image_index;
VkResult result = vkQueuePresentKHR(Render_Core::get().getQueue().getPresent(), &presentInfo);
if(result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || _framebuffer_resized)
{
_framebuffer_resized = false;
recreateRenderData();
}
else if(result != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan error : failed to present swap chain image");
_current_frame_index = (_current_frame_index + 1) % MAX_FRAMES_IN_FLIGHT;
}
else
{
_cmd.getCmdBuffer(_current_frame_index).submitIdle(true);
_current_frame_index = 0;
}
}
void Renderer::recreateRenderData()
{
_swapchain.recreate();
_pass.destroy();
_pass.init(_swapchain.getImagesFormat(), VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
for(auto& fb : _framebuffers)
fb.destroy();
_framebuffers.clear();
for(std::size_t i = 0; i < _swapchain.getImagesNumber(); i++)
_framebuffers.emplace_back().init(_pass, _swapchain.getImage(i));
}
void Renderer::destroy()
{
MLX_PROFILE_FUNCTION();
vkDeviceWaitIdle(Render_Core::get().getDevice().get());
_pipeline.destroy();
_uniform_buffer->destroy();
_vert_layout.destroy();
_frag_layout.destroy();
_frag_set.destroy();
_vert_set.destroy();
_cmd.destroy();
_pass.destroy();
if(_render_target == nullptr)
{
_swapchain.destroy();
_surface.destroy();
}
for(auto& fb : _framebuffers)
fb.destroy();
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
_semaphores[i].destroy();
}
}

View File

@@ -0,0 +1,53 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_framebuffer.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/06 18:18:06 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:03:35 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <renderer/core/render_core.h>
#include <renderer/renderer.h>
#include <renderer/images/vk_image.h>
namespace mlx
{
void FrameBuffer::init(RenderPass& renderpass, Image& image)
{
VkImageView attachments[] = { image.getImageView() };
_width = image.getWidth();
_height = image.getHeight();
VkFramebufferCreateInfo framebufferInfo{};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = renderpass.get();
framebufferInfo.attachmentCount = 1;
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = _width;
framebufferInfo.height = _height;
framebufferInfo.layers = 1;
VkResult res = vkCreateFramebuffer(Render_Core::get().getDevice().get(), &framebufferInfo, nullptr, &_framebuffer);
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create a framebuffer, %s", RCore::verbaliseResultVk(res));
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : created new framebuffer");
#endif
}
void FrameBuffer::destroy() noexcept
{
vkDestroyFramebuffer(Render_Core::get().getDevice().get(), _framebuffer, nullptr);
_framebuffer = VK_NULL_HANDLE;
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : destroyed a framebuffer");
#endif
}
}

View File

@@ -0,0 +1,121 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_render_pass.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/06 18:21:36 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:03:37 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include "vk_render_pass.h"
#include <renderer/core/render_core.h>
#include <renderer/renderer.h>
#include <renderer/renderpass/vk_framebuffer.h>
#include <core/profiler.h>
namespace mlx
{
static const VkClearValue clearColor = {{{ 0.f, 0.f, 0.f, 1.0f }}}; // wtf, this mess to satisfy a warning
void RenderPass::init(VkFormat attachement_format, VkImageLayout layout)
{
VkAttachmentDescription colorAttachment{};
colorAttachment.format = attachement_format;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = layout;
VkAttachmentReference colorAttachmentRef{};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = (layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : layout);
VkSubpassDescription subpass1{};
subpass1.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass1.colorAttachmentCount = 1;
subpass1.pColorAttachments = &colorAttachmentRef;
VkSubpassDescription subpasses[] = { subpass1 };
std::vector<VkSubpassDependency> subpassesDeps;
subpassesDeps.emplace_back();
subpassesDeps.back().srcSubpass = VK_SUBPASS_EXTERNAL;
subpassesDeps.back().dstSubpass = 0;
subpassesDeps.back().srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
subpassesDeps.back().dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
subpassesDeps.back().srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
subpassesDeps.back().dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
subpassesDeps.back().dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
subpassesDeps.emplace_back();
subpassesDeps.back().srcSubpass = 0;
subpassesDeps.back().dstSubpass = VK_SUBPASS_EXTERNAL;
subpassesDeps.back().srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
subpassesDeps.back().dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
subpassesDeps.back().srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
subpassesDeps.back().dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
subpassesDeps.back().dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
VkRenderPassCreateInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = sizeof(subpasses) / sizeof(VkSubpassDescription);
renderPassInfo.pSubpasses = subpasses;
renderPassInfo.dependencyCount = static_cast<std::uint32_t>(subpassesDeps.size());
renderPassInfo.pDependencies = subpassesDeps.data();
VkResult res = vkCreateRenderPass(Render_Core::get().getDevice().get(), &renderPassInfo, nullptr, &_render_pass);
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create render pass, %s", RCore::verbaliseResultVk(res));
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : created new render pass");
#endif
}
void RenderPass::begin(class CmdBuffer& cmd, class FrameBuffer& fb)
{
MLX_PROFILE_FUNCTION();
if(_is_running)
return;
VkRenderPassBeginInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = _render_pass;
renderPassInfo.framebuffer = fb.get();
renderPassInfo.renderArea.offset = { 0, 0 };
renderPassInfo.renderArea.extent = { fb.getWidth(), fb.getHeight() };
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass(cmd.get(), &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
_is_running = true;
}
void RenderPass::end(class CmdBuffer& cmd)
{
MLX_PROFILE_FUNCTION();
if(!_is_running)
return;
vkCmdEndRenderPass(cmd.get());
_is_running = false;
}
void RenderPass::destroy() noexcept
{
vkDestroyRenderPass(Render_Core::get().getDevice().get(), _render_pass, nullptr);
_render_pass = VK_NULL_HANDLE;
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : destroyed a renderpass");
#endif
}
}

View File

@@ -0,0 +1,152 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* vk_swapchain.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/10/06 18:22:28 by maldavid #+# #+# */
/* Updated: 2024/03/25 23:09:33 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <renderer/core/render_core.h>
#include <renderer/renderer.h>
#include <platform/window.h>
namespace mlx
{
void SwapChain::init(Renderer* renderer)
{
VkDevice device = Render_Core::get().getDevice().get();
_renderer = renderer;
_swapchain_support = querySwapChainSupport(Render_Core::get().getDevice().getPhysicalDevice());
VkSurfaceFormatKHR surfaceFormat = renderer->getSurface().chooseSwapSurfaceFormat(_swapchain_support.formats);
VkPresentModeKHR presentMode = chooseSwapPresentMode(_swapchain_support.present_modes);
_extent = chooseSwapExtent(_swapchain_support.capabilities);
std::uint32_t imageCount = _swapchain_support.capabilities.minImageCount + 1;
if(_swapchain_support.capabilities.maxImageCount > 0 && imageCount > _swapchain_support.capabilities.maxImageCount)
imageCount = _swapchain_support.capabilities.maxImageCount;
Queues::QueueFamilyIndices indices = Render_Core::get().getQueue().findQueueFamilies(Render_Core::get().getDevice().getPhysicalDevice());
std::uint32_t queueFamilyIndices[] = { indices.graphics_family.value(), indices.present_family.value() };
VkSwapchainCreateInfoKHR createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = renderer->getSurface().get();
createInfo.minImageCount = imageCount;
createInfo.imageFormat = surfaceFormat.format;
createInfo.imageColorSpace = surfaceFormat.colorSpace;
createInfo.imageExtent = _extent;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
createInfo.preTransform = _swapchain_support.capabilities.currentTransform;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = presentMode;
createInfo.clipped = VK_TRUE;
createInfo.oldSwapchain = VK_NULL_HANDLE;
if(indices.graphics_family != indices.present_family)
{
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
createInfo.queueFamilyIndexCount = 2;
createInfo.pQueueFamilyIndices = queueFamilyIndices;
}
else
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
VkResult res = vkCreateSwapchainKHR(device, &createInfo, nullptr, &_swapchain);
if(res != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : failed to create the swapchain, %s", RCore::verbaliseResultVk(res));
std::vector<VkImage> tmp;
vkGetSwapchainImagesKHR(device, _swapchain, &imageCount, nullptr);
_images.resize(imageCount);
tmp.resize(imageCount);
vkGetSwapchainImagesKHR(device, _swapchain, &imageCount, tmp.data());
for(std::size_t i = 0; i < imageCount; i++)
{
_images[i].create(tmp[i], surfaceFormat.format, _extent.width, _extent.height);
_images[i].transitionLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
_images[i].createImageView(VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_COLOR_BIT);
}
_swapchain_image_format = surfaceFormat.format;
#ifdef DEBUG
core::error::report(e_kind::message, "Vulkan : created new swapchain");
#endif
}
SwapChain::SwapChainSupportDetails SwapChain::querySwapChainSupport(VkPhysicalDevice device)
{
SwapChain::SwapChainSupportDetails details;
VkSurfaceKHR surface = _renderer->getSurface().get();
if(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities) != VK_SUCCESS)
core::error::report(e_kind::fatal_error, "Vulkan : unable to retrieve surface capabilities");
std::uint32_t formatCount = 0;
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
if(formatCount != 0)
{
details.formats.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
}
std::uint32_t presentModeCount;
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
if(presentModeCount != 0)
{
details.present_modes.resize(presentModeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.present_modes.data());
}
return details;
}
VkPresentModeKHR SwapChain::chooseSwapPresentMode([[maybe_unused]] const std::vector<VkPresentModeKHR>& availablePresentModes)
{
// in the future, you may choose to activate vsync or not
return VK_PRESENT_MODE_IMMEDIATE_KHR;
}
VkExtent2D SwapChain::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities)
{
if(capabilities.currentExtent.width != std::numeric_limits<std::uint32_t>::max())
return capabilities.currentExtent;
int width, height;
glfwGetFramebufferSize(_renderer->getWindow()->getNativeWindow(), &width, &height);
VkExtent2D actualExtent = { static_cast<std::uint32_t>(width), static_cast<std::uint32_t>(height) };
actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
return actualExtent;
}
void SwapChain::recreate()
{
destroy();
init(_renderer);
}
void SwapChain::destroy() noexcept
{
if(_swapchain == VK_NULL_HANDLE)
return;
vkDeviceWaitIdle(Render_Core::get().getDevice().get());
vkDestroySwapchainKHR(Render_Core::get().getDevice().get(), _swapchain, nullptr);
_swapchain = VK_NULL_HANDLE;
for(Image& img : _images)
img.destroyImageView();
}
}

89
runtime/Sources/Renderer/Texts/Font.cpp git.filemode.normal_file
View File

@@ -0,0 +1,89 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* font.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/12/11 22:06:09 by kbz_8 #+# #+# */
/* Updated: 2024/03/25 19:03:54 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <renderer/texts/font.h>
#include <renderer/renderer.h>
#include <core/profiler.h>
constexpr const int RANGE = 1024;
namespace mlx
{
Font::Font(Renderer& renderer, const std::filesystem::path& path, float scale) : _name(path.string()), _renderer(renderer), _scale(scale)
{
_build_data = path;
}
Font::Font(class Renderer& renderer, const std::string& name, const std::vector<std::uint8_t>& ttf_data, float scale) : _name(name), _renderer(renderer), _scale(scale)
{
_build_data = ttf_data;
}
void Font::buildFont()
{
MLX_PROFILE_FUNCTION();
std::vector<std::uint8_t> file_bytes;
if(std::holds_alternative<std::filesystem::path>(_build_data))
{
std::ifstream file(std::get<std::filesystem::path>(_build_data), std::ios::binary);
if(!file.is_open())
{
core::error::report(e_kind::error, "Font load : cannot open font file, %s", _name.c_str());
return;
}
std::ifstream::pos_type fileSize = std::filesystem::file_size(std::get<std::filesystem::path>(_build_data));
file.seekg(0, std::ios::beg);
file_bytes.resize(fileSize);
file.read(reinterpret_cast<char*>(file_bytes.data()), fileSize);
file.close();
}
std::vector<std::uint8_t> tmp_bitmap(RANGE * RANGE);
std::vector<std::uint8_t> vulkan_bitmap(RANGE * RANGE * 4);
stbtt_pack_context pc;
stbtt_PackBegin(&pc, tmp_bitmap.data(), RANGE, RANGE, RANGE, 1, nullptr);
if(std::holds_alternative<std::filesystem::path>(_build_data))
stbtt_PackFontRange(&pc, file_bytes.data(), 0, _scale, 32, 96, _cdata.data());
else
stbtt_PackFontRange(&pc, std::get<std::vector<std::uint8_t>>(_build_data).data(), 0, _scale, 32, 96, _cdata.data());
stbtt_PackEnd(&pc);
for(int i = 0, j = 0; i < RANGE * RANGE; i++, j += 4)
{
vulkan_bitmap[j + 0] = tmp_bitmap[i];
vulkan_bitmap[j + 1] = tmp_bitmap[i];
vulkan_bitmap[j + 2] = tmp_bitmap[i];
vulkan_bitmap[j + 3] = tmp_bitmap[i];
}
#ifdef DEBUG
_atlas.create(vulkan_bitmap.data(), RANGE, RANGE, VK_FORMAT_R8G8B8A8_UNORM, std::string(_name + "_font_altas").c_str(), true);
#else
_atlas.create(vulkan_bitmap.data(), RANGE, RANGE, VK_FORMAT_R8G8B8A8_UNORM, nullptr, true);
#endif
_atlas.setDescriptor(_renderer.getFragDescriptorSet().duplicate());
_is_init = true;
}
void Font::destroy()
{
MLX_PROFILE_FUNCTION();
_atlas.destroy();
_is_init = false;
}
Font::~Font()
{
if(_is_init)
destroy();
}
}

View File

@@ -0,0 +1,70 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* font_library.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/01/18 09:28:14 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:03:57 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <renderer/texts/font_library.h>
#include <renderer/texts/font.h>
#include <core/errors.h>
#include <renderer/renderer.h>
#include <core/profiler.h>
namespace mlx
{
std::shared_ptr<Font> FontLibrary::getFontData(FontID id)
{
MLX_PROFILE_FUNCTION();
if(!_cache.count(id) || std::find(_invalid_ids.begin(), _invalid_ids.end(), id) != _invalid_ids.end())
core::error::report(e_kind::fatal_error, "Font Library : wrong font ID '%d'", id);
return _cache[id];
}
FontID FontLibrary::addFontToLibrary(std::shared_ptr<Font> font)
{
MLX_PROFILE_FUNCTION();
auto it = std::find_if(_cache.begin(), _cache.end(), [&](const std::pair<FontID, std::shared_ptr<Font>>& v)
{
return v.second->getScale() == font->getScale() &&
v.second->getName() == font->getName() &&
std::find(_invalid_ids.begin(), _invalid_ids.end(), v.first) == _invalid_ids.end();
});
if(it != _cache.end())
return it->first;
font->buildFont();
_cache[_current_id] = font;
_current_id++;
return _current_id - 1;
}
void FontLibrary::removeFontFromLibrary(FontID id)
{
MLX_PROFILE_FUNCTION();
if(!_cache.count(id) || std::find(_invalid_ids.begin(), _invalid_ids.end(), id) != _invalid_ids.end())
{
core::error::report(e_kind::warning, "Font Library : trying to remove a font with an unkown or invalid ID '%d'", id);
return;
}
_cache[id]->destroy();
_invalid_ids.push_back(id);
}
void FontLibrary::clearLibrary()
{
MLX_PROFILE_FUNCTION();
for(auto& [id, font] : _cache)
{
font->destroy();
_invalid_ids.push_back(id);
}
// do not `_cache.clear();` as it releases the fonts and may not destroy the texture atlas that is in use by command buffers
}
}

79
runtime/Sources/Renderer/Texts/Text.cpp git.filemode.normal_file
View File

@@ -0,0 +1,79 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* text.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/01/11 00:11:56 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:04:01 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <core/profiler.h>
#include <renderer/texts/text.h>
#include <renderer/renderer.h>
namespace mlx
{
void Text::init(std::string text, FontID font, std::uint32_t color, std::vector<Vertex> vbo_data, std::vector<std::uint16_t> ibo_data)
{
MLX_PROFILE_FUNCTION();
if(_is_init)
return;
_text = std::move(text);
_color = color;
_font = font;
#ifdef DEBUG
std::string debug_name = _text;
for(char& c : debug_name)
{
if(c == ' ' || c == '"' || c == '\'')
c = '_';
}
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
_vbo[i].create(sizeof(Vertex) * vbo_data.size(), static_cast<const void*>(vbo_data.data()), debug_name.c_str());
_ibo.create(sizeof(std::uint16_t) * ibo_data.size(), ibo_data.data(), debug_name.c_str());
#else
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
_vbo[i].create(sizeof(Vertex) * vbo_data.size(), static_cast<const void*>(vbo_data.data()), nullptr);
_ibo.create(sizeof(std::uint16_t) * ibo_data.size(), ibo_data.data(), nullptr);
#endif
_is_init = true;
}
void Text::bind(Renderer& renderer) noexcept
{
MLX_PROFILE_FUNCTION();
if(!_is_init)
return;
_vbo[renderer.getActiveImageIndex()].bind(renderer);
_ibo.bind(renderer);
}
void Text::updateVertexData(int frame, std::vector<Vertex> vbo_data)
{
MLX_PROFILE_FUNCTION();
if(!_is_init)
return;
_vbo[frame].setData(sizeof(Vertex) * vbo_data.size(), static_cast<const void*>(vbo_data.data()));
}
void Text::destroy() noexcept
{
MLX_PROFILE_FUNCTION();
if(!_is_init)
return;
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
_vbo[i].destroy();
_ibo.destroy();
_is_init = false;
}
Text::~Text()
{
destroy();
}
}

View File

@@ -0,0 +1,112 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* text_descriptor.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/01/11 00:23:11 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:04:52 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <core/profiler.h>
#include <renderer/texts/text_descriptor.h>
#include <renderer/images/texture_atlas.h>
#include <renderer/texts/font.h>
#include <renderer/texts/text.h>
#define STB_RECT_PACK_IMPLEMENTATION
#include <stb_rect_pack.h>
#include <core/memory.h>
#define STB_TRUETYPE_IMPLEMENTATION
#define STB_malloc(x, u) ((void)(u), MemManager::malloc(x))
#define STB_free(x, u) ((void)(u), MemManager::free(x))
#include <stb_truetype.h>
constexpr const int RANGE = 1024;
namespace mlx
{
TextDrawDescriptor::TextDrawDescriptor(std::string text, std::uint32_t _color, int _x, int _y) : color(_color), x(_x), y(_y), _text(std::move(text))
{}
void TextDrawDescriptor::init(FontID font) noexcept
{
MLX_PROFILE_FUNCTION();
std::vector<Vertex> vertexData;
std::vector<std::uint16_t> indexData;
float stb_x = 0.0f;
float stb_y = 0.0f;
{
std::shared_ptr<Font> font_data = FontLibrary::get().getFontData(font);
for(char c : _text)
{
if(c < 32)
continue;
stbtt_aligned_quad q;
stbtt_GetPackedQuad(font_data->getCharData().data(), RANGE, RANGE, c - 32, &stb_x, &stb_y, &q, 1);
std::size_t index = vertexData.size();
glm::vec4 vertex_color = {
static_cast<float>((color & 0x000000FF)) / 255.f,
static_cast<float>((color & 0x0000FF00) >> 8) / 255.f,
static_cast<float>((color & 0x00FF0000) >> 16) / 255.f,
static_cast<float>((color & 0xFF000000) >> 24) / 255.f
};
vertexData.emplace_back(glm::vec2{q.x0, q.y0}, vertex_color, glm::vec2{q.s0, q.t0});
vertexData.emplace_back(glm::vec2{q.x1, q.y0}, vertex_color, glm::vec2{q.s1, q.t0});
vertexData.emplace_back(glm::vec2{q.x1, q.y1}, vertex_color, glm::vec2{q.s1, q.t1});
vertexData.emplace_back(glm::vec2{q.x0, q.y1}, vertex_color, glm::vec2{q.s0, q.t1});
indexData.emplace_back(index + 0);
indexData.emplace_back(index + 1);
indexData.emplace_back(index + 2);
indexData.emplace_back(index + 2);
indexData.emplace_back(index + 3);
indexData.emplace_back(index + 0);
}
}
std::shared_ptr<Text> text_data = std::make_shared<Text>();
text_data->init(_text, font, color, std::move(vertexData), std::move(indexData));
id = TextLibrary::get().addTextToLibrary(text_data);
#ifdef DEBUG
core::error::report(e_kind::message, "Text put : registered new text to render");
#endif
}
void TextDrawDescriptor::render(std::array<VkDescriptorSet, 2>& sets, Renderer& renderer)
{
MLX_PROFILE_FUNCTION();
std::shared_ptr<Text> draw_data = TextLibrary::get().getTextData(id);
std::shared_ptr<Font> font_data = FontLibrary::get().getFontData(draw_data->getFontInUse());
TextureAtlas& atlas = const_cast<TextureAtlas&>(font_data->getAtlas());
draw_data->bind(renderer);
if(!atlas.getSet().isInit())
atlas.setDescriptor(renderer.getFragDescriptorSet().duplicate());
if(!atlas.hasBeenUpdated())
atlas.updateSet(0);
sets[1] = const_cast<TextureAtlas&>(atlas).getVkSet();
vkCmdBindDescriptorSets(renderer.getActiveCmdBuffer().get(), VK_PIPELINE_BIND_POINT_GRAPHICS, renderer.getPipeline().getPipelineLayout(), 0, sets.size(), sets.data(), 0, nullptr);
atlas.render(renderer, x, y, draw_data->getIBOsize());
}
void TextDrawDescriptor::resetUpdate()
{
std::shared_ptr<Text> draw_data = TextLibrary::get().getTextData(id);
std::shared_ptr<Font> font_data = FontLibrary::get().getFontData(draw_data->getFontInUse());
TextureAtlas& atlas = const_cast<TextureAtlas&>(font_data->getAtlas());
atlas.resetUpdate();
}
}

View File

@@ -0,0 +1,64 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* text_library.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/04/10 11:59:57 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:05:09 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <renderer/texts/text_library.h>
#include <renderer/texts/text.h>
#include <core/errors.h>
#include <renderer/renderer.h>
#include <core/profiler.h>
namespace mlx
{
std::shared_ptr<Text> TextLibrary::getTextData(TextID id)
{
MLX_PROFILE_FUNCTION();
if(!_cache.count(id))
core::error::report(e_kind::fatal_error, "Text Library : wrong text ID '%d'", id);
return _cache[id];
}
TextID TextLibrary::addTextToLibrary(std::shared_ptr<Text> text)
{
MLX_PROFILE_FUNCTION();
auto it = std::find_if(_cache.begin(), _cache.end(), [&](const std::pair<TextID, std::shared_ptr<Text>>& v)
{
return v.second->getText() == text->getText() && v.second->getColor() == text->getColor();
});
if(it != _cache.end())
return it->first;
_cache[_current_id] = text;
_current_id++;
return _current_id - 1;
}
void TextLibrary::removeTextFromLibrary(TextID id)
{
MLX_PROFILE_FUNCTION();
if(!_cache.count(id))
{
core::error::report(e_kind::warning, "Text Library : trying to remove a text with an unkown or invalid ID '%d'", id);
return;
}
_cache[id]->destroy();
_cache.erase(id);
}
void TextLibrary::clearLibrary()
{
MLX_PROFILE_FUNCTION();
for(auto& [id, text] : _cache)
text->destroy();
_cache.clear();
}
}

View File

@@ -0,0 +1,68 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* text_manager.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/04/06 16:41:13 by maldavid #+# #+# */
/* Updated: 2024/03/25 19:05:13 by maldavid ### ########.fr */
/* */
/* ************************************************************************** */
#include <pre_compiled.h>
#include <renderer/texts/text_descriptor.h>
#include <renderer/texts/text_library.h>
#include <renderer/texts/text.h>
#include <renderer/texts/text_manager.h>
#include <core/profiler.h>
#include <utils/dogica_ttf.h>
namespace mlx
{
void TextManager::init(Renderer& renderer) noexcept
{
MLX_PROFILE_FUNCTION();
loadFont(renderer, "default", 6.f);
}
void TextManager::loadFont(Renderer& renderer, const std::filesystem::path& filepath, float scale)
{
MLX_PROFILE_FUNCTION();
std::shared_ptr<Font> font;
if(filepath.string() == "default")
font = std::make_shared<Font>(renderer, "default", dogica_ttf, scale);
else
font = std::make_shared<Font>(renderer, filepath, scale);
_font_in_use = FontLibrary::get().addFontToLibrary(font);
}
std::pair<DrawableResource*, bool> TextManager::registerText(int x, int y, std::uint32_t color, std::string str)
{
MLX_PROFILE_FUNCTION();
auto res = _text_descriptors.emplace(std::move(str), color, x, y);
if(res.second)
{
const_cast<TextDrawDescriptor&>(*res.first).init(_font_in_use);
return std::make_pair(static_cast<DrawableResource*>(&const_cast<TextDrawDescriptor&>(*res.first)), true);
}
auto text_ptr = TextLibrary::get().getTextData(res.first->id);
if(_font_in_use != text_ptr->getFontInUse())
{
// TODO : update text vertex buffers rather than destroying it and recreating it
TextLibrary::get().removeTextFromLibrary(res.first->id);
const_cast<TextDrawDescriptor&>(*res.first).init(_font_in_use);
}
return std::make_pair(static_cast<DrawableResource*>(&const_cast<TextDrawDescriptor&>(*res.first)), false);
}
void TextManager::destroy() noexcept
{
MLX_PROFILE_FUNCTION();
_text_descriptors.clear();
}
}