Vertex buffers

- Fix RenderObject comparator for storing in set
- RenderObjects get ID on register
- Transfer queue family and queue
- Command buffers for transfer queue
- PipelineFactory constructor parameters combined in PipelineBinding structure
- GPU_Vertex and GPU_Buffer structures to map GPU memory data
- Vertex buffers for world and UI RenderObjects with staging
This commit is contained in:
legatt 2025-10-04 20:46:15 +03:00
parent 16bd65c35a
commit 2e0369ee2b
9 changed files with 493 additions and 74 deletions

Binary file not shown.

View File

@ -24,6 +24,7 @@ list(TRANSFORM SOURCES PREPEND "src/")
find_package(Boost REQUIRED COMPONENTS lockfree) find_package(Boost REQUIRED COMPONENTS lockfree)
find_package(xxHash CONFIG REQUIRED) find_package(xxHash CONFIG REQUIRED)
find_package(glfw3 CONFIG REQUIRED) find_package(glfw3 CONFIG REQUIRED)
find_package(glm CONFIG REQUIRED)
find_package(Vulkan REQUIRED) find_package(Vulkan REQUIRED)
find_package(RmlUi CONFIG 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 xxHash::xxhash)
target_link_libraries(${PROJECT_NAME} PRIVATE p7) target_link_libraries(${PROJECT_NAME} PRIVATE p7)
target_link_libraries(${PROJECT_NAME} PRIVATE glfw) 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 Vulkan::Vulkan)
target_link_libraries(${PROJECT_NAME} PRIVATE RmlUi::RmlUi) target_link_libraries(${PROJECT_NAME} PRIVATE RmlUi::RmlUi)

View File

