Skip to content

Commit

Permalink
Fix in-flight frame update Vulkan (#3122)
Browse files Browse the repository at this point in the history
  • Loading branch information
adrian-cojocaru authored Jan 9, 2025
1 parent f700a41 commit c1f76cf
Show file tree
Hide file tree
Showing 11 changed files with 210 additions and 102 deletions.
14 changes: 9 additions & 5 deletions include/mbgl/vulkan/buffer_resource.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,18 @@ class BufferResource {
void update(const void* data, std::size_t size, std::size_t offset) noexcept;

std::size_t getSizeInBytes() const noexcept { return size; }
const void* contents() const noexcept { return (raw.empty() ? nullptr : raw.data()); }
const void* contents() const noexcept;
const void* contents(uint8_t resourceIndex) const noexcept;

Context& getContext() const noexcept { return context; }
const vk::Buffer& getVulkanBuffer() const noexcept { return bufferAllocation->buffer; }
std::size_t getVulkanBufferOffset() const noexcept;
std::size_t getVulkanBufferSize() const noexcept;
std::size_t getVulkanBufferOffset(uint8_t resourceIndex) const noexcept;
// update the current sub-buffer with the latest data
void updateVulkanBuffer();
void updateVulkanBuffer(const int8_t destination, const uint8_t source);

bool isValid() const noexcept { return !raw.empty(); }
bool isValid() const noexcept { return !!bufferAllocation; }
operator bool() const noexcept { return isValid(); }
bool operator!() const noexcept { return !isValid(); }

Expand All @@ -69,14 +73,14 @@ class BufferResource {

protected:
Context& context;
std::vector<std::uint8_t> raw;
std::size_t size;
std::uint32_t usage;
std::uint16_t version = 0;
VersionType version = 0;
bool persistent;

SharedBufferAllocation bufferAllocation;
size_t bufferWindowSize = 0;
std::vector<VersionType> bufferWindowVersions;
};

} // namespace vulkan
Expand Down
8 changes: 2 additions & 6 deletions include/mbgl/vulkan/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,7 @@ class Context final : public gfx::Context {
RenderStaticData& staticData,
const std::vector<shaders::ClipUBO>& tileUBOs);

const std::unique_ptr<BufferResource>& getDummyVertexBuffer();
const std::unique_ptr<BufferResource>& getDummyUniformBuffer();
const std::unique_ptr<BufferResource>& getDummyStorageBuffer();
const std::unique_ptr<BufferResource>& getDummyBuffer();
const std::unique_ptr<Texture2D>& getDummyTexture();

const vk::DescriptorSetLayout& getDescriptorSetLayout(DescriptorSetType type);
Expand Down Expand Up @@ -190,9 +188,7 @@ class Context final : public gfx::Context {
vulkan::UniformBufferArray globalUniformBuffers;
std::unordered_map<DescriptorSetType, DescriptorPoolGrowable> descriptorPoolMap;

std::unique_ptr<BufferResource> dummyVertexBuffer;
std::unique_ptr<BufferResource> dummyUniformBuffer;
std::unique_ptr<BufferResource> dummyStorageBuffer;
std::unique_ptr<BufferResource> dummyBuffer;
std::unique_ptr<Texture2D> dummyTexture2D;
vk::UniqueDescriptorSetLayout globalUniformDescriptorSetLayout;
vk::UniqueDescriptorSetLayout layerUniformDescriptorSetLayout;
Expand Down
2 changes: 2 additions & 0 deletions include/mbgl/vulkan/renderer_backend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ class RendererBackend : public gfx::RendererBackend {

void startFrameCapture();
void endFrameCapture();
void setFrameCaptureLoop(bool value);
void triggerFrameCapture(uint32_t frameCount = 1, uint32_t frameDelay = 0);

protected:
std::unique_ptr<gfx::Context> createContext() override;
Expand Down
14 changes: 11 additions & 3 deletions include/mbgl/vulkan/uniform_buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class UniformBuffer final : public gfx::UniformBuffer {
~UniformBuffer() override;

const BufferResource& getBufferResource() const { return buffer; }
BufferResource& mutableBufferResource() { return buffer; }

UniformBuffer clone() const { return {buffer.clone()}; }

Expand All @@ -37,14 +38,21 @@ class UniformBufferArray final : public gfx::UniformBufferArray {
descriptorStorageCount(descriptorStorageCount_),
descriptorUniformCount(descriptorUniformCount_) {}

UniformBufferArray(UniformBufferArray&& other)
: gfx::UniformBufferArray(std::move(other)) {}
UniformBufferArray(UniformBufferArray&& other) noexcept
: gfx::UniformBufferArray(std::move(other)),
descriptorSetType(other.descriptorSetType),
descriptorStartIndex(other.descriptorStartIndex),
descriptorStorageCount(other.descriptorStorageCount),
descriptorUniformCount(other.descriptorUniformCount),
descriptorSet(std::move(other.descriptorSet)) {}

UniformBufferArray(const UniformBufferArray&) = delete;

UniformBufferArray& operator=(UniformBufferArray&& other) {
UniformBufferArray& operator=(UniformBufferArray&& other) noexcept {
gfx::UniformBufferArray::operator=(std::move(other));
return *this;
}

UniformBufferArray& operator=(const UniformBufferArray& other) {
gfx::UniformBufferArray::operator=(other);
return *this;
Expand Down
85 changes: 70 additions & 15 deletions src/mbgl/vulkan/buffer_resource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <mbgl/util/instrumentation.hpp>

#include <algorithm>
#include <numeric>

namespace mbgl {
namespace vulkan {
Expand Down Expand Up @@ -59,12 +60,25 @@ BufferResource::BufferResource(
if (usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT || usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) {
const auto& backend = context.getBackend();
const auto& deviceProps = backend.getDeviceProperties();
const auto& align = deviceProps.limits.minUniformBufferOffsetAlignment;

vk::DeviceSize align = 0;
if (usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) {
align = deviceProps.limits.minUniformBufferOffsetAlignment;
}

if (usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) {
align = align ? std::lcm(align, deviceProps.limits.minStorageBufferOffsetAlignment)
: deviceProps.limits.minStorageBufferOffsetAlignment;
}

bufferWindowSize = (size + align - 1) & ~(align - 1);

assert(bufferWindowSize != 0);

totalSize = bufferWindowSize * backend.getMaxFrames();
const auto frameCount = backend.getMaxFrames();
totalSize = bufferWindowSize * frameCount;

bufferWindowVersions = std::vector<std::uint16_t>(frameCount, 0);
}

const auto bufferInfo = vk::BufferCreateInfo()
Expand All @@ -87,9 +101,7 @@ BufferResource::BufferResource(
vmaMapMemory(allocator, bufferAllocation->allocation, &bufferAllocation->mappedBuffer);

if (data) {
raw.resize(size);
std::memcpy(raw.data(), data, size);
std::memcpy(static_cast<uint8_t*>(bufferAllocation->mappedBuffer) + getVulkanBufferOffset(), data, size);
update(data, size, 0);
}

if (isValid()) {
Expand All @@ -104,12 +116,13 @@ BufferResource::BufferResource(

BufferResource::BufferResource(BufferResource&& other) noexcept
: context(other.context),
raw(std::move(other.raw)),
size(other.size),
usage(other.usage),
version(other.version),
persistent(other.persistent),
bufferAllocation(std::move(other.bufferAllocation)),
bufferWindowSize(other.bufferWindowSize) {
bufferWindowSize(other.bufferWindowSize),
bufferWindowVersions(std::move(other.bufferWindowVersions)) {
other.bufferAllocation = nullptr;
}

Expand All @@ -134,7 +147,7 @@ BufferResource& BufferResource::operator=(BufferResource&& other) noexcept {
context.renderingStats().numBuffers--;
context.renderingStats().memBuffers -= size;
};
raw = std::move(other.raw);

size = other.size;
usage = other.usage;
persistent = other.persistent;
Expand All @@ -152,21 +165,63 @@ void BufferResource::update(const void* newData, std::size_t updateSize, std::si
return;
}

auto& stats = context.renderingStats();
uint8_t* data = static_cast<uint8_t*>(bufferAllocation->mappedBuffer) + getVulkanBufferOffset() + offset;
std::memcpy(data, newData, updateSize);

std::memcpy(raw.data() + offset, newData, updateSize);
std::memcpy(
static_cast<uint8_t*>(bufferAllocation->mappedBuffer) + getVulkanBufferOffset() + offset, newData, updateSize);
auto& stats = context.renderingStats();
stats.bufferUpdateBytes += updateSize;

stats.bufferUpdates++;
version++;

if (bufferWindowSize) {
const auto frameIndex = context.getCurrentFrameResourceIndex();
bufferWindowVersions[frameIndex] = version;
}
}

const void* BufferResource::contents() const noexcept {
return contents(context.getCurrentFrameResourceIndex());
}

const void* BufferResource::contents(uint8_t resourceIndex) const noexcept {
if (!isValid()) {
return nullptr;
}

return static_cast<uint8_t*>(bufferAllocation->mappedBuffer) + getVulkanBufferOffset(resourceIndex);
}

std::size_t BufferResource::getVulkanBufferOffset() const noexcept {
if (bufferWindowSize > 0) return 0;
return getVulkanBufferOffset(context.getCurrentFrameResourceIndex());
}

std::size_t BufferResource::getVulkanBufferOffset(std::uint8_t resourceIndex) const noexcept {
assert(context.getBackend().getMaxFrames() >= resourceIndex);
return bufferWindowSize ? resourceIndex * bufferWindowSize : 0;
}

void BufferResource::updateVulkanBuffer() {
const auto frameCount = context.getBackend().getMaxFrames();

const int8_t currentIndex = context.getCurrentFrameResourceIndex();
const int8_t prevIndex = currentIndex == 0 ? frameCount - 1 : currentIndex - 1;

updateVulkanBuffer(currentIndex, prevIndex);
}

void BufferResource::updateVulkanBuffer(const int8_t destination, const uint8_t source) {
if (!bufferWindowSize) {
return;
}

return context.getCurrentFrameResourceIndex() * bufferWindowSize;
if (bufferWindowVersions[destination] < bufferWindowVersions[source]) {
uint8_t* dstData = static_cast<uint8_t*>(bufferAllocation->mappedBuffer) + bufferWindowSize * destination;
uint8_t* srcData = static_cast<uint8_t*>(bufferAllocation->mappedBuffer) + bufferWindowSize * source;

std::memcpy(dstData, srcData, size);

bufferWindowVersions[destination] = bufferWindowVersions[source];
}
}

} // namespace vulkan
Expand Down
25 changes: 7 additions & 18 deletions src/mbgl/vulkan/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -563,25 +563,14 @@ bool Context::renderTileClippingMasks(gfx::RenderPass& renderPass,
return true;
}

const std::unique_ptr<BufferResource>& Context::getDummyVertexBuffer() {
if (!dummyVertexBuffer)
dummyVertexBuffer = std::make_unique<BufferResource>(
*this, nullptr, 16, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, false);
return dummyVertexBuffer;
}

const std::unique_ptr<BufferResource>& Context::getDummyUniformBuffer() {
if (!dummyUniformBuffer)
dummyUniformBuffer = std::make_unique<BufferResource>(
*this, nullptr, 16, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, false);
return dummyUniformBuffer;
}
const std::unique_ptr<BufferResource>& Context::getDummyBuffer() {
if (!dummyBuffer) {
const uint32_t usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
dummyBuffer = std::make_unique<BufferResource>(*this, nullptr, 16, usage, false);
}

const std::unique_ptr<BufferResource>& Context::getDummyStorageBuffer() {
if (!dummyStorageBuffer)
dummyStorageBuffer = std::make_unique<BufferResource>(
*this, nullptr, 16, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, false);
return dummyStorageBuffer;
return dummyBuffer;
}

const std::unique_ptr<Texture2D>& Context::getDummyTexture() {
Expand Down
3 changes: 1 addition & 2 deletions src/mbgl/vulkan/descriptor_set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,7 @@ void UniformDescriptorSet::update(const gfx::UniformBufferArray& uniforms,
.setOffset(bufferResource.getVulkanBufferOffset())
.setRange(bufferResource.getSizeInBytes());
} else {
const auto& dummyBuffer = index < descriptorStorageCount ? context.getDummyStorageBuffer()
: context.getDummyUniformBuffer();
const auto& dummyBuffer = context.getDummyBuffer();
descriptorBufferInfo.setBuffer(dummyBuffer->getVulkanBuffer()).setOffset(0).setRange(VK_WHOLE_SIZE);
}

Expand Down
84 changes: 43 additions & 41 deletions src/mbgl/vulkan/renderable_resource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ void SurfaceRenderableResource::initDepthStencil() {
.setViewType(vk::ImageViewType::e2D)
.setFormat(depthFormat)
.setComponents(vk::ComponentMapping()) // defaults to vk::ComponentSwizzle::eIdentity
.setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eDepth, 0, 1, 0, 1));
.setSubresourceRange(vk::ImageSubresourceRange(
vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil, 0, 1, 0, 1));

depthAllocation->imageView = device->createImageViewUnique(imageViewCreateInfo);

Expand Down Expand Up @@ -307,28 +308,29 @@ void SurfaceRenderableResource::init(uint32_t w, uint32_t h) {

// create render pass
const auto colorLayout = surface ? vk::ImageLayout::ePresentSrcKHR : vk::ImageLayout::eTransferSrcOptimal;
const auto colorAttachment = vk::AttachmentDescription(vk::AttachmentDescriptionFlags())
.setFormat(colorFormat)
.setSamples(vk::SampleCountFlagBits::e1)
.setLoadOp(vk::AttachmentLoadOp::eClear)
.setStoreOp(vk::AttachmentStoreOp::eStore)
.setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
.setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
.setInitialLayout(vk::ImageLayout::eUndefined)
.setFinalLayout(colorLayout);

const vk::AttachmentReference colorAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal);

const auto depthAttachment = vk::AttachmentDescription()
.setFormat(depthFormat)
.setSamples(vk::SampleCountFlagBits::e1)
.setLoadOp(vk::AttachmentLoadOp::eClear)
.setStoreOp(vk::AttachmentStoreOp::eDontCare)
.setStencilLoadOp(vk::AttachmentLoadOp::eClear)
.setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
.setInitialLayout(vk::ImageLayout::eUndefined)
.setFinalLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal);
const std::array<vk::AttachmentDescription, 2> attachments = {
vk::AttachmentDescription()
.setFormat(colorFormat)
.setSamples(vk::SampleCountFlagBits::e1)
.setLoadOp(vk::AttachmentLoadOp::eClear)
.setStoreOp(vk::AttachmentStoreOp::eStore)
.setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
.setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
.setInitialLayout(vk::ImageLayout::eUndefined)
.setFinalLayout(colorLayout),

vk::AttachmentDescription()
.setFormat(depthFormat)
.setSamples(vk::SampleCountFlagBits::e1)
.setLoadOp(vk::AttachmentLoadOp::eClear)
.setStoreOp(vk::AttachmentStoreOp::eDontCare)
.setStencilLoadOp(vk::AttachmentLoadOp::eClear)
.setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
.setInitialLayout(vk::ImageLayout::eUndefined)
.setFinalLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal)};

const vk::AttachmentReference colorAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal);
const vk::AttachmentReference depthAttachmentRef(1, vk::ImageLayout::eDepthStencilAttachmentOptimal);

const auto subpass = vk::SubpassDescription()
Expand All @@ -337,28 +339,28 @@ void SurfaceRenderableResource::init(uint32_t w, uint32_t h) {
.setColorAttachments(colorAttachmentRef)
.setPDepthStencilAttachment(&depthAttachmentRef);

const auto subpassSrcStageMask = vk::PipelineStageFlags() | vk::PipelineStageFlagBits::eColorAttachmentOutput |
vk::PipelineStageFlagBits::eLateFragmentTests;

const auto subpassDstStageMask = vk::PipelineStageFlags() | vk::PipelineStageFlagBits::eColorAttachmentOutput |
vk::PipelineStageFlagBits::eEarlyFragmentTests;

const auto subpassSrcAccessMask = vk::AccessFlags() | vk::AccessFlagBits::eDepthStencilAttachmentWrite;

const auto subpassDstAccessMask = vk::AccessFlags() | vk::AccessFlagBits::eColorAttachmentWrite |
vk::AccessFlagBits::eDepthStencilAttachmentWrite;

const auto subpassDependency = vk::SubpassDependency()
.setSrcSubpass(VK_SUBPASS_EXTERNAL)
.setDstSubpass(0)
.setSrcStageMask(subpassSrcStageMask)
.setDstStageMask(subpassDstStageMask)
.setSrcAccessMask(subpassSrcAccessMask)
.setDstAccessMask(subpassDstAccessMask);
const std::array<vk::SubpassDependency, 2> dependencies = {
vk::SubpassDependency()
.setSrcSubpass(VK_SUBPASS_EXTERNAL)
.setDstSubpass(0)
.setSrcStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
.setDstStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
.setSrcAccessMask({})
.setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite),

vk::SubpassDependency()
.setSrcSubpass(VK_SUBPASS_EXTERNAL)
.setDstSubpass(0)
.setSrcStageMask(vk::PipelineStageFlagBits::eEarlyFragmentTests |
vk::PipelineStageFlagBits::eLateFragmentTests)
.setDstStageMask(vk::PipelineStageFlagBits::eEarlyFragmentTests |
vk::PipelineStageFlagBits::eLateFragmentTests)
.setSrcAccessMask({})
.setDstAccessMask(vk::AccessFlagBits::eDepthStencilAttachmentWrite),
};

const std::array<vk::AttachmentDescription, 2> attachments = {colorAttachment, depthAttachment};
const auto renderPassCreateInfo =
vk::RenderPassCreateInfo().setAttachments(attachments).setSubpasses(subpass).setDependencies(subpassDependency);
vk::RenderPassCreateInfo().setAttachments(attachments).setSubpasses(subpass).setDependencies(dependencies);

renderPass = device->createRenderPassUnique(renderPassCreateInfo);

Expand Down
Loading

0 comments on commit c1f76cf

Please sign in to comment.