Added a rectangle, and started reworking logs

This commit is contained in:
Alexandre 2026-08-18 03:13:38 +02:00
parent 2603d12394
commit 9236ada59a
7 changed files with 823 additions and 76 deletions

View File

@ -128,7 +128,7 @@ function ( addBinary BINARY_NAME )
${CMAKE_SOURCE_DIR}/src/imgui/imgui_tables.cpp
${CMAKE_SOURCE_DIR}/src/imgui/imgui_widgets.cpp
${CMAKE_SOURCE_DIR}/src/imgui/imgui_demo.cpp
${CMAKE_SOURCE_DIR}/src/imgui/imgui_memory_editor.h
#${CMAKE_SOURCE_DIR}/src/imgui/imgui_memory_editor.h
${CMAKE_SOURCE_DIR}/src/imgui/backends/imgui_impl_glfw.cpp
${CMAKE_SOURCE_DIR}/src/imgui/backends/imgui_impl_vulkan.cpp

View File

@ -0,0 +1,36 @@
#version 450
#extension GL_EXT_buffer_reference : require
layout(location = 0) out vec3 outColor;
layout(location = 1) out vec2 outUV;
struct Vertex {
vec3 position;
float uv_x;
vec3 normal;
float uv_y;
vec4 color;
};
layout(buffer_reference, std430) readonly buffer VertexBuffer {
Vertex vertices[];
};
//push constants block
layout(push_constant) uniform constants
{
mat4 render_matrix;
VertexBuffer vertexBuffer;
} PushConstants;
void main()
{
//load vertex data from device adress
Vertex v = PushConstants.vertexBuffer.vertices[gl_VertexIndex];
//output data
gl_Position = PushConstants.render_matrix * vec4(v.position, 1.0f);
outColor = v.color.xyz;
outUV.x = v.uv_x;
outUV.y = v.uv_y;
}

View File

