Skip to content
Snippets Groups Projects
Verified Commit cf35ba83 authored by Tobias Frisch's avatar Tobias Frisch
Browse files

[#38] Added staging buffer to fill buffers

parent f5a67f17
No related branches found
No related tags found
1 merge request!30Resolve "Buffer-Manager + Staging-Buffer"
Pipeline #25068 passed
......@@ -26,11 +26,11 @@ namespace vkcv {
return m_count * sizeof(T);
}
void fill(T* data, size_t count = SIZE_MAX, size_t offset = 0) {
void fill(T* data, size_t count = 0, size_t offset = 0) {
m_manager->fillBuffer(m_handle_id, data, count * sizeof(T), offset * sizeof(T));
}
T* map(size_t offset = 0, size_t count = SIZE_MAX) {
T* map(size_t offset = 0, size_t count = 0) {
return reinterpret_cast<T*>(m_manager->mapBuffer(m_handle_id, offset * sizeof(T), count * sizeof(T)));
}
......
......@@ -5,13 +5,14 @@
namespace vkcv
{
enum BufferType {
enum class BufferType {
VERTEX,
UNIFORM,
STORAGE
STORAGE,
STAGING
};
enum BufferMemoryType {
enum class BufferMemoryType {
DEVICE_LOCAL,
HOST_VISIBLE
};
......@@ -27,14 +28,19 @@ namespace vkcv
{
vk::Buffer m_handle;
vk::DeviceMemory m_memory;
size_t m_size;
void* m_mapped = nullptr;
bool m_mappable;
};
Core* m_core;
std::vector<Buffer> m_buffers;
uint64_t m_stagingBuffer;
BufferManager() noexcept;
void init();
public:
~BufferManager() noexcept;
......
......@@ -36,13 +36,22 @@ int main(int argc, const char** argv) {
float x, y, z;
};
auto buffer = core.createBuffer<vec3>(vkcv::BufferType::VERTEX, 3, vkcv::BufferMemoryType::HOST_VISIBLE);
const size_t n = 5027;
vec3* m = buffer.map();
auto buffer = core.createBuffer<vec3>(vkcv::BufferType::VERTEX, n, vkcv::BufferMemoryType::DEVICE_LOCAL);
vec3 vec_data [n];
for (size_t i = 0; i < n; i++) {
vec_data[i] = { 42, static_cast<float>(i), 7 };
}
buffer.fill(vec_data);
/*vec3* m = buffer.map();
m[0] = { 0, 0, 0 };
m[1] = { 0, 0, 0 };
m[2] = { 0, 0, 0 };
buffer.unmap();
buffer.unmap();*/
std::cout << "Physical device: " << physicalDevice.getProperties().deviceName << std::endl;
......
......@@ -9,8 +9,17 @@
namespace vkcv {
BufferManager::BufferManager() noexcept :
m_core(nullptr), m_buffers()
{}
m_core(nullptr), m_buffers(), m_stagingBuffer(UINT64_MAX)
{
}
void BufferManager::init() {
if (!m_core) {
return;
}
m_stagingBuffer = createBuffer(BufferType::STAGING, 1024 * 1024, BufferMemoryType::HOST_VISIBLE);
}
BufferManager::~BufferManager() noexcept {
for (size_t id = 0; id < m_buffers.size(); id++) {
......@@ -48,20 +57,27 @@ namespace vkcv {
vk::BufferUsageFlags usageFlags;
switch (type) {
case VERTEX:
case BufferType::VERTEX:
usageFlags = vk::BufferUsageFlagBits::eVertexBuffer;
break;
case UNIFORM:
case BufferType::UNIFORM:
usageFlags = vk::BufferUsageFlagBits::eUniformBuffer;
break;
case STORAGE:
case BufferType::STORAGE:
usageFlags = vk::BufferUsageFlagBits::eStorageBuffer;
break;
case BufferType::STAGING:
usageFlags = vk::BufferUsageFlagBits::eTransferSrc;
break;
default:
// TODO: maybe an issue
break;
}
if (memoryType == BufferMemoryType::DEVICE_LOCAL) {
usageFlags |= vk::BufferUsageFlagBits::eTransferDst;
}
const vk::Device& device = m_core->getContext().getDevice();
vk::Buffer buffer = device.createBuffer(
......@@ -72,13 +88,15 @@ namespace vkcv {
const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice();
vk::MemoryPropertyFlags memoryTypeFlags;
bool mappable = false;
switch (memoryType) {
case DEVICE_LOCAL:
case BufferMemoryType::DEVICE_LOCAL:
memoryTypeFlags = vk::MemoryPropertyFlagBits::eDeviceLocal;
break;
case HOST_VISIBLE:
case BufferMemoryType::HOST_VISIBLE:
memoryTypeFlags = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent;
mappable = true;
break;
default:
// TODO: maybe an issue
......@@ -91,14 +109,80 @@ namespace vkcv {
memoryTypeFlags
);
vk::DeviceMemory memory = device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryType));
vk::DeviceMemory memory = device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryTypeIndex));
device.bindBufferMemory(buffer, memory, 0);
const uint64_t id = m_buffers.size();
m_buffers.push_back({ buffer, memory, nullptr });
m_buffers.push_back({ buffer, memory, size, nullptr, mappable });
return id;
}
struct StagingStepInfo {
void* data;
size_t size;
size_t offset;
vk::Buffer buffer;
vk::Buffer stagingBuffer;
vk::DeviceMemory stagingMemory;
size_t stagingLimit;
size_t stagingPosition;
};
/**
* Copies data from CPU to a staging buffer and submits the commands to copy
* each part one after another into the actual target buffer.
*
* The function can be used fully asynchronously!
* Just be careful to not use the staging buffer in parallel!
*
* @param core Core instance
* @param info Staging-info structure
*/
void copyFromStagingBuffer(Core* core, StagingStepInfo& info) {
const size_t remaining = info.size - info.stagingPosition;
const size_t mapped_size = std::min(remaining, info.stagingLimit);
const vk::Device& device = core->getContext().getDevice();
void* mapped = device.mapMemory(info.stagingMemory, 0, mapped_size);
memcpy(mapped, reinterpret_cast<char*>(info.data) + info.stagingPosition, mapped_size);
device.unmapMemory(info.stagingMemory);
SubmitInfo submitInfo;
submitInfo.queueType = QueueType::Transfer;
core->submitCommands(
submitInfo,
[&info, &mapped_size](const vk::CommandBuffer& commandBuffer) {
const vk::BufferCopy region (
0,
info.offset + info.stagingPosition,
mapped_size
);
commandBuffer.copyBuffer(info.stagingBuffer, info.buffer, 1, &region);
},
[&core, &info, &mapped_size, &remaining]() {
if (mapped_size < remaining) {
info.stagingPosition += mapped_size;
copyFromStagingBuffer(
core,
info
);
}
}
);
}
void BufferManager::fillBuffer(uint64_t id, void *data, size_t size, size_t offset) {
if (size == 0) {
size = SIZE_MAX;
}
if (id >= m_buffers.size()) {
return;
}
......@@ -111,19 +195,42 @@ namespace vkcv {
const vk::Device& device = m_core->getContext().getDevice();
const vk::MemoryRequirements requirements = device.getBufferMemoryRequirements(buffer.m_handle);
if (offset > requirements.size) {
if (offset > buffer.m_size) {
return;
}
const size_t mapped_size = std::min(size, requirements.size - offset);
void* mapped = device.mapMemory(buffer.m_memory, offset, mapped_size);
memcpy(mapped, data, mapped_size);
device.unmapMemory(buffer.m_memory);
const size_t max_size = std::min(size, buffer.m_size - offset);
if (buffer.m_mappable) {
void* mapped = device.mapMemory(buffer.m_memory, offset, max_size);
memcpy(mapped, data, max_size);
device.unmapMemory(buffer.m_memory);
} else {
auto& stagingBuffer = m_buffers[m_stagingBuffer];
StagingStepInfo info;
info.data = data;
info.size = std::min(size, max_size - offset);
info.offset = offset;
info.buffer = buffer.m_handle;
info.stagingBuffer = stagingBuffer.m_handle;
info.stagingMemory = stagingBuffer.m_memory;
const vk::MemoryRequirements stagingRequirements = device.getBufferMemoryRequirements(stagingBuffer.m_handle);
info.stagingLimit = stagingRequirements.size;
info.stagingPosition = 0;
copyFromStagingBuffer(m_core, info);
}
}
void* BufferManager::mapBuffer(uint64_t id, size_t offset, size_t size) {
if (size == 0) {
size = SIZE_MAX;
}
if (id >= m_buffers.size()) {
return nullptr;
}
......@@ -136,14 +243,12 @@ namespace vkcv {
const vk::Device& device = m_core->getContext().getDevice();
const vk::MemoryRequirements requirements = device.getBufferMemoryRequirements(buffer.m_handle);
if (offset > requirements.size) {
if (offset > buffer.m_size) {
return nullptr;
}
const size_t mapped_size = std::min(size, requirements.size - offset);
buffer.m_mapped = device.mapMemory(buffer.m_memory, offset, mapped_size);
const size_t max_size = std::min(size, buffer.m_size - offset);
buffer.m_mapped = device.mapMemory(buffer.m_memory, offset, max_size);
return buffer.m_mapped;
}
......
......@@ -97,6 +97,7 @@ namespace vkcv
m_SyncResources(syncResources)
{
m_BufferManager->m_core = this;
m_BufferManager->init();
}
Core::~Core() noexcept {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment