diff --git a/assets/shaders/helloworld.vert.spv b/assets/shaders/helloworld.vert.spv index 27cdb4f..7d2aebb 100644 Binary files a/assets/shaders/helloworld.vert.spv and b/assets/shaders/helloworld.vert.spv differ diff --git a/frontend/CMakeLists.txt b/frontend/CMakeLists.txt index 7b51280..f1146f3 100644 --- a/frontend/CMakeLists.txt +++ b/frontend/CMakeLists.txt @@ -24,6 +24,7 @@ list(TRANSFORM SOURCES PREPEND "src/") find_package(Boost REQUIRED COMPONENTS lockfree) find_package(xxHash CONFIG REQUIRED) find_package(glfw3 CONFIG REQUIRED) +find_package(glm CONFIG REQUIRED) find_package(Vulkan REQUIRED) find_package(RmlUi CONFIG REQUIRED) @@ -37,6 +38,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE Boost::lockfree) target_link_libraries(${PROJECT_NAME} PRIVATE xxHash::xxhash) target_link_libraries(${PROJECT_NAME} PRIVATE p7) target_link_libraries(${PROJECT_NAME} PRIVATE glfw) +target_link_libraries(${PROJECT_NAME} PRIVATE glm::glm) target_link_libraries(${PROJECT_NAME} PRIVATE Vulkan::Vulkan) target_link_libraries(${PROJECT_NAME} PRIVATE RmlUi::RmlUi) diff --git a/frontend/include/PipelineFactory.hpp b/frontend/include/PipelineFactory.hpp index 82f65ab..16db0db 100644 --- a/frontend/include/PipelineFactory.hpp +++ b/frontend/include/PipelineFactory.hpp @@ -7,6 +7,19 @@ #define PIPELINE_CREATE_NEW 0 #define PIPELINE_CREATE_DERIVED (1 << 0) +struct PipelineBinding +{ + const AssetMaterial *baseMaterial; + + const VkRenderPass *baseRenderPass; + const VkDevice *device; + const VkExtent2D *surfaceExtent; + + const VkVertexInputBindingDescription *vertexBindingConfig; + uint32_t vertexAttributesCount; + const VkVertexInputAttributeDescription *vertexAttributesConfig; +}; + struct PipelineConfig { VkPipelineVertexInputStateCreateInfo vertexInputConfig; @@ -42,19 +55,22 @@ struct Pipeline class PipelineFactory { public: - PipelineFactory(const AssetMaterial &baseMaterial, const VkRenderPass &baseRenderPass, VkDevice &device, VkExtent2D &surfaceExtent); + PipelineFactory(const PipelineBinding &binding); ~PipelineFactory(); Pipeline& Get(const AssetMaterial &material); void Create(const AssetMaterial &material, const VkRenderPass &renderPass, const uint32_t &flags); private: + PipelineBinding binding; PipelineConfig basePipelineConfig; Pipeline basePipeline; std::unordered_map pipelines; - VkDevice &device; - VkExtent2D &surfaceExtent; + const VkDevice &device; + const VkExtent2D &surfaceExtent; + + const VkVertexInputBindingDescription &vertexBindingConfig; }; \ No newline at end of file diff --git a/frontend/include/Renderer.hpp b/frontend/include/Renderer.hpp index 138437f..35cead2 100644 --- a/frontend/include/Renderer.hpp +++ b/frontend/include/Renderer.hpp @@ -2,13 +2,14 @@ #include #include #include +#include #include #include #include #define GLFW_INCLUDE_VULKAN #include -#include +#include #include "PipelineFactory.hpp" @@ -18,6 +19,10 @@ #define VALIDATION_LAYER #endif +#define ATTRIBUTE_FORMAT_VEC2 VK_FORMAT_R32G32_SFLOAT +#define ATTRIBUTE_FORMAT_VEC3 VK_FORMAT_R32G32B32_SFLOAT +#define ATTRIBUTE_FORMAT_VEC4 VK_FORMAT_R32G32B32A32_SFLOAT + // GPU properties priority for selection #define GPU_DISCRETE_PRIORITY 1000 #define GPU_GRAPHICS_WITH_PRESENT_PRIORITY 500 @@ -34,17 +39,93 @@ struct Transform float scaleX = 1.0f, scaleY = 1.0f, scaleZ = 1.0f; }; +struct GPU_Vertex +{ + glm::vec2 pos; + glm::vec3 color; + + static std::array GetAttributes() + { + VkVertexInputAttributeDescription posAttribute = { + .location = 0, + .binding = 0, + .format = ATTRIBUTE_FORMAT_VEC2, + .offset = offsetof(GPU_Vertex, pos) + }; + VkVertexInputAttributeDescription colorAttribute = { + .location = 1, + .binding = 0, + .format = ATTRIBUTE_FORMAT_VEC3, + .offset = offsetof(GPU_Vertex, color) + }; + + return {posAttribute, colorAttribute}; + } +}; + +struct GPU_BufferSlot +{ + size_t position; + size_t size; +}; + +//TODO: move methods definition to the end of file +template struct GPU_Buffer +{ + T *data; + size_t size; + const size_t ELEMENT_SIZE; + std::vector freeSlots; + + GPU_Buffer() : ELEMENT_SIZE(sizeof(T)) {freeSlots.push_back({.position = 0, .size = size});} + + GPU_BufferSlot* FindFreeSlot(size_t size) + { + for (auto i = freeSlots.rbegin(); i != freeSlots.rend(); i++) + { + if (i->size >= size) + return &(*i); + } + return nullptr; + } + + size_t Write(const T *elements, size_t elementsCount) + { + GPU_BufferSlot *slot = FindFreeSlot(elementsCount); + if (slot == nullptr) return 0; + + memcpy(data + slot->position, elements, elementsCount * ELEMENT_SIZE); + + slot->size -= elementsCount; + slot->position += elementsCount; + if (slot->size == 0) + freeSlots.pop_back(); + + return slot->position - elementsCount; + } + + // void Clear(GPU_BufferSlot range) + // { + // freeSlots.push_back(range); + // //TODO: insert sorted by pos and combine intersections + // } +}; + struct Mesh { - uint32_t vertexCount = 0; + size_t verticesBufferPosition = 0; + std::vector vertices; }; struct RenderObject { + uint32_t engineId = UINT32_MAX; + // Multiple transforms for batching std::vector transforms; uint8_t depth = 0; - Mesh mesh; //TODO: mesh (vertex/index buffer) + Mesh mesh; + bool isUI = false; //TODO: texture AssetMaterial &material; @@ -69,12 +150,14 @@ struct RenderCommand struct QueueFamiliesBundle { + uint32_t transfer = QUEUE_FAMILY_NO_VALUE; uint32_t graphics = QUEUE_FAMILY_NO_VALUE; uint32_t present = QUEUE_FAMILY_NO_VALUE; }; struct QueuesBundle { + VkQueue transfer; VkQueue graphics; VkQueue present; }; @@ -115,6 +198,12 @@ struct DrawConfig .swapchainCount = 1, .pResults = nullptr }; + + VkVertexInputBindingDescription vertexBindingConfig { + .binding = 0, + .stride = sizeof(GPU_Vertex), + .inputRate = VK_VERTEX_INPUT_RATE_VERTEX + }; }; struct SurfaceInfo @@ -131,7 +220,7 @@ struct SurfaceInfo class Renderer { public: - Renderer(GLFWwindow *window, const AssetMaterial& baseMaterial); + Renderer(GLFWwindow *window, const AssetMaterial& baseMaterial, const size_t &vertexBufferSize); ~Renderer(); void Start(); @@ -160,8 +249,16 @@ private: VkDevice device; VkSwapchainKHR swapchain; VkRenderPass baseRenderPass; - VkCommandPool commandPool = nullptr; - std::vector commandBuffers; + VkCommandPool graphicsCommandPool = nullptr; + VkCommandPool transferCommandPool = nullptr; + VkDeviceMemory worldVerticesMemory; + VkDeviceMemory stagingWorldVerticesMemory; + VkDeviceMemory uiVerticesMemory; + VkBuffer worldVertices; + VkBuffer stagingWorldVertices; + VkBuffer uiVertices; + std::vector graphicsCommandBuffers; + std::vector transferCommandBuffers; std::vector frames; std::vector frameViews; std::vector baseFrameBuffers; @@ -175,14 +272,20 @@ private: QueuesBundle queues; DrawConfig drawConfig; PipelineFactory *pipelineFactory; + PipelineBinding pipelineBinding; size_t currentFrame = 0; + + GPU_Buffer worldVerticesBinded; + GPU_Buffer uiVerticesBinded; + const float DEFAULT_QUEUE_PRIORITY = 1.0f; const AssetMaterial &baseMaterial; const size_t MAX_FRAMES_IN_FLIGHT = 2; + const size_t VERTEX_BUFFER_SIZE; std::set objects; - Rml::Context *uiContext; + //Rml::Context *uiContext; void InitVulkan(); void SetupDevice(); @@ -190,10 +293,13 @@ private: void RecreateSwapchain(); void ReleaseSwapchain(); void SetupRenderPasses(); - void SetupBuffers(); + void SetupPipelines(); + void SetupObjectsBuffers(); + void SetupRenderBuffers(); void SetupSync(); void Draw(); + void CopyBuffer(VkBuffer src, size_t offset, VkBuffer dst, VkDeviceSize size); void Threading(); void Request(const RenderCommand &command); diff --git a/frontend/src/PipelineFactory.cpp b/frontend/src/PipelineFactory.cpp index 77862f1..83e729b 100644 --- a/frontend/src/PipelineFactory.cpp +++ b/frontend/src/PipelineFactory.cpp @@ -22,14 +22,13 @@ PipelineConfig PipelineConfig::Copy() const return copy; } -PipelineFactory::PipelineFactory( - const AssetMaterial &baseMaterial, - const VkRenderPass &baseRenderPass, - VkDevice &device, - VkExtent2D &surfaceExtent -) : - device(device), surfaceExtent(surfaceExtent) +PipelineFactory::PipelineFactory(const PipelineBinding &binding) : + device(*binding.device), + surfaceExtent(*binding.surfaceExtent), + vertexBindingConfig(*binding.vertexBindingConfig) { + this->binding = binding; + basePipelineConfig.dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT); basePipelineConfig.dynamicStates.push_back(VK_DYNAMIC_STATE_SCISSOR); @@ -47,10 +46,10 @@ PipelineFactory::PipelineFactory( basePipelineConfig.vertexInputConfig = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pNext = nullptr, - .vertexBindingDescriptionCount = 0, - .pVertexBindingDescriptions = nullptr, - .vertexAttributeDescriptionCount = 0, - .pVertexAttributeDescriptions = nullptr + .vertexBindingDescriptionCount = 1, + .pVertexBindingDescriptions = &vertexBindingConfig, + .vertexAttributeDescriptionCount = binding.vertexAttributesCount, + .pVertexAttributeDescriptions = binding.vertexAttributesConfig }; basePipelineConfig.inputAssemblyConfig = { @@ -146,7 +145,7 @@ PipelineFactory::PipelineFactory( }; LOG(INFO, "Creating base material pipeline..."); - Create(baseMaterial, baseRenderPass, PIPELINE_CREATE_NEW); + Create(*binding.baseMaterial, *binding.baseRenderPass, PIPELINE_CREATE_NEW); LOG(INFO, "Base material pipeline created"); } diff --git a/frontend/src/Renderer.cpp b/frontend/src/Renderer.cpp index 25f5105..51c1b63 100644 --- a/frontend/src/Renderer.cpp +++ b/frontend/src/Renderer.cpp @@ -11,13 +11,16 @@ bool operator < (const RenderObject& lhs, const RenderObject& rhs) { - return &lhs.material < &rhs.material; + if (&lhs.material != &rhs.material) + return &lhs.material < &rhs.material; + else return lhs.engineId < rhs.engineId; } -Renderer::Renderer(GLFWwindow *window, const AssetMaterial &baseMaterial) : +Renderer::Renderer(GLFWwindow *window, const AssetMaterial &baseMaterial, const size_t &vertexBufferSize) : isRunning(false), isDrawing(false), - baseMaterial(baseMaterial) + baseMaterial(baseMaterial), + VERTEX_BUFFER_SIZE(vertexBufferSize) { this->window = window; int width = -1, height = -1; @@ -180,6 +183,7 @@ void Renderer::SetupDevice() queueFamiliesBuffer.present == QUEUE_FAMILY_NO_VALUE ) continue; + queueFamiliesBuffer.transfer = queueFamiliesBuffer.graphics; // Check surface info SurfaceInfo surfaceInfoBuffer {}; @@ -272,8 +276,10 @@ void Renderer::SetupDevice() } // Define queues + //TODO: transfer is separate queue for async loading std::vector queuesCreateConfigs; std::set uniqueQueueFamilies { + queueFamilies.transfer, queueFamilies.graphics, queueFamilies.present }; @@ -315,6 +321,7 @@ void Renderer::SetupDevice() else LOG(INFO, "%hs is selected for render with %d priority score", deviceProperties.deviceName, best_priority); // Save queues descriptor + vkGetDeviceQueue(device, queueFamilies.transfer, 0, &queues.transfer); vkGetDeviceQueue(device, queueFamilies.graphics, 0, &queues.graphics); vkGetDeviceQueue(device, queueFamilies.present, 0, &queues.present); } @@ -430,8 +437,8 @@ void Renderer::RecreateSwapchain() SetupSwapchain(); SetupRenderPasses(); - pipelineFactory = Allocate(baseMaterial, baseRenderPass, device, surfaceInfo.extent); - SetupBuffers(); + SetupPipelines(); + SetupRenderBuffers(); } void Renderer::ReleaseSwapchain() @@ -442,9 +449,15 @@ void Renderer::ReleaseSwapchain() } vkFreeCommandBuffers( device, - commandPool, - static_cast(commandBuffers.size()), - commandBuffers.data() + graphicsCommandPool, + static_cast(graphicsCommandBuffers.size()), + graphicsCommandBuffers.data() + ); + vkFreeCommandBuffers( + device, + transferCommandPool, + static_cast(transferCommandBuffers.size()), + transferCommandBuffers.data() ); vkDestroyRenderPass(device, baseRenderPass, nullptr); @@ -509,7 +522,162 @@ void Renderer::SetupRenderPasses() LOG(CRITICAL, "Failed to create base render pass. Code: %d", vkResult); } -void Renderer::SetupBuffers() +void Renderer::SetupPipelines() +{ + std::array vertexAttributes = GPU_Vertex::GetAttributes(); + + pipelineBinding = { + .baseMaterial = &baseMaterial, + .baseRenderPass = &baseRenderPass, + .device = &device, + .surfaceExtent = &surfaceInfo.extent, + + .vertexBindingConfig = &drawConfig.vertexBindingConfig, + .vertexAttributesCount = static_cast(vertexAttributes.size()), + .vertexAttributesConfig = vertexAttributes.data() + }; + + pipelineFactory = Allocate(pipelineBinding); +} + +void Renderer::SetupObjectsBuffers() +{ + VkResult vkResult; + + VkBufferCreateInfo worldVertexBufferConfig { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .size = sizeof(GPU_Vertex) * VERTEX_BUFFER_SIZE, + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE + }; + worldVerticesBinded.size = VERTEX_BUFFER_SIZE; + vkResult = vkCreateBuffer(device, &worldVertexBufferConfig, nullptr, &worldVertices); + if (vkResult != VK_SUCCESS) + LOG(CRITICAL, "Failed to create buffer for world vertices. Code: %d", vkResult); + + VkBufferCreateInfo stagingWorldVertexBufferConfig { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .size = sizeof(GPU_Vertex) * VERTEX_BUFFER_SIZE, + .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE + }; + vkResult = vkCreateBuffer(device, &stagingWorldVertexBufferConfig, nullptr, &stagingWorldVertices); + if (vkResult != VK_SUCCESS) + LOG(CRITICAL, "Failed to create buffer for staging world vertices. Code: %d", vkResult); + + VkBufferCreateInfo uiVertexBufferConfig { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .size = sizeof(GPU_Vertex) * VERTEX_BUFFER_SIZE, + .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE + }; + uiVerticesBinded.size = VERTEX_BUFFER_SIZE; + vkResult = vkCreateBuffer(device, &uiVertexBufferConfig, nullptr, &uiVertices); + if (vkResult != VK_SUCCESS) + LOG(CRITICAL, "Failed to create buffer for UI vertices. Code: %d", vkResult); + + VkMemoryRequirements worldMemoryRequirements; + vkGetBufferMemoryRequirements(device, worldVertices, &worldMemoryRequirements); + VkMemoryRequirements stagingWorldMemoryRequirements; + vkGetBufferMemoryRequirements(device, stagingWorldVertices, &stagingWorldMemoryRequirements); + VkMemoryRequirements uiMemoryRequirements; + vkGetBufferMemoryRequirements(device, uiVertices, &uiMemoryRequirements); + + VkPhysicalDeviceMemoryProperties memoryProps; + vkGetPhysicalDeviceMemoryProperties(gpu, &memoryProps); + uint32_t optimizedMemTypeIndex = UINT32_MAX, cpuAccessMemTypeIndex = UINT32_MAX; + VkMemoryPropertyFlags optimizedMemoryProps = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + VkMemoryPropertyFlags cpuAccessMemoryProps = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + for (uint32_t i = 0; i < memoryProps.memoryTypeCount; i++) + { + if ( + optimizedMemTypeIndex == UINT32_MAX && + worldMemoryRequirements.memoryTypeBits & (1 << i) && + (memoryProps.memoryTypes[i].propertyFlags & optimizedMemoryProps) == optimizedMemoryProps + ) + optimizedMemTypeIndex = i; + if ( + cpuAccessMemTypeIndex == UINT32_MAX && + stagingWorldMemoryRequirements.memoryTypeBits & (1 << i) && + uiMemoryRequirements.memoryTypeBits & (1 << i) && + (memoryProps.memoryTypes[i].propertyFlags & cpuAccessMemoryProps) == cpuAccessMemoryProps + ) + cpuAccessMemTypeIndex = i; + } + if (optimizedMemTypeIndex == UINT32_MAX) + LOG(CRITICAL, "Failed to find GPU memory available for local data. Code: %d", vkResult); + if (cpuAccessMemTypeIndex == UINT32_MAX) + LOG(CRITICAL, "Failed to find GPU memory available for transfering data. Code: %d", vkResult); + + VkMemoryAllocateInfo allocateWorldVertices { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = nullptr, + .allocationSize = worldMemoryRequirements.size, + .memoryTypeIndex = optimizedMemTypeIndex + }; + VkMemoryAllocateInfo allocateStagingWorldVertices { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = nullptr, + .allocationSize = stagingWorldMemoryRequirements.size, + .memoryTypeIndex = cpuAccessMemTypeIndex + }; + VkMemoryAllocateInfo allocateUiVertices { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = nullptr, + .allocationSize = uiMemoryRequirements.size, + .memoryTypeIndex = cpuAccessMemTypeIndex + }; + + vkResult = vkAllocateMemory(device, &allocateWorldVertices, nullptr, &worldVerticesMemory); + if (vkResult != VK_SUCCESS) + LOG(CRITICAL, "Failed to allocate GPU memory for world vertices. Code: %d", vkResult); + vkResult = vkAllocateMemory( + device, &allocateStagingWorldVertices, nullptr, &stagingWorldVerticesMemory); + if (vkResult != VK_SUCCESS) + LOG(CRITICAL, "Failed to allocate GPU memory for staging world vertices. Code: %d", vkResult); + vkResult = vkAllocateMemory(device, &allocateUiVertices, nullptr, &uiVerticesMemory); + if (vkResult != VK_SUCCESS) + LOG(CRITICAL, "Failed to allocate GPU memory for UI vertices. Code: %d", vkResult); + + vkResult = vkBindBufferMemory(device, worldVertices, worldVerticesMemory, 0); + if (vkResult != VK_SUCCESS) + LOG(CRITICAL, "Failed to bind GPU memory for world vertices. Code: %d", vkResult); + vkResult = vkBindBufferMemory(device, stagingWorldVertices, stagingWorldVerticesMemory, 0); + if (vkResult != VK_SUCCESS) + LOG(CRITICAL, "Failed to bind GPU memory for staging world vertices. Code: %d", vkResult); + vkResult = vkBindBufferMemory(device, uiVertices, uiVerticesMemory, 0); + if (vkResult != VK_SUCCESS) + LOG(CRITICAL, "Failed to bind GPU memory for UI vertices. Code: %d", vkResult); + + vkResult = vkMapMemory( + device, + stagingWorldVerticesMemory, + 0, + stagingWorldVertexBufferConfig.size, + 0, + reinterpret_cast(&(worldVerticesBinded.data)) + ); + if (vkResult != VK_SUCCESS) + LOG(CRITICAL, "Failed to map staging world vertices buffer to CPU"); + vkResult = vkMapMemory( + device, + uiVerticesMemory, + 0, + uiVertexBufferConfig.size, + 0, + reinterpret_cast(&uiVerticesBinded.data) + ); + if (vkResult != VK_SUCCESS) + LOG(CRITICAL, "Failed to map UI buffer vertices to CPU"); + + memset(worldVerticesBinded.data, 0, stagingWorldVertexBufferConfig.size); + memset(uiVerticesBinded.data, 0, uiVertexBufferConfig.size); +} + +void Renderer::SetupRenderBuffers() { VkResult vkResult; @@ -537,7 +705,7 @@ void Renderer::SetupBuffers() i++; } - if (commandPool == nullptr) + if (graphicsCommandPool == nullptr) { VkCommandPoolCreateInfo commandPoolConfig { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, @@ -546,22 +714,48 @@ void Renderer::SetupBuffers() .queueFamilyIndex = queueFamilies.graphics, }; - vkResult = vkCreateCommandPool(device, &commandPoolConfig, nullptr, &commandPool); + vkResult = vkCreateCommandPool(device, &commandPoolConfig, nullptr, &graphicsCommandPool); if (vkResult != VK_SUCCESS) - LOG(CRITICAL, "Failed to create command pool. Code: %d", vkResult); + LOG(CRITICAL, "Failed to create graphics command pool. Code: %d", vkResult); } - commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); + graphicsCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT); VkCommandBufferAllocateInfo allocateConfig { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .pNext = nullptr, - .commandPool = commandPool, + .commandPool = graphicsCommandPool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, //TODO: reuse some commands using SECONDARY - .commandBufferCount = static_cast(commandBuffers.size()) + .commandBufferCount = static_cast(graphicsCommandBuffers.size()) }; - vkResult = vkAllocateCommandBuffers(device, &allocateConfig, commandBuffers.data()); + vkResult = vkAllocateCommandBuffers(device, &allocateConfig, graphicsCommandBuffers.data()); if (vkResult != VK_SUCCESS) - LOG(CRITICAL, "Failed to allocate command buffers. Code: %d", vkResult); + LOG(CRITICAL, "Failed to allocate graphics command buffers. Code: %d", vkResult); + + if (transferCommandPool == nullptr) + { + VkCommandPoolCreateInfo commandPoolConfig { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, + .queueFamilyIndex = queueFamilies.transfer, + }; + + vkResult = vkCreateCommandPool(device, &commandPoolConfig, nullptr, &transferCommandPool); + if (vkResult != VK_SUCCESS) + LOG(CRITICAL, "Failed to create transfer command pool. Code: %d", vkResult); + } + + transferCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT); + allocateConfig = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .pNext = nullptr, + .commandPool = transferCommandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, //TODO: reuse some commands using SECONDARY + .commandBufferCount = static_cast(transferCommandBuffers.size()) + }; + vkResult = vkAllocateCommandBuffers(device, &allocateConfig, transferCommandBuffers.data()); + if (vkResult != VK_SUCCESS) + LOG(CRITICAL, "Failed to allocate transfer command buffers. Code: %d", vkResult); } void Renderer::Draw() @@ -604,10 +798,10 @@ void Renderer::Draw() } imagesInFlight[imageIndex] = inFlightFences[currentFrame]; - vkResult = vkResetCommandBuffer(commandBuffers[currentFrame], 0); + vkResult = vkResetCommandBuffer(graphicsCommandBuffers[currentFrame], 0); if (vkResult != VK_SUCCESS) LOG(ERROR, "Failed to reset command buffer. Code: %d", vkResult); - vkResult = vkBeginCommandBuffer(commandBuffers[currentFrame], &drawConfig.commandBeginConfig); + vkResult = vkBeginCommandBuffer(graphicsCommandBuffers[currentFrame], &drawConfig.commandBeginConfig); if (vkResult != VK_SUCCESS) LOG(ERROR, "Failed to begin command buffer. Code: %d", vkResult); @@ -619,7 +813,7 @@ void Renderer::Draw() }; vkCmdBeginRenderPass( - commandBuffers[currentFrame], + graphicsCommandBuffers[currentFrame], &drawConfig.renderPassBeginConfig, VK_SUBPASS_CONTENTS_INLINE ); @@ -632,26 +826,41 @@ void Renderer::Draw() if (pipeline != lastPipeline) { vkCmdBindPipeline( - commandBuffers[currentFrame], + graphicsCommandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->instance ); - vkCmdSetViewport(commandBuffers[currentFrame], 0, 1, &pipeline->viewport); - vkCmdSetScissor(commandBuffers[currentFrame], 0, 1, &pipeline->scissors); + vkCmdSetViewport(graphicsCommandBuffers[currentFrame], 0, 1, &pipeline->viewport); + vkCmdSetScissor(graphicsCommandBuffers[currentFrame], 0, 1, &pipeline->scissors); lastPipeline = pipeline; } + std::vector vertexBuffers; + vertexBuffers.reserve(1); + if (object.isUI) + vertexBuffers.push_back(uiVertices); + else + vertexBuffers.push_back(worldVertices); + VkDeviceSize offsets[] = {object.mesh.verticesBufferPosition}; + vkCmdBindVertexBuffers( + graphicsCommandBuffers[currentFrame], + 0, + 1, + vertexBuffers.data(), + offsets + ); + vkCmdDraw( - commandBuffers[currentFrame], - object.mesh.vertexCount, + graphicsCommandBuffers[currentFrame], + static_cast(object.mesh.vertices.size()), static_cast(object.transforms.size()), 0, // TODO: batching 0 // TODO: batching ); } - vkCmdEndRenderPass(commandBuffers[currentFrame]); - vkResult = vkEndCommandBuffer(commandBuffers[currentFrame]); + vkCmdEndRenderPass(graphicsCommandBuffers[currentFrame]); + vkResult = vkEndCommandBuffer(graphicsCommandBuffers[currentFrame]); if (vkResult != VK_SUCCESS) LOG(ERROR, "Failed to end command buffer. Code: %d", vkResult); @@ -662,7 +871,7 @@ void Renderer::Draw() drawConfig.submitConfig.pWaitSemaphores = waitSemaphores; //drawConfig.submitConfig.pSignalSemaphores = &renderFinished[currentFrame]; drawConfig.submitConfig.pSignalSemaphores =signalSemaphores; - drawConfig.submitConfig.pCommandBuffers = &commandBuffers[currentFrame]; + drawConfig.submitConfig.pCommandBuffers = &graphicsCommandBuffers[currentFrame]; drawConfig.submitConfig.pWaitDstStageMask = waitStages; vkResult = vkResetFences(device, 1, &inFlightFences[currentFrame]); @@ -699,6 +908,54 @@ void Renderer::Draw() currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } +void Renderer::CopyBuffer(VkBuffer src, size_t offset, VkBuffer dst, VkDeviceSize size) +{ + VkResult vkResult; + + VkCommandBufferAllocateInfo bufferAllocateConfig { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = transferCommandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + + VkCommandBuffer commandBuffer; + vkResult = vkAllocateCommandBuffers(device, &bufferAllocateConfig, &commandBuffer); + if (vkResult != VK_SUCCESS) + LOG(ERROR, "Failed to allocate temporary command buffer for copy operation. Code: %d", vkResult); + + VkCommandBufferBeginInfo commandBeginConfig { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + }; + vkResult = vkBeginCommandBuffer(commandBuffer, &commandBeginConfig); + if (vkResult != VK_SUCCESS) + LOG(ERROR, "Failed to begin command for copy operation. Code: %d", vkResult); + + VkBufferCopy copyRegion { + .srcOffset = offset, + .dstOffset = offset, + .size = size + }; + vkCmdCopyBuffer(commandBuffer, src, dst, 1, ©Region); + + vkResult = vkEndCommandBuffer(commandBuffer); + if (vkResult != VK_SUCCESS) + LOG(ERROR, "Failed to end command for copy operation. Code: %d", vkResult); + + VkSubmitInfo submitConfig { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = nullptr, + .commandBufferCount = 1, + .pCommandBuffers = &commandBuffer + }; + vkQueueSubmit(queues.transfer, 1, &submitConfig, VK_NULL_HANDLE); //TODO: use fence for optimizatoin + vkQueueWaitIdle(queues.transfer); + + vkFreeCommandBuffers(device, transferCommandPool, 1, &commandBuffer); +} + void Renderer::SetupSync() { VkResult vkResult; @@ -764,10 +1021,10 @@ void Renderer::Init() SetupDevice(); SetupSwapchain(); SetupRenderPasses(); - - pipelineFactory = Allocate(baseMaterial, baseRenderPass, device, surfaceInfo.extent); - - SetupBuffers(); + SetupPipelines(); + SetupObjectsBuffers(); + SetupRenderBuffers(); + CopyBuffer(stagingWorldVertices, 0, worldVertices, worldVerticesBinded.size * worldVerticesBinded.ELEMENT_SIZE); SetupSync(); } @@ -803,7 +1060,23 @@ void Renderer::ProcessCommand(const RenderCommand &command) for (size_t i = 0; i < command.objectsSize; i++) { - objects.insert(command.objects[i]); + RenderObject &object = command.objects[i]; + object.engineId = static_cast(objects.size()); + if (objects.contains(object)) continue; + + // TODO: combine vertices data and write with one Write out of loop + size_t verticesPos = worldVerticesBinded.Write( + object.mesh.vertices.data(), + object.mesh.vertices.size() + ) * worldVerticesBinded.ELEMENT_SIZE; + object.mesh.verticesBufferPosition = verticesPos; + CopyBuffer( + stagingWorldVertices, + verticesPos, + worldVertices, + object.mesh.vertices.size() * worldVerticesBinded.ELEMENT_SIZE + ); + objects.insert(object); } break; @@ -864,6 +1137,17 @@ void Renderer::Release() ReleaseSwapchain(); + vkUnmapMemory(device, stagingWorldVerticesMemory); + vkUnmapMemory(device, uiVerticesMemory); + + vkDestroyBuffer(device, worldVertices, nullptr); + vkDestroyBuffer(device, stagingWorldVertices, nullptr); + vkDestroyBuffer(device, uiVertices, nullptr); + + vkFreeMemory(device, worldVerticesMemory, nullptr); + vkFreeMemory(device, stagingWorldVerticesMemory, nullptr); + vkFreeMemory(device, uiVerticesMemory, nullptr); + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { vkDestroySemaphore(device, renderFinished[i], nullptr); @@ -871,7 +1155,8 @@ void Renderer::Release() vkDestroyFence(device, inFlightFences[i], nullptr); } - vkDestroyCommandPool(device, commandPool, nullptr); + vkDestroyCommandPool(device, graphicsCommandPool, nullptr); + vkDestroyCommandPool(device, transferCommandPool, nullptr); vkDestroyDevice(device, nullptr); vkDestroySurfaceKHR(vulkan, surface, nullptr); diff --git a/frontend/src/main.cpp b/frontend/src/main.cpp index 458bec7..869a8d4 100644 --- a/frontend/src/main.cpp +++ b/frontend/src/main.cpp @@ -35,7 +35,7 @@ int main(int argc, char **argv) assetManager.LoadAsync(AssetInfo::MATERIAL, "assets/materials/helloworld.mat"); AssetHandle baseMaterialHandle = assetManager.Find("assets/materials/helloworld.mat"); AssetMaterial baseMaterial = assetManager.Get(baseMaterialHandle); - Renderer renderer(window, baseMaterial); + Renderer renderer(window, baseMaterial, 1000000); //TODO: buffer sizes renderer.Start(); //Setup Engine @@ -49,11 +49,30 @@ int main(int argc, char **argv) // Test render obj RenderObject triangle { .transforms = {{.x = 0, .y = 0}}, - .mesh = {.vertexCount = 3}, + .mesh = { + .vertices = { + {.pos = {-0.5f, -0.5f}, .color = {1.0f, 0.0f, 0.0f}}, + {.pos = {0.0f, 0.5f}, .color = {0.0f, 0.0f, 0.0f}}, + {.pos = {-1.0f, 0.5f}, .color = {1.0f, 1.0f, 1.0f}} + } + }, .material = baseMaterial, }; - renderer.RegisterObjects(&triangle, 1); + RenderObject triangle2 { + .transforms = {{.x = 0, .y = 0}}, + .mesh = { + .vertices = { + {.pos = {0.0f, -0.5f}, .color = {1.0f, 0.0f, 0.0f}}, + {.pos = {0.5f, 0.5f}, .color = {0.0f, 1.0f, 0.0f}}, + {.pos = {-0.5f, 0.5f}, .color = {0.0f, 0.0f, 1.0f}} + } + }, + + .material = baseMaterial, + }; + RenderObject objs[] = {triangle, triangle2}; + renderer.RegisterObjects(objs, 2); // Process GLFW events while(!glfwWindowShouldClose(window)) diff --git a/sdk/shaders/helloworld/helloworld.vert b/sdk/shaders/helloworld/helloworld.vert index 0439d32..92a086e 100644 --- a/sdk/shaders/helloworld/helloworld.vert +++ b/sdk/shaders/helloworld/helloworld.vert @@ -1,21 +1,12 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec3 inColor; + layout(location = 0) out vec3 fragColor; -vec2 positions[3] = vec2[]( - vec2(0.0, -0.5), - vec2(0.5, 0.5), - vec2(-0.5, 0.5) -); - -vec3 colors[3] = vec3[]( - vec3(1.0, 0.0, 0.0), - vec3(0.0, 1.0, 0.0), - vec3(0.0, 0.0, 1.0) -); - void main() { - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); - fragColor = colors[gl_VertexIndex]; + gl_Position = vec4(inPosition, 0.0, 1.0); + fragColor = inColor; } \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index 34dab61..6126994 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -6,6 +6,7 @@ "entt", "vulkan", "glfw3", + "glm", "rmlui", "gtest" ]