@ -3,6 +3,24 @@
// Custom includes
// Enums
enum class LogModule {
ENGINE,
RENDERER,
GLFW,
CLEANUP
};
enum class LogLevel {
EDEBUG,
EINIT, // Initialization related
EINFO, // General infos
EWARN,
EERROR,
EFATAL
};
class NetLogger {
public:
struct Server{
@ -17,7 +35,65 @@ class NetLogger {
void logNetAsync(); // Todo
private:
};
class Logger {
public:
// Members
std::string logBuffer; // Log is empty by default
std::vector<std::string_view> lines;
// Functions
template<typename... Args>
void logDisk(
LogModule module,
LogLevel level,
std::string_view message,
Args&&... args
); // Logs to a file on disk
// Overload
void logDisk(std::string_view message);
template<typename... Args>
void logRAM(
LogModule module,
LogLevel level,
std::string_view message,
Args&&... args
); // Logs to ram (ImGUI)
template<typename... Args>
void logConsole(
LogModule module,
LogLevel level,
std::string_view message,
Args&&... args
); // Logs to the console (crashes, etc...)
template<typename... Args>
void logAll(
LogModule module,
LogLevel level,
std::string_view message,
Args&&... args
); // Logs using all of the above, RAM then console and finally disk, maybe multithread (?)
template<typename... Args>
void logError(
LogModule module,
LogLevel level,
std::string_view message,
Args&&... args
); // Logs to console and file, in cases of crashes or errors
void merge(std::string_view source); // Merges a source dumped logs into this
void clear();
private:
// Helper functions
static std::string_view moduleToString(LogModule m);
static std::string_view levelToString(LogLevel l);
};
#endif // End of header
@ -29,12 +105,229 @@ class NetLogger {
// Implementation
// Includes
#include <httplib.h> // Todo : add it in the flake
//#include <httplib.h> // Todo : add it in the flake
#include <iostream>
#include <fstream>
#include <chrono>
/*
void NetLogger::initNetLogger(Server server) {
httplib::Client cli(server.url)
// Todo : add error handling
}
*/
template<typename... Args>
void Logger::logRAM(
LogModule module,
LogLevel level,
std::string_view message,
Args&&... args
) {
auto now = std::chrono::system_clock::now();
auto timestamp = std::format("{:%Y-%m-%d %H:%M}", now);
auto start = logBuffer.size();
fmt::format_to(
std::back_inserter(logBuffer),
"[{}] ",
timestamp
);
fmt::format_to(
std::back_inserter(logBuffer),
"[{}:{}] ",
moduleToString(module),
levelToString(level)
);
fmt::format_to(
std::back_inserter(logBuffer),
fmt::runtime(message),
std::forward<Args>(args)...
);
logBuffer.push_back('\n');
auto end = logBuffer.size();
lines.emplace_back(logBuffer.data() + start, end - start);
}
template<typename... Args>
void Logger::logConsole(
LogModule module,
LogLevel level,
std::string_view message,
Args&&... args
) {
std::string tempBuffer;
auto now = std::chrono::system_clock::now();
auto timestamp = std::format("{:%Y-%m-%d %H:%M}", now);
fmt::format_to(
std::back_inserter(tempBuffer),
"[{}] ",
timestamp
);
fmt::format_to(
std::back_inserter(tempBuffer),
"[{}:{}] ",
moduleToString(module),
levelToString(level)
);
fmt::format_to(
std::back_inserter(tempBuffer),
fmt::runtime(message),
std::forward<Args>(args)...
);
std::cout.write(tempBuffer.data(), tempBuffer.size());
std::cout.put('\n');
}
template<typename... Args>
void Logger::logDisk(
LogModule module,
LogLevel level,
std::string_view message,
Args&&... args
) {
// Open the logfile
std::ofstream logFile ("log.txt", std::ios::out | std::ios::app);
if (!logFile.good()) {
throw std::runtime_error("Failed to open logfile !");
}
// Create a temporary buffer
std::string tempBuffer;
auto now = std::chrono::system_clock::now();
auto timestamp = std::format("{:%Y-%m-%d %H:%M}", now);
fmt::format_to(
std::back_inserter(tempBuffer),
"[{}] ",
timestamp
);
fmt::format_to(
std::back_inserter(tempBuffer),
"[{}:{}] ",
moduleToString(module),
levelToString(level)
);
fmt::format_to(
std::back_inserter(tempBuffer),
fmt::runtime(message),
std::forward<Args>(args)...
);
logFile << tempBuffer << '\n';
logFile.close();
}
// Overload to inject a message
void Logger::logDisk(std::string_view message) {
std::ofstream logFile ("log.txt", std::ios::out | std::ios::app);
if (!logFile.good()) {
throw std::runtime_error("Failed to open logfile !");
}
logFile << message << '\n';
logFile.close();
}
template<typename... Args>
void Logger::logAll(
LogModule module,
LogLevel level,
std::string_view message,
Args&&... args
) {
// Logs to all targets !
logRAM(
module,
level,
message,
args...
);
logConsole(
module,
level,
message,
args...
);
logDisk(
module,
level,
message,
args...
);
}
template<typename... Args>
void Logger::logError(
LogModule module,
LogLevel level,
std::string_view message,
Args&&... args
) {
logDisk(
module,
level,
message,
args...
);
logConsole(
module,
level,
message,
args...
);
}
void Logger::merge(std::string_view source) {
/*
This merges a dumped logger into this one
Assumes the other logger is already formated.
This allows to add arbitrary strings into the logs
*/
logBuffer += source;
}
void Logger::clear() {
logBuffer.clear();
}
std::string_view Logger::moduleToString(LogModule m) {
using enum LogModule;
switch (m) {
case ENGINE: return "Engine";
case RENDERER: return "Renderer";
case GLFW: return "GLFW";
case CLEANUP: return "CLEANUP";
default: return "Unkown";
}
}
std::string_view Logger::levelToString(LogLevel l) {
using enum LogLevel;
switch (l) {
case EDEBUG: return "Debug";
case EINFO: return "Info";
case EINIT: return "Init";
case EWARN: return "Warn";
case EERROR: return "Error";
case EFATAL: return "Fatal";
default: return "???";
}
}
#endif
#endif

View File

@ -74,6 +74,35 @@ struct AllocatedImage {
VkFormat imageFormat;
};
struct AllocatedBuffer {
VkBuffer buffer;
VmaAllocation allocation;
VmaAllocationInfo info;
};
struct Vertex {
alignas(16) glm::vec3 position;
float uv_x;
alignas(16) glm::vec3 normal;
float uv_y;
glm::vec4 color;
};
// Holds the ressouces required for a mesh
struct GPUMeshBuffers {
AllocatedBuffer indexBuffer;
AllocatedBuffer vertexBuffer;
VkDeviceAddress vertexBufferAddress;
};
struct GPUDrawPushConstants {
glm::mat4 worldMatrix;
VkDeviceAddress vertexBuffer;
};
// "Global" ? (I hope so) logger
Logger logger;
#endif

View File

@ -17,7 +17,7 @@ class Callback {
void Callback::keyboardCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
#ifdef DEBUG
fmt::println("[Engine:Info] Key pressed, key {}", key);
logger.logRAM(LogModule::GLFW, LogLevel::EINFO, "Key pressed, key {}", key);
#endif
}

View File

@ -46,7 +46,7 @@ class Engine {
//========== Class Members ==========
bool _isInitialized { false };
int _frameNumber { 0 };
VkExtent2D _windowExtent { 1920, 1080 };
VkExtent2D _windowExtent;
GLFWwindow *window { nullptr };
@ -99,6 +99,7 @@ class Engine {
// Resize
bool _requestResize;
bool _requestRez; // Resolution
// Push constants
std::vector<ComputeEffect> backgroundEffects;
@ -115,6 +116,18 @@ class Engine {
// ImGUI memory editor
MemoryEditor _memEdit;
// Logger
Logger _logger;
// Sum things
int prevID = 0;
// Mesh
VkPipelineLayout _meshPipelineLayout;
VkPipeline _meshPipeline;
GPUMeshBuffers rectangle;
void init();
@ -129,6 +142,8 @@ class Engine {
);
private:
using enum LogModule;
using enum LogLevel;
// Init functions
void initRender();
void initVulkan();
@ -137,10 +152,31 @@ class Engine {
void initSyncStructures();
void initDescriptors();
void initPipelines();
void initTrianglePipeline();
void initBackgroundPipelines();
void initTrianglePipeline();
void initMeshPipeline();
void initImgui();
// Default data
void initDefaultData();
// Allocations
AllocatedBuffer createBuffer(
size_t allocSize,
VkBufferUsageFlags usage,
VmaMemoryUsage memoryUsage
);
void destroyBuffer(
const AllocatedBuffer& buffer
);
// Meshes
GPUMeshBuffers uploadMesh(
std::span<uint32_t> indices,
std::span<Vertex> vertices
);
// Random functions
void createSwapchain(
uint32_t width,
@ -163,8 +199,6 @@ class Engine {
uint32_t width,
uint32_t height
);
};
#endif
@ -181,6 +215,7 @@ class Engine {
#define PIPELINES_IMPL
#define CALLBACK_IMPL
#define TIMER_IMPL
#define LOGGER_IMPL
#define TYPES_IMPL
#define VMA_IMPLEMENTATION
@ -192,6 +227,7 @@ class Engine {
#include "pipelines.h"
#include "Common/Types.h"
#include "Common/Timer.h"
#include "Common/Logger.h"
// Imgui
#include "imgui.h"
@ -203,11 +239,14 @@ class Engine {
#include "VkBootstrap.h"
#include "vk_mem_alloc.h"
// STL includes
#include <iostream>
#ifndef DEBUG
constexpr bool bUseValidationLayers = false;
#else
#ifdef DEBUG
constexpr bool bUseValidationLayers = true;
#else
constexpr bool bUseValidationLayers = false;
#endif
// Abort when there is an error
@ -230,17 +269,30 @@ constexpr bool bUseValidationLayers = true;
void Engine::init() {
#ifdef DEBUG
std::ifstream infile("LICENSE");
fmt::println("[Engine:Init] Initializing the engine...");
fmt::println("[Engine:Info] Current engine version {}", BUILD_ID);
fmt::println("[Engine:Info] Compiled on {} at {}", __DATE__ ,__TIME__);
_logger.logAll(ENGINE, EINIT, "Initializing the engine...");
_logger.logAll(ENGINE, EINFO, "Current engine version {}", BUILD_ID);
_logger.logAll(ENGINE, EINFO, "Compiled on {} at {}", __DATE__ ,__TIME__);
if (infile.good()) {
std::string sLine;
std::getline(infile, sLine);
fmt::println("[Engine:Info] Current Licence : {}", sLine);
_logger.logAll(ENGINE, EINFO, "Current Licence : {}", sLine);
}
#endif
glfwInit();
if(!glfwInit()) {
throw std::runtime_error("Failed to init GLFW !");
}
// Get primary monitor
GLFWmonitor* monitor { glfwGetPrimaryMonitor() };
// Get video mode (resolution)
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
if(mode) {
_windowExtent.width = mode->width;
_windowExtent.height = mode->height;
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
@ -257,16 +309,18 @@ void Engine::init() {
initRender();
initDefaultData();
_isInitialized = true;
#ifdef DEBUG
fmt::println("[Engine:Init] Engine Initialized !");
_logger.logRAM(ENGINE, EINIT, "Engine Initialized !");
#endif
}
void Engine::cleanup() {
#ifdef DEBUG
fmt::println("[Engine:Cleanup] Cleaning up...");
_logger.logConsole(CLEANUP, EINFO, "Cleaning up...");
#endif
if (_isInitialized) {
// Wait for the GPU to stop working
@ -298,8 +352,10 @@ void Engine::cleanup() {
glfwTerminate();
}
_logger.logDisk("=========================================================================");
#ifdef DEBUG
fmt::println("[Engine:Cleanup] Engine cleaned up !");
_logger.logConsole(CLEANUP, EINFO, "Engine cleaned up !");
#endif
}
@ -321,7 +377,7 @@ void Engine::draw() {
_requestResize = true;
return;
} else if (aquireResult != VK_SUCCESS) {
throw std::runtime_error("[Engine:Error] Failed to aquire swapchain image");
throw std::runtime_error("Failed to aquire swapchain image");
}
// Reset fences to redo sync
@ -436,16 +492,15 @@ void Engine::draw() {
VK_CHECK(vkQueueSubmit2(_graphicsQueue, 1, &submit, getCurrentFrame()._renderFence));
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.pNext = nullptr;
presentInfo.pSwapchains = &_swapchain;
presentInfo.swapchainCount = 1;
presentInfo.pWaitSemaphores = &renderSemaphore;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pImageIndices = &swapchainImageIndex;
VkPresentInfoKHR presentInfo {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.pNext = nullptr,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &renderSemaphore,
.swapchainCount = 1,
.pSwapchains = &_swapchain,
.pImageIndices = &swapchainImageIndex
};
VkResult presentResult {vkQueuePresentKHR(_graphicsQueue, &presentInfo)};
@ -454,7 +509,7 @@ void Engine::draw() {
_requestResize = true;
return;
} else if (presentResult != VK_SUCCESS) {
throw std::runtime_error("[Engine:Error] Failed to present swapchain image");
throw std::runtime_error("Failed to present swapchain image");
}
@ -463,7 +518,7 @@ void Engine::draw() {
void Engine::run() {
#ifdef DEBUG
fmt::println("[Engine:Info] Running...");
_logger.logAll(ENGINE, EINFO,"Running...");
RunTimer timer;
timer.startTimer();
#endif
@ -471,6 +526,12 @@ void Engine::run() {
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
// Pipeline change monitor
int currentID = currentBackgroundEffect;
if (prevID != currentID) {
_logger.logRAM(ENGINE, EINFO, "Background shader changed");
}
prevID = currentID;
int w, h;
glfwGetFramebufferSize(window, &w, &h);
@ -493,9 +554,13 @@ void Engine::run() {
buildImgui();
draw();
// Merge glfw logger with main logger
_logger.merge(logger.logBuffer);
logger.clear();
}
#ifdef DEBUG
fmt::println("[Engine:Info] Engine has finished running after {} seconds!", timer.endTimer());
// Not an error but logs to required targets
_logger.logError(ENGINE, EINFO, "Engine has finished running after {} seconds!", timer.endTimer());
#endif
}
@ -515,11 +580,11 @@ void Engine::initRender() {
void Engine::initVulkan() {
#ifdef DEBUG
fmt::println("[Engine:Init] Initializing Vulkan...");
_logger.logRAM(ENGINE, EINIT, "Initializing Vulkan...");
#endif
vkb::InstanceBuilder builder;
// Create a basic VK instance, with debuging features
// Create a basic VK instance, with debuging features if compiled in debug mode
auto instRet = builder.set_app_name("Engine")
.request_validation_layers(bUseValidationLayers)
.use_default_debug_messenger()
@ -562,9 +627,9 @@ void Engine::initVulkan() {
.value();
vkb::DeviceBuilder deviceBuilder { physicalDevice };
#ifdef DEBUG
fmt::println("[Engine:Info] Device {}", physicalDevice.name);
#endif
_logger.logAll(ENGINE, EINFO, "Device {}", physicalDevice.name);
vkb::Device vkbDevice { deviceBuilder.build().value() };
_device = { vkbDevice.device };
@ -581,20 +646,29 @@ void Engine::initVulkan() {
vmaCreateAllocator(&allocatorInfo, &_allocator);
_mainDeletionQueue.pushFunction([&]() {
_mainDeletionQueue.pushFunction([this]() {
#ifdef DEBUG
logger.logConsole(CLEANUP, EINFO, "Allocator");
#endif
vmaDestroyAllocator(_allocator);
});
#ifdef DEBUG
fmt::println("[Engine:Init] Vulkan initialized !");
_logger.logRAM(ENGINE, EINIT, "Vulkan initialized !");
#endif
}
void Engine::initSwapchain() {
#ifdef DEBUG
fmt::println("[Engine:Init] Initializing the swapchain...");
_logger.logRAM(ENGINE, EINIT, "Initializing the swapchain...");
#endif
glfwGetFramebufferSize(
window,
(int*)&_windowExtent.width,
(int*)&_windowExtent.height
);
createSwapchain(_windowExtent.width, _windowExtent.height);
VkExtent3D drawImageExtent = {
@ -671,13 +745,13 @@ void Engine::initSwapchain() {
});
#ifdef DEBUG
fmt::println("[Engine:Init] Swapchain initialized !");
_logger.logRAM(ENGINE, EINIT, "Swapchain initialized !");
#endif
}
void Engine::initCommands() {
#ifdef DEBUG
fmt::println("[Engine:Init] Initializing Vulkan commands..");
_logger.logRAM(ENGINE, EINIT, "Initializing Vulkan commands..");
#endif
VkCommandPoolCreateInfo commandPoolInfo = {
@ -707,8 +781,8 @@ void Engine::initCommands() {
)
);
}
// Immediate commands
// Immediate commands
VK_CHECK(
vkCreateCommandPool(
_device,
@ -737,13 +811,13 @@ void Engine::initCommands() {
});
#ifdef DEBUG
fmt::println("[Engine:Init] Vulkan commands initialized !");
_logger.logRAM(ENGINE, EINIT, "Vulkan commands initialized !");
#endif
}
void Engine::initSyncStructures() {
#ifdef DEBUG
fmt::println("[Engine:Init] Creating sync structures...");
_logger.logRAM(ENGINE, EINIT, "Creating sync structures...");
#endif
// Render semaphores, depends on swapchain
@ -865,19 +939,24 @@ void Engine::initDescriptors(){
void Engine::initPipelines() {
#ifdef DEBUG
fmt::println("[Engine:Init] Initializing pipelines...");
_logger.logRAM(ENGINE, EINIT, "Initializing pipelines...");
#endif
// COMPUTE PIPELINES
initBackgroundPipelines();
// GRAPHICS PIPELINES
initTrianglePipeline();
initMeshPipeline();
#ifdef DEBUG
fmt::println("[Engine:Init] Pipelines initialized !");
_logger.logRAM(ENGINE, EINIT, "Pipelines initialized !");
#endif
}
void Engine::initBackgroundPipelines() {
#ifdef DEBUG
fmt::println("[Engine:Init] Initializing compute pipelines...");
_logger.logRAM(ENGINE, EINIT, "Initializing compute pipelines...");
#endif
@ -1025,13 +1104,13 @@ void Engine::initBackgroundPipelines() {
});
#ifdef DEBUG
fmt::println("[Engine:Init] Compute pipelines initalized !");
_logger.logRAM(ENGINE, EINIT, "Compute pipelines initalized !");
#endif
}
void Engine::initTrianglePipeline() {
#ifdef DEBUG
fmt::println("[Engine:Init] Initializing triangle pipeline...");
_logger.logRAM(ENGINE, EINIT, "Initializing triangle pipeline...");
#endif
// Create the shader
VkShaderModule triangleFragShader;
@ -1106,7 +1185,81 @@ void Engine::initTrianglePipeline() {
});
#ifdef DEBUG
fmt::println("[Engine:Init] Triangle pipeline initalized !");
_logger.logRAM(ENGINE, EINIT, "Triangle pipeline initalized !");
#endif
}
void Engine::initMeshPipeline() {
#ifdef DEBUG
_logger.logRAM(ENGINE, EINIT, "Initializing mesh pipeline...");
#endif
VkShaderModule triangleFragShader;
if (!vkutil::loadShaderModule("shaders/colored_triangle_fragment.spv", _device, &triangleFragShader)) {
fmt::println("[Engine:Error] Error when building the triangle fragment shader module");
}
else {
_logger.logRAM(ENGINE, EINFO, "Triangle fragment shader succesfully loaded");
}
VkShaderModule triangleVertexShader;
if (!vkutil::loadShaderModule("shaders/colored_triangle_mesh.spv", _device, &triangleVertexShader)) {
fmt::println("[Engine:Error] Error when building the triangle vertex shader module");
}
else {
_logger.logRAM(ENGINE, EINFO, "Triangle vertex shader succesfully loaded");
}
VkPushConstantRange bufferRange{};
bufferRange.offset = 0;
bufferRange.size = offsetof(GPUDrawPushConstants, vertexBuffer) + sizeof(VkDeviceAddress);
bufferRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = vkinit::pipelineLayoutCreateInfo();
pipelineLayoutInfo.pPushConstantRanges = &bufferRange;
pipelineLayoutInfo.pushConstantRangeCount = 1;
VK_CHECK(vkCreatePipelineLayout(_device, &pipelineLayoutInfo, nullptr, &_meshPipelineLayout));
PipelineBuilder pipelineBuilder;
// Use the triangle layout we created
pipelineBuilder._pipelineLayout = _meshPipelineLayout;
// Connecting the vertex and pixel shaders to the pipeline
pipelineBuilder.setShaders(triangleVertexShader, triangleFragShader);
// It will draw triangles
pipelineBuilder.setInputTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
// Filled triangles
pipelineBuilder.setPolygonMode(VK_POLYGON_MODE_FILL);
// No backface culling
pipelineBuilder.setCullMode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
// No multisampling
pipelineBuilder.setMultisamplingNone();
// No blending
pipelineBuilder.disableBlending();
pipelineBuilder.disableDepthtest();
// Connect the image format we will draw into, from draw image
pipelineBuilder.setColorAttachmentFormat(_drawImage.imageFormat);
pipelineBuilder.setDepthFormat(VK_FORMAT_UNDEFINED);
// Finally build the pipeline
_meshPipeline = pipelineBuilder.buildPipeline(_device);
// Clean structures
vkDestroyShaderModule(_device, triangleFragShader, nullptr);
vkDestroyShaderModule(_device, triangleVertexShader, nullptr);
_mainDeletionQueue.pushFunction([this]() {
vkDestroyPipelineLayout(_device, _meshPipelineLayout, nullptr);
vkDestroyPipeline(_device, _meshPipeline, nullptr);
});
#ifdef DEBUG
_logger.logRAM(ENGINE, EINIT, "Mesh pipeline initialized !");
std::cout << "sizeof(GPUDrawPushConstants) = " << sizeof(GPUDrawPushConstants) << std::endl;
std::cout << "offsetof(GPUDrawPushConstants, worldMatrix) = " << offsetof(GPUDrawPushConstants, worldMatrix) << std::endl;
std::cout << "offsetof(GPUDrawPushConstants, vertexBuffer) = " << offsetof(GPUDrawPushConstants, vertexBuffer) << std::endl;
#endif
}
@ -1227,6 +1380,203 @@ void Engine::initImgui() {
#endif
}
//====================================
//=== Allocation-related Functions ===
//====================================
AllocatedBuffer Engine::createBuffer(
size_t allocSize,
VkBufferUsageFlags usage,
VmaMemoryUsage memoryUsage
) {
// Allocate the buffer
VkBufferCreateInfo bufferInfo {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.size = allocSize,
.usage = usage
};
VmaAllocationCreateInfo vmaAllocInfo {
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT,
.usage = memoryUsage
};
AllocatedBuffer newBuffer;
// Allocate the buffer
VK_CHECK(
vmaCreateBuffer(
_allocator,
&bufferInfo,
&vmaAllocInfo,
&newBuffer.buffer,
&newBuffer.allocation,
&newBuffer.info
);
);
return newBuffer;
}
void Engine::destroyBuffer(
const AllocatedBuffer& buffer
) {
vmaDestroyBuffer(
_allocator,
buffer.buffer,
buffer.allocation
);
}
//==============================
//=== Mesh-related functions ===
//==============================
GPUMeshBuffers Engine::uploadMesh(
std::span<uint32_t> indices,
std::span<Vertex> vertices
) {
const size_t vertexBufferSize = vertices.size() * sizeof(Vertex);
const size_t indexBufferSize = indices.size() * sizeof(uint32_t);
GPUMeshBuffers newSurface;
// Create a vertex buffer
newSurface.vertexBuffer = createBuffer(
vertexBufferSize,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
VMA_MEMORY_USAGE_GPU_ONLY
);
// Get the address of the vertex buffer
VkBufferDeviceAddressInfo deviceAddressInfo {
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = newSurface.vertexBuffer.buffer
};
newSurface.vertexBufferAddress = vkGetBufferDeviceAddress(_device, &deviceAddressInfo);
// Create a index buffer
newSurface.indexBuffer = createBuffer(
indexBufferSize,
VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VMA_MEMORY_USAGE_GPU_ONLY
);
// Create a staging buffer
AllocatedBuffer staging = createBuffer(
vertexBufferSize + indexBufferSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VMA_MEMORY_USAGE_CPU_ONLY
);
void* data = staging.allocation->GetMappedData();
// Copy vertex buffer
memcpy(
data,
vertices.data(),
vertexBufferSize
);
// Same, but for index buffer
memcpy(
(char*)data + vertexBufferSize,
indices.data(),
indexBufferSize
);
immediateSubmit(
[&](VkCommandBuffer cmd) {
VkBufferCopy vertexCopy{ 0 };
vertexCopy.dstOffset = 0;
vertexCopy.srcOffset = 0;
vertexCopy.size = vertexBufferSize;
vkCmdCopyBuffer(
cmd,
staging.buffer,
newSurface.vertexBuffer.buffer,
1,
&vertexCopy
);
VkBufferCopy indexCopy{ 0 };
indexCopy.dstOffset = 0;
indexCopy.srcOffset = vertexBufferSize;
indexCopy.size = indexBufferSize;
vkCmdCopyBuffer(
cmd,
staging.buffer,
newSurface.indexBuffer.buffer,
1,
&indexCopy
);
}
);
destroyBuffer(staging);
return newSurface;
}
//==============================
//=== Default data functions ===
//==============================
void Engine::initDefaultData() {
#ifdef DEBUG
_logger.logRAM(ENGINE, EINIT, "Creating default data...");
#endif
std::array<Vertex, 4> rectVertices;
rectVertices[0].position = {0.5, -0.5, 0};
rectVertices[1].position = {0.5, 0.5, 0};
rectVertices[2].position = {-0.5, -0.5, 0};
rectVertices[3].position = {-0.5, 0.5, 0};
rectVertices[0].color = {0, 0, 0, 1};
rectVertices[1].color = {0.5, 0.5, 0.5, 1};
rectVertices[2].color = {1, 0, 0, 1};
rectVertices[3].color = {0, 1, 0, 1};
std::array<uint32_t, 6> rectIndices;
rectIndices[0] = 0;
rectIndices[1] = 1;
rectIndices[2] = 2;
rectIndices[3] = 2;
rectIndices[4] = 1;
rectIndices[5] = 3;
rectangle = uploadMesh(
rectIndices,
rectVertices
);
// Delete the rectangle data on engine shutdown
_mainDeletionQueue.pushFunction([this](){
destroyBuffer(rectangle.indexBuffer);
destroyBuffer(rectangle.vertexBuffer);
});
#ifdef DEBUG
_logger.logRAM(ENGINE, EINIT, "Default data created !");
for (size_t i = 0; i < rectIndices.size(); i++) {
fmt::println(
"index[{}] = {} ({:#x})",
i,
rectIndices[i],
rectIndices[i]
);
}
#endif
}
//================================
//=== Render-related Functions ===
@ -1280,6 +1630,7 @@ void Engine::buildImgui() {
ImGui::NewFrame();
// Docking
ImGui::DockSpaceOverViewport(); // Fullscreen dockspace
//_logger.logRAM(ENGINE, EINFO, "Framecount {}", ImGui::GetFrameCount());
// Main ImGui things
ImGui::ShowDemoWindow();
@ -1292,12 +1643,12 @@ void Engine::buildImgui() {
)) {
ImVec2 size = ImGui::GetWindowSize();
if (size.x > 0 && size.y > 0) {
// Redimensionner l'image de rendu si la taille du dock a changé
// Resize viewport render image if dock size changed
if ((uint32_t)size.x != _drawImage.imageExtent.width ||
(uint32_t)size.y != _drawImage.imageExtent.height) {
resizeRenderImage((uint32_t)size.x, (uint32_t)size.y);
}
// Afficher la texture en remplissant la zone
// Display the texture filling the zone
ImGui::Image(_viewportTextureID, size);
}
}
@ -1336,6 +1687,17 @@ void Engine::buildImgui() {
(float*)& selected.data.data4
);
}
ImGui::End();
// Logs
if(ImGui::Begin("Logs")) {
ImGui::TextWrapped("%s", _logger.logBuffer.c_str());
float scrollY = ImGui::GetScrollY();
float maxScrollY = ImGui::GetScrollMaxY();
if (scrollY >= maxScrollY - 1.0f) {
ImGui::SetScrollHereY(1.0f);
}
}
ImGui::End();
// Memory editor
@ -1463,6 +1825,34 @@ void Engine::drawGeometry(
//launch a draw command to draw 3 vertices
vkCmdDraw(cmd, 3, 1, 0, 0);
vkCmdBindPipeline(
cmd,
VK_PIPELINE_BIND_POINT_GRAPHICS,
_meshPipeline
);
GPUDrawPushConstants pushConstants;
pushConstants.worldMatrix = glm::mat4{ 1.0f };
pushConstants.vertexBuffer = rectangle.vertexBufferAddress;
vkCmdPushConstants(
cmd,
_meshPipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT,
0,
offsetof(GPUDrawPushConstants, vertexBuffer) + sizeof(VkDeviceAddress),
&pushConstants
);
vkCmdBindIndexBuffer(
cmd,
rectangle.indexBuffer.buffer,
0,
VK_INDEX_TYPE_UINT32
);
vkCmdDrawIndexed(cmd, 6, 1, 0, 0, 0);
vkCmdEndRendering(cmd);
}
@ -1564,12 +1954,11 @@ void Engine::immediateSubmit(
//INT64_MAX
);
);
}
void Engine::resizeSwapchain() {
#ifdef DEBUG
fmt::println("[Engine:Utils] Resizing swapchain...");
_logger.logRAM(ENGINE, EINFO, "Resizing swapchain...");
#endif
vkDeviceWaitIdle(_device);
@ -1590,13 +1979,13 @@ void Engine::resizeSwapchain() {
_requestResize = false;
#ifdef DEBUG
fmt::println("[Engine:Utils] Swapchain resized !");
_logger.logRAM(ENGINE, EINFO, "Swapchain resized !");
#endif
}
void Engine::resizeRenderImage(uint32_t width, uint32_t height) {
#ifdef DEBUG
fmt::println("[Engine:Utils] Resizing render image to {}x{}", width, height);
_logger.logRAM(ENGINE,EINFO, "Resizing render image to {}x{}", width, height);
#endif
vkDeviceWaitIdle(_device);