diff --git a/include/mbgl/vulkan/buffer_resource.hpp b/include/mbgl/vulkan/buffer_resource.hpp index 890d4b61d..e93b8944a 100644 --- a/include/mbgl/vulkan/buffer_resource.hpp +++ b/include/mbgl/vulkan/buffer_resource.hpp @@ -47,6 +47,7 @@ public: BufferResource& operator=(BufferResource&&) noexcept; BufferResource clone() const; + void destroy(bool deferred = true); void update(const void* data, std::size_t size, std::size_t offset) noexcept; diff --git a/include/mbgl/vulkan/context.hpp b/include/mbgl/vulkan/context.hpp index d0e267e7c..1b3f43fea 100644 --- a/include/mbgl/vulkan/context.hpp +++ b/include/mbgl/vulkan/context.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -150,7 +151,8 @@ public: uint8_t getCurrentFrameResourceIndex() const { return frameResourceIndex; } void enqueueDeletion(DeletionTask&& function); void submitOneTimeCommand(const std::function& function); - void submitOneTimeCommand(const vk::UniqueCommandPool& commandPool, + void submitOneTimeCommand(const vk::UniqueCommandBuffer& buffer); + void submitOneTimeCommand(const vk::UniqueCommandPool& pool, const std::function& function); void requestSurfaceUpdate(bool useDelay = true); @@ -212,6 +214,8 @@ private: #if DYNAMIC_TEXTURE_VULKAN_MULTITHREADED_UPLOAD std::mutex graphicsQueueSubmitMutex; #endif + + MBGL_STORE_THREAD(tid); }; } // namespace vulkan diff --git a/include/mbgl/vulkan/dynamic_texture.hpp b/include/mbgl/vulkan/dynamic_texture.hpp index 42692b4ad..061c2fecd 100644 --- a/include/mbgl/vulkan/dynamic_texture.hpp +++ b/include/mbgl/vulkan/dynamic_texture.hpp @@ -3,7 +3,7 @@ #include #include -#define DYNAMIC_TEXTURE_VULKAN_MULTITHREADED_UPLOAD 1 +#define DYNAMIC_TEXTURE_VULKAN_MULTITHREADED_UPLOAD 0 namespace mbgl { namespace vulkan { @@ -15,20 +15,22 @@ using UniqueBufferAllocation = std::unique_ptr; class DynamicTexture : public gfx::DynamicTexture { public: DynamicTexture(Context& context, Size size, gfx::TexturePixelType pixelType); + ~DynamicTexture() override; void uploadImage(const uint8_t* pixelData, gfx::TextureHandle& texHandle) override; void uploadDeferredImages() override; bool removeTexture(const gfx::TextureHandle& texHandle) override; #if DYNAMIC_TEXTURE_VULKAN_MULTITHREADED_UPLOAD - using TexturesToBlit = std::unordered_map; + using TexturesToBlit = + std::unordered_map, gfx::TextureHandle::Hasher>; #else using TextureBuffersToUpload = std::unordered_map; #endif private: - Context& context; + vulkan::Context& context; #if DYNAMIC_TEXTURE_VULKAN_MULTITHREADED_UPLOAD TexturesToBlit texturesToBlit; diff --git a/include/mbgl/vulkan/texture2d.hpp b/include/mbgl/vulkan/texture2d.hpp index 30a303635..fb3230a9d 100644 --- a/include/mbgl/vulkan/texture2d.hpp +++ b/include/mbgl/vulkan/texture2d.hpp @@ -65,6 +65,7 @@ public: bool isModifiedAfter(const std::chrono::duration& t) const { return t < lastModified; } void create() noexcept override; + void destroy(bool deferred = true) noexcept; void upload() noexcept override; void upload(const void* pixelData, const Size& size_) noexcept override; @@ -74,7 +75,7 @@ public: uint16_t xOffset, uint16_t yOffset, const vk::UniqueCommandBuffer& buffer, - std::vector>* deletionQueue = nullptr) noexcept; + bool submit) noexcept; bool needsUpload() const noexcept override { return !!imageData; }; @@ -96,8 +97,8 @@ private: void createTexture(); void createSampler(); - void destroyTexture(); - void destroySampler(); + void destroyTexture(bool deferred = true); + void destroySampler(bool deferred = true); void transitionToTransferWriteLayout(const vk::UniqueCommandBuffer&); void transitionToTransferReadLayout(const vk::UniqueCommandBuffer&); diff --git a/src/mbgl/vulkan/buffer_resource.cpp b/src/mbgl/vulkan/buffer_resource.cpp index 655fbcce0..a7a3478f3 100644 --- a/src/mbgl/vulkan/buffer_resource.cpp +++ b/src/mbgl/vulkan/buffer_resource.cpp @@ -128,6 +128,10 @@ BufferResource::BufferResource(BufferResource&& other) noexcept } BufferResource::~BufferResource() noexcept { + destroy(true); +} + +void BufferResource::destroy(bool deferred) { if (isValid()) { context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { stats.numBuffers--; @@ -140,9 +144,15 @@ BufferResource::~BufferResource() noexcept { }); } - if (!bufferAllocation) return; + if (!bufferAllocation) { + return; + } - context.enqueueDeletion([allocation = std::move(bufferAllocation)](auto&) mutable { allocation.reset(); }); + if (deferred) { + context.enqueueDeletion([allocation = std::move(bufferAllocation)](auto&) mutable { allocation.reset(); }); + } else { + bufferAllocation.reset(); + } } BufferResource BufferResource::clone() const { diff --git a/src/mbgl/vulkan/context.cpp b/src/mbgl/vulkan/context.cpp index b6315d05e..2695c376c 100644 --- a/src/mbgl/vulkan/context.cpp +++ b/src/mbgl/vulkan/context.cpp @@ -66,6 +66,8 @@ Context::Context(RendererBackend& backend_) } Context::~Context() noexcept { + MBGL_VERIFY_THREAD(tid); + backend.getThreadPool().runRenderJobs(true /* closeQueue */); destroyResources(); @@ -143,6 +145,8 @@ void Context::initFrameResources() { } void Context::destroyResources() { + MBGL_VERIFY_THREAD(tid); + backend.getDevice()->waitIdle(backend.getDispatcher()); for (auto& frame : frameResources) { @@ -166,6 +170,8 @@ void Context::destroyResources() { } void Context::enqueueDeletion(DeletionTask&& function) { + MBGL_VERIFY_THREAD(tid); + if (frameResources.empty()) { function(*this); return; @@ -175,32 +181,19 @@ void Context::enqueueDeletion(DeletionTask&& function) { } void Context::submitOneTimeCommand(const std::function& function) { - submitOneTimeCommand({}, function); + submitOneTimeCommand(backend.getCommandPool(), function); } -void Context::submitOneTimeCommand(const vk::UniqueCommandPool& commandPool, - const std::function& function) { +void Context::submitOneTimeCommand(const vk::UniqueCommandBuffer& commandBuffer) { MLN_TRACE_FUNC(); + #if DYNAMIC_TEXTURE_VULKAN_MULTITHREADED_UPLOAD std::scoped_lock lock(graphicsQueueSubmitMutex); #endif - const vk::CommandBufferAllocateInfo allocateInfo( - commandPool ? commandPool.get() : backend.getCommandPool().get(), vk::CommandBufferLevel::ePrimary, 1); - const auto& device = backend.getDevice(); const auto& dispatcher = backend.getDispatcher(); - const auto& commandBuffers = device->allocateCommandBuffersUnique(allocateInfo, dispatcher); - auto& commandBuffer = commandBuffers.front(); - - backend.setDebugName(commandBuffer.get(), "OneTimeSubmitCommandBuffer"); - - commandBuffer->begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit), dispatcher); - function(commandBuffer); - commandBuffer->end(dispatcher); - const auto submitInfo = vk::SubmitInfo().setCommandBuffers(commandBuffer.get()); - const auto& fence = device->createFenceUnique(vk::FenceCreateInfo(vk::FenceCreateFlags()), nullptr, dispatcher); backend.getGraphicsQueue().submit(submitInfo, fence.get(), dispatcher); @@ -211,6 +204,22 @@ void Context::submitOneTimeCommand(const vk::UniqueCommandPool& commandPool, } } +void Context::submitOneTimeCommand(const vk::UniqueCommandPool& pool, + const std::function& function) { + const auto& device = backend.getDevice(); + const auto& dispatcher = backend.getDispatcher(); + + const vk::CommandBufferAllocateInfo allocateInfo(pool.get(), vk::CommandBufferLevel::ePrimary, 1); + const auto& commandBuffers = device->allocateCommandBuffersUnique(allocateInfo, dispatcher); + const auto& commandBuffer = commandBuffers.front(); + + commandBuffer->begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit), dispatcher); + function(commandBuffer); + commandBuffer->end(dispatcher); + + submitOneTimeCommand(commandBuffer); +} + void Context::requestSurfaceUpdate(bool useDelay) { if (surfaceUpdateRequested) { if (!useDelay) { @@ -226,6 +235,8 @@ void Context::requestSurfaceUpdate(bool useDelay) { void Context::waitFrame() const { MLN_TRACE_FUNC(); + MBGL_VERIFY_THREAD(tid); + const auto& device = backend.getDevice(); const auto& dispatcher = backend.getDispatcher(); auto& frame = frameResources[frameResourceIndex]; @@ -240,6 +251,7 @@ void Context::waitFrame() const { void Context::beginFrame() { MLN_TRACE_FUNC(); + MBGL_VERIFY_THREAD(tid); frameResourceIndex = (frameResourceIndex + 1) % frameResources.size(); @@ -330,6 +342,7 @@ void Context::endFrame() {} void Context::submitFrame() { MLN_TRACE_FUNC(); + #if DYNAMIC_TEXTURE_VULKAN_MULTITHREADED_UPLOAD std::scoped_lock lock(graphicsQueueSubmitMutex); #endif @@ -640,7 +653,7 @@ const std::unique_ptr& Context::getDummyTexture() { dummyTexture2D->setSize(size); submitOneTimeCommand([&](const vk::UniqueCommandBuffer& commandBuffer) { - dummyTexture2D->uploadSubRegion(data.data(), size, 0, 0, commandBuffer); + dummyTexture2D->uploadSubRegion(data.data(), size, 0, 0, commandBuffer, false); }); } diff --git a/src/mbgl/vulkan/dynamic_texture.cpp b/src/mbgl/vulkan/dynamic_texture.cpp index 612c2b423..797453258 100644 --- a/src/mbgl/vulkan/dynamic_texture.cpp +++ b/src/mbgl/vulkan/dynamic_texture.cpp @@ -17,30 +17,38 @@ DynamicTexture::DynamicTexture(Context& context_, Size size, gfx::TexturePixelTy createInfo, nullptr, context.getBackend().getDispatcher()); } +DynamicTexture::~DynamicTexture() { + for (auto& texture : texturesToBlit) { + texture.second->destroy(false); + } + + if (texture) { + static_cast(*texture).destroy(false); + } +} + void DynamicTexture::uploadImage(const uint8_t* pixelData, gfx::TextureHandle& texHandle) { std::scoped_lock lock(mutex); const auto& rect = texHandle.getRectangle(); const auto imageSize = Size(rect.w, rect.h); - gfx::Texture2DPtr textureToBlit = context.createTexture2D(); + auto textureToBlit = std::static_pointer_cast(context.createTexture2D()); texturesToBlit.emplace(texHandle, textureToBlit); - const auto& textureToBlitVK = static_cast(textureToBlit.get()); - textureToBlitVK->setSize(imageSize); - textureToBlitVK->setFormat(texture.get()->getFormat(), gfx::TextureChannelDataType::UnsignedByte); - textureToBlitVK->setUsage(Texture2DUsage::Blit); - textureToBlitVK->create(); + textureToBlit->setSize(imageSize); + textureToBlit->setFormat(texture.get()->getFormat(), gfx::TextureChannelDataType::UnsignedByte); + textureToBlit->setUsage(Texture2DUsage::Blit); + textureToBlit->create(); - DeletionQueue deletionQueue; - context.submitOneTimeCommand(commandPool, [&](const vk::UniqueCommandBuffer& commandBuffer) { - textureToBlitVK->uploadSubRegion(pixelData, imageSize, 0, 0, commandBuffer, &deletionQueue); - }); + const auto& device = context.getBackend().getDevice(); + const auto& dispatcher = context.getBackend().getDispatcher(); - for (const auto& function : deletionQueue) { - function(context); - } + const vk::CommandBufferAllocateInfo allocateInfo(commandPool.get(), vk::CommandBufferLevel::ePrimary, 1); + const auto& commandBuffers = device->allocateCommandBuffersUnique(allocateInfo, dispatcher); + const auto& commandBuffer = commandBuffers.front(); - deletionQueue.clear(); + commandBuffer->begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit), dispatcher); + textureToBlit->uploadSubRegion(pixelData, imageSize, 0, 0, commandBuffer, true); gfx::DynamicTexture::uploadImage(pixelData, texHandle); } @@ -49,7 +57,7 @@ void DynamicTexture::uploadDeferredImages() { std::scoped_lock lock(mutex); const auto& textureVK = static_cast(texture.get()); - context.submitOneTimeCommand([&](const vk::UniqueCommandBuffer& commandBuffer) { + context.submitOneTimeCommand(commandPool, [&](const vk::UniqueCommandBuffer& commandBuffer) { textureVK->transitionToTransferWriteLayout(commandBuffer); for (const auto& pair : texturesToBlit) { const auto& rect = pair.first.getRectangle(); @@ -69,13 +77,23 @@ void DynamicTexture::uploadDeferredImages() { } textureVK->transitionToShaderReadLayout(commandBuffer); }); + + for (auto& texture : texturesToBlit) { + texture.second->destroy(false); + } + texturesToBlit.clear(); } bool DynamicTexture::removeTexture(const gfx::TextureHandle& texHandle) { if (gfx::DynamicTexture::removeTexture(texHandle)) { std::scoped_lock lock(mutex); - texturesToBlit.erase(texHandle); + + const auto& tex = texturesToBlit.find(texHandle); + if (tex != texturesToBlit.end()) { + tex->second->destroy(false); + texturesToBlit.erase(tex); + } return true; } return false; @@ -88,6 +106,12 @@ DynamicTexture::DynamicTexture(Context& context_, Size size, gfx::TexturePixelTy texture->create(); } +DynamicTexture::~DynamicTexture() { + if (texture) { + static_cast(*texture).destroy(false); + } +} + void DynamicTexture::uploadImage(const uint8_t* pixelData, gfx::TextureHandle& texHandle) { std::scoped_lock lock(mutex); const auto& rect = texHandle.getRectangle(); @@ -142,11 +166,14 @@ void DynamicTexture::uploadDeferredImages() { region, context.getBackend().getDispatcher()); - context.renderingStats().numTextureUpdates++; - context.renderingStats().textureUpdateBytes += rect.w * rect.h * texture->getPixelStride(); + context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { + stats.numTextureUpdates++; + stats.textureUpdateBytes += rect.w * rect.h * texture->getPixelStride(); + }); } textureVK->transitionToShaderReadLayout(commandBuffer); }); + textureBuffersToUpload.clear(); } diff --git a/src/mbgl/vulkan/texture2d.cpp b/src/mbgl/vulkan/texture2d.cpp index 067df90d6..4143cff93 100644 --- a/src/mbgl/vulkan/texture2d.cpp +++ b/src/mbgl/vulkan/texture2d.cpp @@ -42,8 +42,7 @@ Texture2D::Texture2D(Context& context_) : context(context_) {} Texture2D::~Texture2D() { - destroyTexture(); - destroySampler(); + destroy(true); } gfx::Texture2D& Texture2D::setSamplerConfiguration(const SamplerState& samplerState_) noexcept { @@ -132,6 +131,11 @@ void Texture2D::create() noexcept { } } +void Texture2D::destroy(bool deferred) noexcept { + destroyTexture(deferred); + destroySampler(deferred); +} + void Texture2D::upload() noexcept { if (!imageData) return; @@ -151,7 +155,7 @@ void Texture2D::uploadSubRegion(const void* pixelData, const Size& size_, uint16 const auto& encoder = context.createCommandEncoder(); const auto& encoderImpl = static_cast(*encoder); - uploadSubRegion(pixelData, size_, xOffset, yOffset, encoderImpl.getCommandBuffer()); + uploadSubRegion(pixelData, size_, xOffset, yOffset, encoderImpl.getCommandBuffer(), false); } void Texture2D::uploadSubRegion(const void* pixelData, @@ -159,7 +163,7 @@ void Texture2D::uploadSubRegion(const void* pixelData, uint16_t xOffset, uint16_t yOffset, const vk::UniqueCommandBuffer& commandBuffer, - DeletionQueue* deletionQueue) noexcept { + bool submit) noexcept { if (!pixelData || size_.width == 0 || size_.height == 0) return; create(); @@ -189,39 +193,32 @@ void Texture2D::uploadSubRegion(const void* pixelData, vmaMapMemory(allocator, bufferAllocation->allocation, &bufferAllocation->mappedBuffer); memcpy(bufferAllocation->mappedBuffer, pixelData, bufferInfo.size); - const auto enqueueCommands = [&](const auto& buffer) { - transitionToTransferWriteLayout(buffer); + transitionToTransferWriteLayout(commandBuffer); - const auto region = vk::BufferImageCopy() - .setBufferOffset(0) - .setBufferRowLength(size_.width) - .setImageSubresource( - vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1)) - .setImageOffset(vk::Offset3D(xOffset, yOffset)) - .setImageExtent(vk::Extent3D(size_.width, size_.height, 1)); + const auto region = vk::BufferImageCopy() + .setBufferOffset(0) + .setBufferRowLength(size_.width) + .setImageSubresource(vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1)) + .setImageOffset(vk::Offset3D(xOffset, yOffset)) + .setImageExtent(vk::Extent3D(size_.width, size_.height, 1)); - buffer->copyBufferToImage( - bufferAllocation->buffer, imageAllocation->image, imageLayout, region, backend.getDispatcher()); + commandBuffer->copyBufferToImage( + bufferAllocation->buffer, imageAllocation->image, imageLayout, region, backend.getDispatcher()); - if (samplerState.mipmapped && textureUsage == Texture2DUsage::ShaderInput) { - generateMips(buffer); - } else if (textureUsage == Texture2DUsage::Blit) { - transitionToTransferReadLayout(buffer); - } else { - transitionToShaderReadLayout(buffer); - } - }; - - enqueueCommands(commandBuffer); - - const auto function = [buffAlloc = std::move(bufferAllocation)](auto&) mutable { - buffAlloc.reset(); - }; - - if (deletionQueue) { - deletionQueue->push_back(std::move(function)); + if (samplerState.mipmapped && textureUsage == Texture2DUsage::ShaderInput) { + generateMips(commandBuffer); + } else if (textureUsage == Texture2DUsage::Blit) { + transitionToTransferReadLayout(commandBuffer); } else { - context.enqueueDeletion(std::move(function)); + transitionToShaderReadLayout(commandBuffer); + } + + if (submit) { + commandBuffer->end(context.getBackend().getDispatcher()); + context.submitOneTimeCommand(commandBuffer); + bufferAllocation.reset(); + } else { + context.enqueueDeletion([buffAlloc = std::move(bufferAllocation)](auto&) mutable { buffAlloc.reset(); }); } context.threadSafeAccessRenderingStats([&](gfx::RenderingStats& stats) { @@ -429,9 +426,13 @@ void Texture2D::createSampler() { lastModified = util::MonotonicTimer::now(); } -void Texture2D::destroyTexture() { +void Texture2D::destroyTexture(bool deferred) { if (imageAllocation) { - context.enqueueDeletion([allocation = std::move(imageAllocation)](auto&) mutable { allocation.reset(); }); + if (deferred) { + context.enqueueDeletion([allocation = std::move(imageAllocation)](auto&) mutable { allocation.reset(); }); + } else { + imageAllocation.reset(); + } imageLayout = vk::ImageLayout::eUndefined; @@ -442,11 +443,16 @@ void Texture2D::destroyTexture() { } } -void Texture2D::destroySampler() { +void Texture2D::destroySampler(bool deferred) { if (sampler) { - context.enqueueDeletion([sampler_ = std::move(sampler)](auto& context_) mutable { - context_.getBackend().getDevice()->destroySampler(sampler_, nullptr, context_.getBackend().getDispatcher()); - }); + if (deferred) { + context.enqueueDeletion([sampler_ = std::move(sampler)](auto& context_) mutable { + context_.getBackend().getDevice()->destroySampler( + sampler_, nullptr, context_.getBackend().getDispatcher()); + }); + } else { + context.getBackend().getDevice()->destroySampler(sampler, nullptr, context.getBackend().getDispatcher()); + } sampler = nullptr; }