Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix in-flight frame update #3122

Merged
merged 8 commits into from
Jan 9, 2025
Merged
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
Loading