@ -7,6 +7,19 @@
#define PIPELINE_CREATE_NEW 0 #define PIPELINE_CREATE_NEW 0
#define PIPELINE_CREATE_DERIVED (1 << 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 struct PipelineConfig
{ {
VkPipelineVertexInputStateCreateInfo vertexInputConfig; VkPipelineVertexInputStateCreateInfo vertexInputConfig;
@ -42,19 +55,22 @@ struct Pipeline
class PipelineFactory class PipelineFactory
{ {
public: public:
PipelineFactory(const AssetMaterial &baseMaterial, const VkRenderPass &baseRenderPass, VkDevice &device, VkExtent2D &surfaceExtent); PipelineFactory(const PipelineBinding &binding);
~PipelineFactory(); ~PipelineFactory();
Pipeline& Get(const AssetMaterial &material); Pipeline& Get(const AssetMaterial &material);
void Create(const AssetMaterial &material, const VkRenderPass &renderPass, const uint32_t &flags); void Create(const AssetMaterial &material, const VkRenderPass &renderPass, const uint32_t &flags);
private: private:
PipelineBinding binding;
PipelineConfig basePipelineConfig; PipelineConfig basePipelineConfig;
Pipeline basePipeline; Pipeline basePipeline;
std::unordered_map<const AssetMaterial*, Pipeline> pipelines; std::unordered_map<const AssetMaterial*, Pipeline> pipelines;
VkDevice &device; const VkDevice &device;
VkExtent2D &surfaceExtent; const VkExtent2D &surfaceExtent;
const VkVertexInputBindingDescription &vertexBindingConfig;
}; };

View File

@ -2,13 +2,14 @@
#include <thread> #include <thread>
#include <atomic> #include <atomic>
#include <cstdint> #include <cstdint>
#include <array>
#include <vector> #include <vector>
#include <set> #include <set>
#include <Boost/lockfree/spsc_queue.hpp> #include <Boost/lockfree/spsc_queue.hpp>
#define GLFW_INCLUDE_VULKAN #define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <RmlUi/Core.h> #include <glm/glm.hpp>
#include "PipelineFactory.hpp" #include "PipelineFactory.hpp"
@ -18,6 +19,10 @@
#define VALIDATION_LAYER #define VALIDATION_LAYER
#endif #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 // GPU properties priority for selection
#define GPU_DISCRETE_PRIORITY 1000 #define GPU_DISCRETE_PRIORITY 1000
#define GPU_GRAPHICS_WITH_PRESENT_PRIORITY 500 #define GPU_GRAPHICS_WITH_PRESENT_PRIORITY 500
@ -34,17 +39,93 @@ struct Transform
float scaleX = 1.0f, scaleY = 1.0f, scaleZ = 1.0f; float scaleX = 1.0f, scaleY = 1.0f, scaleZ = 1.0f;
}; };
struct GPU_Vertex
{
glm::vec2 pos;
glm::vec3 color;
static std::array<VkVertexInputAttributeDescription, 2> 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<typename T> struct GPU_Buffer
{
T *data;
size_t size;
const size_t ELEMENT_SIZE;
std::vector<GPU_BufferSlot> 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 struct Mesh
{ {
uint32_t vertexCount = 0; size_t verticesBufferPosition = 0;
std::vector<GPU_Vertex> vertices;
}; };
struct RenderObject struct RenderObject
{ {
uint32_t engineId = UINT32_MAX;
// Multiple transforms for batching // Multiple transforms for batching
std::vector<Transform> transforms; std::vector<Transform> transforms;
uint8_t depth = 0; uint8_t depth = 0;
Mesh mesh; //TODO: mesh (vertex/index buffer) Mesh mesh;
bool isUI = false;
//TODO: texture //TODO: texture
AssetMaterial &material; AssetMaterial &material;
@ -69,12 +150,14 @@ struct RenderCommand
struct QueueFamiliesBundle struct QueueFamiliesBundle
{ {
uint32_t transfer = QUEUE_FAMILY_NO_VALUE;
uint32_t graphics = QUEUE_FAMILY_NO_VALUE; uint32_t graphics = QUEUE_FAMILY_NO_VALUE;
uint32_t present = QUEUE_FAMILY_NO_VALUE; uint32_t present = QUEUE_FAMILY_NO_VALUE;
}; };
struct QueuesBundle struct QueuesBundle
{ {
VkQueue transfer;
VkQueue graphics; VkQueue graphics;
VkQueue present; VkQueue present;
}; };
@ -115,6 +198,12 @@ struct DrawConfig
.swapchainCount = 1, .swapchainCount = 1,
.pResults = nullptr .pResults = nullptr
}; };
VkVertexInputBindingDescription vertexBindingConfig {
.binding = 0,
.stride = sizeof(GPU_Vertex),
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX
};
}; };
struct SurfaceInfo struct SurfaceInfo
@ -131,7 +220,7 @@ struct SurfaceInfo
class Renderer class Renderer
{ {
public: public:
Renderer(GLFWwindow *window, const AssetMaterial& baseMaterial); Renderer(GLFWwindow *window, const AssetMaterial& baseMaterial, const size_t &vertexBufferSize);
~Renderer(); ~Renderer();
void Start(); void Start();
@ -160,8 +249,16 @@ private:
VkDevice device; VkDevice device;
VkSwapchainKHR swapchain; VkSwapchainKHR swapchain;
VkRenderPass baseRenderPass; VkRenderPass baseRenderPass;
VkCommandPool commandPool = nullptr; VkCommandPool graphicsCommandPool = nullptr;
std::vector<VkCommandBuffer> commandBuffers; VkCommandPool transferCommandPool = nullptr;
VkDeviceMemory worldVerticesMemory;
VkDeviceMemory stagingWorldVerticesMemory;
VkDeviceMemory uiVerticesMemory;
VkBuffer worldVertices;
VkBuffer stagingWorldVertices;
VkBuffer uiVertices;
std::vector<VkCommandBuffer> graphicsCommandBuffers;
std::vector<VkCommandBuffer> transferCommandBuffers;
std::vector<VkImage> frames; std::vector<VkImage> frames;
std::vector<VkImageView> frameViews; std::vector<VkImageView> frameViews;
std::vector<VkFramebuffer> baseFrameBuffers; std::vector<VkFramebuffer> baseFrameBuffers;
@ -175,14 +272,20 @@ private:
QueuesBundle queues; QueuesBundle queues;
DrawConfig drawConfig; DrawConfig drawConfig;
PipelineFactory *pipelineFactory; PipelineFactory *pipelineFactory;
PipelineBinding pipelineBinding;
size_t currentFrame = 0; size_t currentFrame = 0;
GPU_Buffer<GPU_Vertex> worldVerticesBinded;
GPU_Buffer<GPU_Vertex> uiVerticesBinded;
const float DEFAULT_QUEUE_PRIORITY = 1.0f; const float DEFAULT_QUEUE_PRIORITY = 1.0f;
const AssetMaterial &baseMaterial; const AssetMaterial &baseMaterial;
const size_t MAX_FRAMES_IN_FLIGHT = 2; const size_t MAX_FRAMES_IN_FLIGHT = 2;
const size_t VERTEX_BUFFER_SIZE;
std::set<RenderObject> objects; std::set<RenderObject> objects;
Rml::Context *uiContext; //Rml::Context *uiContext;
void InitVulkan(); void InitVulkan();
void SetupDevice(); void SetupDevice();
@ -190,10 +293,13 @@ private:
void RecreateSwapchain(); void RecreateSwapchain();
void ReleaseSwapchain(); void ReleaseSwapchain();
void SetupRenderPasses(); void SetupRenderPasses();
void SetupBuffers(); void SetupPipelines();
void SetupObjectsBuffers();
void SetupRenderBuffers();
void SetupSync(); void SetupSync();
void Draw(); void Draw();
void CopyBuffer(VkBuffer src, size_t offset, VkBuffer dst, VkDeviceSize size);
void Threading(); void Threading();
void Request(const RenderCommand &command); void Request(const RenderCommand &command);

View File

@ -22,14 +22,13 @@ PipelineConfig PipelineConfig::Copy() const
return copy; return copy;
} }
PipelineFactory::PipelineFactory( PipelineFactory::PipelineFactory(const PipelineBinding &binding) :
const AssetMaterial &baseMaterial, device(*binding.device),
const VkRenderPass &baseRenderPass, surfaceExtent(*binding.surfaceExtent),
VkDevice &device, vertexBindingConfig(*binding.vertexBindingConfig)
VkExtent2D &surfaceExtent
) :
device(device), surfaceExtent(surfaceExtent)
{ {
this->binding = binding;
basePipelineConfig.dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT); basePipelineConfig.dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT);
basePipelineConfig.dynamicStates.push_back(VK_DYNAMIC_STATE_SCISSOR); basePipelineConfig.dynamicStates.push_back(VK_DYNAMIC_STATE_SCISSOR);
@ -47,10 +46,10 @@ PipelineFactory::PipelineFactory(
basePipelineConfig.vertexInputConfig = { basePipelineConfig.vertexInputConfig = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.vertexBindingDescriptionCount = 0, .vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = nullptr, .pVertexBindingDescriptions = &vertexBindingConfig,
.vertexAttributeDescriptionCount = 0, .vertexAttributeDescriptionCount = binding.vertexAttributesCount,
.pVertexAttributeDescriptions = nullptr .pVertexAttributeDescriptions = binding.vertexAttributesConfig
}; };
basePipelineConfig.inputAssemblyConfig = { basePipelineConfig.inputAssemblyConfig = {
@ -146,7 +145,7 @@ PipelineFactory::PipelineFactory(
}; };
LOG(INFO, "Creating base material pipeline..."); 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"); LOG(INFO, "Base material pipeline created");
} }

