diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a9955a..a7a61a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/shaders/colored_triangle_fragment.frag b/shaders/colored_triangle_fragment.frag index d67c317..c43f77f 100644 --- a/shaders/colored_triangle_fragment.frag +++ b/shaders/colored_triangle_fragment.frag @@ -1,13 +1,13 @@ #version 450 //shader input -layout (location = 0) in vec3 inColor; +layout(location = 0) in vec3 inColor; //output write -layout (location = 0) out vec4 outFragColor; +layout(location = 0) out vec4 outFragColor; void main() { - //return red - outFragColor = vec4(inColor,1.0f); + //return red + outFragColor = vec4(inColor, 1.0f); } diff --git a/shaders/colored_triangle_mesh.vert b/shaders/colored_triangle_mesh.vert new file mode 100644 index 0000000..bfb8b93 --- /dev/null +++ b/shaders/colored_triangle_mesh.vert @@ -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; +} diff --git a/src/Common/Logger.h b/src/Common/Logger.h index 0b5b0a7..aed44a9 100644 --- a/src/Common/Logger.h +++ b/src/Common/Logger.h @@ -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 lines; + + // Functions + template + 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 + void logRAM( + LogModule module, + LogLevel level, + std::string_view message, + Args&&... args + ); // Logs to ram (ImGUI) + + template + void logConsole( + LogModule module, + LogLevel level, + std::string_view message, + Args&&... args + ); // Logs to the console (crashes, etc...) + + template + 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 + 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 // Todo : add it in the flake +//#include // Todo : add it in the flake +#include +#include +#include +/* void NetLogger::initNetLogger(Server server) { httplib::Client cli(server.url) // Todo : add error handling } +*/ +template +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)... + ); + + logBuffer.push_back('\n'); + auto end = logBuffer.size(); + lines.emplace_back(logBuffer.data() + start, end - start); +} + +template +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)... + ); + + std::cout.write(tempBuffer.data(), tempBuffer.size()); + std::cout.put('\n'); +} + +template +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)... + ); + + 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 +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 +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 diff --git a/src/Common/Types.h b/src/Common/Types.h index 40b1377..1498032 100644 --- a/src/Common/Types.h +++ b/src/Common/Types.h @@ -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 diff --git a/src/Engine/engine/callbacks.h b/src/Engine/engine/callbacks.h index 93b5986..3135620 100644 --- a/src/Engine/engine/callbacks.h +++ b/src/Engine/engine/callbacks.h @@ -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 } diff --git a/src/Engine/engine/engine.h b/src/Engine/engine/engine.h index 3946759..fe07a6c 100644 --- a/src/Engine/engine/engine.h +++ b/src/Engine/engine/engine.h @@ -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 backgroundEffects; @@ -113,7 +114,19 @@ class Engine { VkDescriptorSet _viewportTextureID; // ImGUI memory editor - MemoryEditor _memEdit; + 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 indices, + std::span 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 -#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,10 +1185,84 @@ 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 +} + void Engine::initImgui() { #ifdef DEBUG fmt::println("[Engine:Init] Initializing ImGui..."); @@ -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 indices, + std::span 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 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 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,28 +1630,29 @@ void Engine::buildImgui() { ImGui::NewFrame(); // Docking ImGui::DockSpaceOverViewport(); // Fullscreen dockspace + //_logger.logRAM(ENGINE, EINFO, "Framecount {}", ImGui::GetFrameCount()); // Main ImGui things ImGui::ShowDemoWindow(); // Viewport - if (ImGui::Begin( + if (ImGui::Begin( "Viewport", nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse - )) { - ImVec2 size = ImGui::GetWindowSize(); - if (size.x > 0 && size.y > 0) { - // Redimensionner l'image de rendu si la taille du dock a changé - 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 - ImGui::Image(_viewportTextureID, size); - } - } - ImGui::End(); + )) { + ImVec2 size = ImGui::GetWindowSize(); + if (size.x > 0 && size.y > 0) { + // 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); + } + // Display the texture filling the zone + ImGui::Image(_viewportTextureID, size); + } + } + ImGui::End(); // Shader control if(ImGui::Begin("Background Shader control")) { @@ -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 @@ -1344,8 +1706,8 @@ void Engine::buildImgui() { } ImGui::End(); - // Finish everything - ImGui::Render(); + // Finish everything + ImGui::Render(); } void Engine::destroySwapchain() { @@ -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);