View File

@ -11,13 +11,16 @@
bool operator < (const RenderObject& lhs, const RenderObject& rhs) 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), isRunning(false),
isDrawing(false), isDrawing(false),
baseMaterial(baseMaterial) baseMaterial(baseMaterial),
VERTEX_BUFFER_SIZE(vertexBufferSize)
{ {
this->window = window; this->window = window;
int width = -1, height = -1; int width = -1, height = -1;
@ -180,6 +183,7 @@ void Renderer::SetupDevice()
queueFamiliesBuffer.present == QUEUE_FAMILY_NO_VALUE queueFamiliesBuffer.present == QUEUE_FAMILY_NO_VALUE
) )
continue; continue;
queueFamiliesBuffer.transfer = queueFamiliesBuffer.graphics;
// Check surface info // Check surface info
SurfaceInfo surfaceInfoBuffer {}; SurfaceInfo surfaceInfoBuffer {};
@ -272,8 +276,10 @@ void Renderer::SetupDevice()
} }
// Define queues // Define queues
//TODO: transfer is separate queue for async loading
std::vector<VkDeviceQueueCreateInfo> queuesCreateConfigs; std::vector<VkDeviceQueueCreateInfo> queuesCreateConfigs;
std::set<uint32_t> uniqueQueueFamilies { std::set<uint32_t> uniqueQueueFamilies {
queueFamilies.transfer,
queueFamilies.graphics, queueFamilies.graphics,
queueFamilies.present 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); else LOG(INFO, "%hs is selected for render with %d priority score", deviceProperties.deviceName, best_priority);
// Save queues descriptor // Save queues descriptor
vkGetDeviceQueue(device, queueFamilies.transfer, 0, &queues.transfer);
vkGetDeviceQueue(device, queueFamilies.graphics, 0, &queues.graphics); vkGetDeviceQueue(device, queueFamilies.graphics, 0, &queues.graphics);
vkGetDeviceQueue(device, queueFamilies.present, 0, &queues.present); vkGetDeviceQueue(device, queueFamilies.present, 0, &queues.present);
} }
@ -430,8 +437,8 @@ void Renderer::RecreateSwapchain()
SetupSwapchain(); SetupSwapchain();
SetupRenderPasses(); SetupRenderPasses();
pipelineFactory = Allocate<PipelineFactory>(baseMaterial, baseRenderPass, device, surfaceInfo.extent); SetupPipelines();
SetupBuffers(); SetupRenderBuffers();
} }
void Renderer::ReleaseSwapchain() void Renderer::ReleaseSwapchain()
@ -442,9 +449,15 @@ void Renderer::ReleaseSwapchain()
} }
vkFreeCommandBuffers( vkFreeCommandBuffers(
device, device,
commandPool, graphicsCommandPool,
static_cast<uint32_t>(commandBuffers.size()), static_cast<uint32_t>(graphicsCommandBuffers.size()),
commandBuffers.data() graphicsCommandBuffers.data()
);
vkFreeCommandBuffers(
device,
transferCommandPool,
static_cast<uint32_t>(transferCommandBuffers.size()),
transferCommandBuffers.data()
); );
vkDestroyRenderPass(device, baseRenderPass, nullptr); vkDestroyRenderPass(device, baseRenderPass, nullptr);
@ -509,7 +522,162 @@ void Renderer::SetupRenderPasses()
LOG(CRITICAL, "Failed to create base render pass. Code: %d", vkResult); 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<uint32_t>(vertexAttributes.size()),
.vertexAttributesConfig = vertexAttributes.data()
};
pipelineFactory = Allocate<PipelineFactory>(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<void**>(&(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<void**>(&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; VkResult vkResult;
@ -537,7 +705,7 @@ void Renderer::SetupBuffers()
i++; i++;
} }
if (commandPool == nullptr) if (graphicsCommandPool == nullptr)
{ {
VkCommandPoolCreateInfo commandPoolConfig { VkCommandPoolCreateInfo commandPoolConfig {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
@ -546,22 +714,48 @@ void Renderer::SetupBuffers()
.queueFamilyIndex = queueFamilies.graphics, .queueFamilyIndex = queueFamilies.graphics,
}; };
vkResult = vkCreateCommandPool(device, &commandPoolConfig, nullptr, &commandPool); vkResult = vkCreateCommandPool(device, &commandPoolConfig, nullptr, &graphicsCommandPool);
if (vkResult != VK_SUCCESS) 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 { VkCommandBufferAllocateInfo allocateConfig {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.pNext = nullptr, .pNext = nullptr,
.commandPool = commandPool, .commandPool = graphicsCommandPool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, //TODO: reuse some commands using SECONDARY .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, //TODO: reuse some commands using SECONDARY
.commandBufferCount = static_cast<uint32_t>(commandBuffers.size()) .commandBufferCount = static_cast<uint32_t>(graphicsCommandBuffers.size())
}; };
vkResult = vkAllocateCommandBuffers(device, &allocateConfig, commandBuffers.data()); vkResult = vkAllocateCommandBuffers(device, &allocateConfig, graphicsCommandBuffers.data());
if (vkResult != VK_SUCCESS) 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<uint32_t>(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() void Renderer::Draw()
@ -604,10 +798,10 @@ void Renderer::Draw()
} }
imagesInFlight[imageIndex] = inFlightFences[currentFrame]; imagesInFlight[imageIndex] = inFlightFences[currentFrame];
vkResult = vkResetCommandBuffer(commandBuffers[currentFrame], 0); vkResult = vkResetCommandBuffer(graphicsCommandBuffers[currentFrame], 0);
if (vkResult != VK_SUCCESS) if (vkResult != VK_SUCCESS)
LOG(ERROR, "Failed to reset command buffer. Code: %d", vkResult); 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) if (vkResult != VK_SUCCESS)
LOG(ERROR, "Failed to begin command buffer. Code: %d", vkResult); LOG(ERROR, "Failed to begin command buffer. Code: %d", vkResult);
@ -619,7 +813,7 @@ void Renderer::Draw()
}; };
vkCmdBeginRenderPass( vkCmdBeginRenderPass(
commandBuffers[currentFrame], graphicsCommandBuffers[currentFrame],
&drawConfig.renderPassBeginConfig, &drawConfig.renderPassBeginConfig,
VK_SUBPASS_CONTENTS_INLINE VK_SUBPASS_CONTENTS_INLINE
); );
@ -632,26 +826,41 @@ void Renderer::Draw()
if (pipeline != lastPipeline) if (pipeline != lastPipeline)
{ {
vkCmdBindPipeline( vkCmdBindPipeline(
commandBuffers[currentFrame], graphicsCommandBuffers[currentFrame],
VK_PIPELINE_BIND_POINT_GRAPHICS, VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline->instance pipeline->instance
); );
vkCmdSetViewport(commandBuffers[currentFrame], 0, 1, &pipeline->viewport); vkCmdSetViewport(graphicsCommandBuffers[currentFrame], 0, 1, &pipeline->viewport);
vkCmdSetScissor(commandBuffers[currentFrame], 0, 1, &pipeline->scissors); vkCmdSetScissor(graphicsCommandBuffers[currentFrame], 0, 1, &pipeline->scissors);
lastPipeline = pipeline; lastPipeline = pipeline;
} }
std::vector<VkBuffer> 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( vkCmdDraw(
commandBuffers[currentFrame], graphicsCommandBuffers[currentFrame],
object.mesh.vertexCount, static_cast<uint32_t>(object.mesh.vertices.size()),
static_cast<uint32_t>(object.transforms.size()), static_cast<uint32_t>(object.transforms.size()),
0, // TODO: batching 0, // TODO: batching
0 // TODO: batching 0 // TODO: batching
); );
} }
vkCmdEndRenderPass(commandBuffers[currentFrame]); vkCmdEndRenderPass(graphicsCommandBuffers[currentFrame]);
vkResult = vkEndCommandBuffer(commandBuffers[currentFrame]); vkResult = vkEndCommandBuffer(graphicsCommandBuffers[currentFrame]);
if (vkResult != VK_SUCCESS) if (vkResult != VK_SUCCESS)
LOG(ERROR, "Failed to end command buffer. Code: %d", vkResult); LOG(ERROR, "Failed to end command buffer. Code: %d", vkResult);
@ -662,7 +871,7 @@ void Renderer::Draw()
drawConfig.submitConfig.pWaitSemaphores = waitSemaphores; drawConfig.submitConfig.pWaitSemaphores = waitSemaphores;
//drawConfig.submitConfig.pSignalSemaphores = &renderFinished[currentFrame]; //drawConfig.submitConfig.pSignalSemaphores = &renderFinished[currentFrame];
drawConfig.submitConfig.pSignalSemaphores =signalSemaphores; drawConfig.submitConfig.pSignalSemaphores =signalSemaphores;
drawConfig.submitConfig.pCommandBuffers = &commandBuffers[currentFrame]; drawConfig.submitConfig.pCommandBuffers = &graphicsCommandBuffers[currentFrame];
drawConfig.submitConfig.pWaitDstStageMask = waitStages; drawConfig.submitConfig.pWaitDstStageMask = waitStages;
vkResult = vkResetFences(device, 1, &inFlightFences[currentFrame]); vkResult = vkResetFences(device, 1, &inFlightFences[currentFrame]);
@ -699,6 +908,54 @@ void Renderer::Draw()
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; 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, &copyRegion);
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() void Renderer::SetupSync()
{ {
VkResult vkResult; VkResult vkResult;
@ -764,10 +1021,10 @@ void Renderer::Init()
SetupDevice(); SetupDevice();
SetupSwapchain(); SetupSwapchain();
SetupRenderPasses(); SetupRenderPasses();
SetupPipelines();
pipelineFactory = Allocate<PipelineFactory>(baseMaterial, baseRenderPass, device, surfaceInfo.extent); SetupObjectsBuffers();
SetupRenderBuffers();
SetupBuffers(); CopyBuffer(stagingWorldVertices, 0, worldVertices, worldVerticesBinded.size * worldVerticesBinded.ELEMENT_SIZE);
SetupSync(); SetupSync();
} }
@ -803,7 +1060,23 @@ void Renderer::ProcessCommand(const RenderCommand &command)
for (size_t i = 0; i < command.objectsSize; i++) for (size_t i = 0; i < command.objectsSize; i++)
{ {
objects.insert(command.objects[i]); RenderObject &object = command.objects[i];
object.engineId = static_cast<uint32_t>(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; break;
@ -864,6 +1137,17 @@ void Renderer::Release()
ReleaseSwapchain(); 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++) for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{ {
vkDestroySemaphore(device, renderFinished[i], nullptr); vkDestroySemaphore(device, renderFinished[i], nullptr);
@ -871,7 +1155,8 @@ void Renderer::Release()
vkDestroyFence(device, inFlightFences[i], nullptr); vkDestroyFence(device, inFlightFences[i], nullptr);
} }
vkDestroyCommandPool(device, commandPool, nullptr); vkDestroyCommandPool(device, graphicsCommandPool, nullptr);
vkDestroyCommandPool(device, transferCommandPool, nullptr);
vkDestroyDevice(device, nullptr); vkDestroyDevice(device, nullptr);
vkDestroySurfaceKHR(vulkan, surface, nullptr); vkDestroySurfaceKHR(vulkan, surface, nullptr);

View File

@ -35,7 +35,7 @@ int main(int argc, char **argv)
assetManager.LoadAsync(AssetInfo::MATERIAL, "assets/materials/helloworld.mat"); assetManager.LoadAsync(AssetInfo::MATERIAL, "assets/materials/helloworld.mat");
AssetHandle baseMaterialHandle = assetManager.Find("assets/materials/helloworld.mat"); AssetHandle baseMaterialHandle = assetManager.Find("assets/materials/helloworld.mat");
AssetMaterial baseMaterial = assetManager.Get<AssetMaterial>(baseMaterialHandle); AssetMaterial baseMaterial = assetManager.Get<AssetMaterial>(baseMaterialHandle);
Renderer renderer(window, baseMaterial); Renderer renderer(window, baseMaterial, 1000000); //TODO: buffer sizes
renderer.Start(); renderer.Start();
//Setup Engine //Setup Engine
@ -49,11 +49,30 @@ int main(int argc, char **argv)
// Test render obj // Test render obj
RenderObject triangle { RenderObject triangle {
.transforms = {{.x = 0, .y = 0}}, .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, .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 // Process GLFW events
while(!glfwWindowShouldClose(window)) while(!glfwWindowShouldClose(window))

View File

@ -1,21 +1,12 @@
#version 450 #version 450
#extension GL_ARB_separate_shader_objects : enable #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; 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() { void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); gl_Position = vec4(inPosition, 0.0, 1.0);
fragColor = colors[gl_VertexIndex]; fragColor = inColor;
} }

View File

@ -6,6 +6,7 @@
"entt", "entt",
"vulkan", "vulkan",
"glfw3", "glfw3",
"glm",
"rmlui", "rmlui",
"gtest" "gtest"
] ]