/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
mCore->SceneCreated();
mCore->Initialize();
+ mCore->ProcessEvents(); // Ensure that scene messages are ready for next update/render.
}
TestApplication::~TestApplication()
${adaptor_graphics_dir}/vulkan-impl/vulkan-sampler.cpp
${adaptor_graphics_dir}/vulkan-impl/vulkan-sampler-impl.cpp
${adaptor_graphics_dir}/vulkan-impl/vulkan-texture.cpp
+ ${adaptor_graphics_dir}/vulkan-impl/vulkan-texture-dependency-checker.cpp
)
# module: graphics, backend: vulkan/x11
void CommandBuffer::Begin(const Graphics::CommandBufferBeginInfo& info)
{
mDynamicStateMask = CommandBuffer::INITIAL_DYNAMIC_MASK_VALUE;
+ mRenderTarget = ConstGraphicsCast<Vulkan::RenderTarget, Graphics::RenderTarget>(info.renderTarget);
+
if(mCommandBufferImpl)
{
vk::CommandBufferInheritanceInfo inheritanceInfo{};
if(info.renderPass)
{
- auto renderTarget = ConstGraphicsCast<Vulkan::RenderTarget, Graphics::RenderTarget>(info.renderTarget);
- inheritanceInfo.renderPass = renderTarget->GetRenderPass(info.renderPass)->GetVkHandle();
+ inheritanceInfo.renderPass = mRenderTarget->GetRenderPass(info.renderPass)->GetVkHandle();
inheritanceInfo.subpass = 0;
- inheritanceInfo.framebuffer = renderTarget->GetCurrentFramebufferImpl()->GetVkHandle();
+ inheritanceInfo.framebuffer = mRenderTarget->GetCurrentFramebufferImpl()->GetVkHandle();
inheritanceInfo.queryFlags = static_cast<vk::QueryControlFlags>(0);
inheritanceInfo.pipelineStatistics = static_cast<vk::QueryPipelineStatisticFlags>(0);
}
mCommandBufferImpl->Reset();
}
mDynamicStateMask = CommandBuffer::INITIAL_DYNAMIC_MASK_VALUE;
- mLastSwapchain = nullptr;
+ mRenderTarget = nullptr;
}
void CommandBuffer::BindVertexBuffers(uint32_t firstBinding,
void CommandBuffer::BindTextures(const std::vector<TextureBinding>& textureBindings)
{
mCommandBufferImpl->BindTextures(textureBindings);
+ mController.CheckTextureDependencies(textureBindings, mRenderTarget);
}
void CommandBuffer::BindSamplers(const std::vector<SamplerBinding>& samplerBindings)
Rect2D renderArea,
const std::vector<ClearValue>& clearValues)
{
- auto renderPass = static_cast<Vulkan::RenderPass*>(gfxRenderPass);
- auto renderTarget = static_cast<Vulkan::RenderTarget*>(gfxRenderTarget);
- auto surface = renderTarget->GetSurface();
- auto& device = mController.GetGraphicsDevice();
+ auto renderTarget = static_cast<Vulkan::RenderTarget*>(gfxRenderTarget);
+ DALI_ASSERT_DEBUG(mRenderTarget == renderTarget && "RenderPass has different render target to cmd buffer Begin");
+
+ auto renderPass = static_cast<Vulkan::RenderPass*>(gfxRenderPass);
+ auto surface = mRenderTarget->GetSurface();
+ auto& device = mController.GetGraphicsDevice();
FramebufferImpl* framebuffer = nullptr;
RenderPassHandle renderPassImpl;
if(surface)
{
framebuffer = swapchain->AcquireNextFramebuffer(true);
}
- assert(framebuffer && "Replacing invalid swapchain unsuccessful! Goodbye!");
+ DALI_ASSERT_ALWAYS(framebuffer && "Replacing invalid swapchain unsuccessful! Goodbye!");
}
renderPassImpl = framebuffer->GetImplFromRenderPass(renderPass);
}
else
{
- auto coreFramebuffer = renderTarget->GetFramebuffer();
+ auto coreFramebuffer = mRenderTarget->GetFramebuffer();
framebuffer = coreFramebuffer->GetImpl();
renderPassImpl = framebuffer->GetImplFromRenderPass(renderPass);
+ mController.AddTextureDependencies(mRenderTarget);
}
std::vector<vk::ClearValue> vkClearValues;
}
}
-Swapchain* CommandBuffer::GetLastSwapchain() const
+Vulkan::RenderTarget* CommandBuffer::GetRenderTarget() const
{
- return mLastSwapchain;
+ // Gets the render target from the Begin() cmd.
+ return mRenderTarget;
}
} // namespace Dali::Graphics::Vulkan
{
class CommandBufferImpl;
class Swapchain;
+class RenderTarget;
using CommandBufferResource = Resource<Graphics::CommandBuffer, Graphics::CommandBufferCreateInfo>;
public: // API
/**
- * Get the last swapchain referenced by a BeginRenderPass command in this command buffer.
- *
- * @todo Should split the command buffer up into multiple buffers if there is more than one
- * render target referenced in it.
+ * Get the last target referenced by a BeginRenderPass command in this command buffer.
+ * Core now splits up command buffers so that they contain 1 render target each.
*/
- Swapchain* GetLastSwapchain() const;
+ RenderTarget* GetRenderTarget() const;
- CommandBufferImpl* GetImpl() const
+ [[nodiscard]] CommandBufferImpl* GetImpl() const
{
return mCommandBufferImpl;
}
}
CommandBufferImpl* mCommandBufferImpl;
+ RenderTarget* mRenderTarget{nullptr};
Swapchain* mLastSwapchain{nullptr};
};
*
* @param[in] imageView The imageview of the attachment
* @param[in] clearColor The color used to clear this attachment during CLEAR_OP
+ * @param[in] description Expected Load/Store ops
* @param[in] type The attachment type (usually COLOR or DEPTH_STENCIL)
* @param[in] presentable Whether the attachment is presentable (changes final layout)
*/
- FramebufferAttachment(std::unique_ptr<ImageView>& imageView,
- vk::ClearValue clearColor,
- AttachmentType type,
- bool presentable);
+ FramebufferAttachment(
+ std::unique_ptr<ImageView>& imageView,
+ vk::ClearValue clearColor,
+ const Graphics::AttachmentDescription* description,
+ AttachmentType type,
+ bool presentable);
/**
* Creates a new color attachment.
*
* @param[in] imageView The imageview of the attachment
* @param[in] clearColorValue The color used to clear this attachment during CLEAR_OP
+ * @param[in] description Expected Load/Store ops
* @param[in] presentable Whether the attachment is presentable (changes final layout)
*/
- static FramebufferAttachment* NewColorAttachment(std::unique_ptr<ImageView>& imageView,
- vk::ClearColorValue clearColorValue,
- bool presentable);
+ static FramebufferAttachment* NewColorAttachment(
+ std::unique_ptr<ImageView>& imageView,
+ vk::ClearColorValue clearColorValue,
+ const Graphics::AttachmentDescription* description,
+ bool presentable);
/**
* Creates a new depth attachment.
*
* @param[in] imageView The imageview of the attachment
* @param[in] clearDepthStencilValue The value used to clear this attachment during CLEAR_OP
+ * @param[in] description Expected Load/Store ops
*/
- static FramebufferAttachment* NewDepthAttachment(std::unique_ptr<ImageView>& imageView,
- vk::ClearDepthStencilValue clearDepthStencilValue);
+ static FramebufferAttachment* NewDepthAttachment(
+ std::unique_ptr<ImageView>& imageView,
+ vk::ClearDepthStencilValue clearDepthStencilValue,
+ const Graphics::AttachmentDescription* description);
[[nodiscard]] ImageView* GetImageView() const;
std::unique_ptr<ImageView> mImageView;
vk::AttachmentDescription mDescription;
vk::ClearValue mClearValue;
- AttachmentType mType{AttachmentType::UNDEFINED};
+
+ AttachmentType mType{AttachmentType::UNDEFINED};
};
using FramebufferAttachmentHandle = Vulkan::Handle<FramebufferAttachment>; // Can share attachments
namespace Dali::Graphics::Vulkan
{
-FramebufferAttachment* FramebufferAttachment::NewColorAttachment(std::unique_ptr<ImageView>& imageView,
- vk::ClearColorValue clearColorValue,
- bool presentable)
+FramebufferAttachment* FramebufferAttachment::NewColorAttachment(
+ std::unique_ptr<ImageView>& imageView,
+ vk::ClearColorValue clearColorValue,
+ const Graphics::AttachmentDescription* description,
+ bool presentable)
{
assert(imageView->GetImage()->GetUsageFlags() & vk::ImageUsageFlagBits::eColorAttachment);
auto attachment = new FramebufferAttachment(imageView,
clearColorValue,
+ description,
AttachmentType::COLOR,
presentable);
return attachment;
}
FramebufferAttachment* FramebufferAttachment::NewDepthAttachment(
- std::unique_ptr<ImageView>& imageView,
- vk::ClearDepthStencilValue clearDepthStencilValue)
+ std::unique_ptr<ImageView>& imageView,
+ vk::ClearDepthStencilValue clearDepthStencilValue,
+ const Graphics::AttachmentDescription* description)
{
assert(imageView->GetImage()->GetUsageFlags() & vk::ImageUsageFlagBits::eDepthStencilAttachment);
auto attachment = new FramebufferAttachment(imageView,
clearDepthStencilValue,
+ description,
AttachmentType::DEPTH_STENCIL,
false /* presentable */);
return attachment;
}
-FramebufferAttachment::FramebufferAttachment(std::unique_ptr<ImageView>& imageView,
- vk::ClearValue clearColor,
- AttachmentType type,
- bool presentable)
+FramebufferAttachment::FramebufferAttachment(
+ std::unique_ptr<ImageView>& imageView,
+ vk::ClearValue clearColor,
+ const Graphics::AttachmentDescription* description,
+ AttachmentType type,
+ bool presentable)
: mClearValue(clearColor),
mType(type)
{
auto sampleCountFlags = image->GetSampleCount();
mDescription.setSamples(sampleCountFlags);
-
- mDescription.setLoadOp(vk::AttachmentLoadOp::eClear);
- mDescription.setStoreOp(vk::AttachmentStoreOp::eStore);
- mDescription.setStencilLoadOp(vk::AttachmentLoadOp::eClear);
- mDescription.setStencilStoreOp(vk::AttachmentStoreOp::eStore);
mDescription.setFormat(image->GetFormat());
mDescription.setInitialLayout(vk::ImageLayout::eUndefined);
+ if(description == nullptr)
+ {
+ mDescription.setLoadOp(vk::AttachmentLoadOp::eClear);
+ mDescription.setStoreOp(vk::AttachmentStoreOp::eStore);
+ mDescription.setStencilLoadOp(vk::AttachmentLoadOp::eClear);
+ mDescription.setStencilStoreOp(vk::AttachmentStoreOp::eStore);
+ }
+ else
+ {
+ mDescription.setLoadOp(VkLoadOpType(description->loadOp).loadOp);
+ mDescription.setStoreOp(VkStoreOpType(description->storeOp).storeOp);
+ mDescription.setStencilLoadOp(VkLoadOpType(description->stencilLoadOp).loadOp);
+ mDescription.setStencilStoreOp(VkStoreOpType(description->stencilStoreOp).storeOp);
+ }
if(type == AttachmentType::DEPTH_STENCIL)
{
if(createInfo.attachmentDescriptions[0].loadOp == VkLoadOpType(matchLoadOp).loadOp &&
createInfo.attachmentDescriptions[0].storeOp == VkStoreOpType(matchStoreOp).storeOp)
{
- // Point at passed in render pass... should be a weak ptr... What's lifecycle?!
element.renderPass = renderPass;
return element.renderPassImpl;
}
}
// @todo create new render pass from existing + load/store op, add it to mRenderPasses, and return it.
- // @todo Need to reconsider swapchain/fbo/renderpass creation model.
- // This framebuffer may belong to a swapchain, in which case, there are multiple framebuffers
- // that could share render passes.
- // A) Need to detect this situation - keep owner info?
- // B) Sharing render passes means we
- // 1) need to ref-count to ensure safe ownership, or
- // 2) move ownership of renderpass to swapchain.
- // Onus is on client to determine which interface to use, if it's a surface, use swapchain;
- // if it's an offscreen, use framebuffer. (Kinda need a core interface to wrap surface/offscreen)
+
return mRenderPasses[0].renderPassImpl;
}
+void FramebufferImpl::AddRenderPass(RenderPass* renderPass, Vulkan::RenderPassHandle renderPassImpl)
+{
+ bool found = false;
+ for(auto& element : mRenderPasses)
+ {
+ if(element.renderPassImpl == renderPassImpl && !element.renderPass)
+ {
+ element.renderPass = renderPass; // Update existing element with matched objects
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ {
+ mRenderPasses.emplace_back(RenderPassMapElement{renderPass, renderPassImpl});
+ }
+}
+
vk::Framebuffer FramebufferImpl::GetVkHandle() const
{
return mFramebuffer;
{
auto result = std::vector<vk::ClearValue>{};
- // @todo & color clear enabled / depth clear enabled
for(auto& attachment : mAttachments)
{
result.emplace_back(attachment->GetClearValue());
namespace Dali::Graphics::Vulkan
{
-class RenderPass;
class Device;
+class RenderPass;
/**
* FramebufferImpl encapsulates following objects:
[[nodiscard]] uint32_t GetAttachmentCount(AttachmentType type) const;
- [[nodiscard]] RenderPassHandle GetImplFromRenderPass(RenderPass* renderPass); // May mutate mRenderPasses
+ /**
+ * Add a renderpass (load/store ops) + Impl (vk wrapper) to the framebuffer.
+ *
+ * The handle may point to the renderpass used to create the framebuffer.
+ * @param[in] renderPass A renderpass object (load/store ops)
+ * @param[in] renderPassImpl The vulkan wrapper to an actual render pass generated using
+ * the renderPass ops and framebuffer attachments.
+ */
+ void AddRenderPass(Vulkan::RenderPass* renderPass, Vulkan::RenderPassHandle renderPassImpl);
+
+ [[nodiscard]] RenderPassHandle GetImplFromRenderPass(Vulkan::RenderPass* renderPass); // May mutate mRenderPasses
[[nodiscard]] RenderPassHandle GetRenderPass(uint32_t index) const;
#include <dali/internal/graphics/vulkan-impl/vulkan-graphics-controller.h>
#include <dali/internal/graphics/vulkan-impl/vulkan-render-pass-impl.h>
#include <dali/internal/graphics/vulkan-impl/vulkan-render-pass.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-texture.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-types.h>
+
+template<class VulkanType, class GraphicsType>
+VulkanType* VulkanCast(GraphicsType* apiObject)
+{
+ return const_cast<VulkanType*>(static_cast<const VulkanType*>(apiObject));
+}
namespace Dali::Graphics::Vulkan
{
bool Framebuffer::InitializeResource()
{
- // Create attachments
- SharedAttachments colorAttachments;
- // for(auto& attachment : mCreateInfo.colorAttachments)
- {
- // auto graphicsTexture = static_cast<const Vulkan::Texture*>(attachment.texture);
- // colorAttachments.push_back(FramebufferAttachment::NewColorAttachment(attachment.texture->GetVkHandle(), clearColor, AttachmentType::COLOR, false);
- }
- FramebufferAttachmentHandle depthStencilAttachment;
- if(mCreateInfo.depthStencilAttachment.depthTexture || mCreateInfo.depthStencilAttachment.stencilTexture)
+ auto& device = mController.GetGraphicsDevice();
+ bool firstRenderPass = true;
+
+ // There are usually 2 render passes, Clear & Load.
+ // They only contain load/store ops for each of color / depth&stencil attachments.
+ for(auto& gfxRenderPass : mCreateInfo.renderPasses)
{
- // depthStencilAttachment = FramebufferAttachment::NewDepthAttachment();
- }
+ auto* renderPass = VulkanCast<Vulkan::RenderPass>(gfxRenderPass);
+ auto& attachmentDescriptions = *renderPass->GetCreateInfo().attachments;
+
+ // Each attachment description must match passed in attachments.
+ size_t attachmentDescriptionIndex = 0;
+
+ // Create attachments
+ SharedAttachments colorAttachments;
+
+ auto clearColor = vk::ClearColorValue{}.setFloat32({1.0f, 0.0f, 1.0f, 1.0f});
- auto& device = mController.GetGraphicsDevice();
- mFramebufferImpl = FramebufferImpl::New(device, RenderPassHandle{}, colorAttachments, depthStencilAttachment, mCreateInfo.size.width, mCreateInfo.size.height);
+ for(auto& attachment : mCreateInfo.colorAttachments)
+ {
+ auto* graphicsTexture = VulkanCast<Vulkan::Texture>(attachment.texture);
+ DALI_ASSERT_DEBUG(attachmentDescriptionIndex < attachmentDescriptions.size() &&
+ "Render pass attachment descriptions out of range");
+
+ // FramebufferAttachment takes ownership of the image view. So, create new view onto the image.
+ std::unique_ptr<ImageView> imageView = graphicsTexture->CreateImageView();
+ colorAttachments.emplace_back(FramebufferAttachment::NewColorAttachment(imageView, clearColor, &attachmentDescriptions[attachmentDescriptionIndex++], false));
+ }
+
+ FramebufferAttachmentHandle depthStencilAttachment;
+
+ auto depthClearValue = vk::ClearDepthStencilValue{}.setDepth(0.0).setStencil(STENCIL_DEFAULT_CLEAR_VALUE);
+
+ if(mCreateInfo.depthStencilAttachment.depthTexture)
+ {
+ DALI_ASSERT_DEBUG(attachmentDescriptionIndex < attachmentDescriptions.size() && "Render pass attachment descriptions out of range");
+
+ auto depthTexture = VulkanCast<Vulkan::Texture>(mCreateInfo.depthStencilAttachment.depthTexture);
+ std::unique_ptr<ImageView> imageView = depthTexture->CreateImageView();
+ depthStencilAttachment = FramebufferAttachmentHandle(FramebufferAttachment::NewDepthAttachment(imageView, depthClearValue, &attachmentDescriptions[attachmentDescriptionIndex++]));
+ }
+ else if(mCreateInfo.depthStencilAttachment.stencilTexture)
+ {
+ DALI_ASSERT_DEBUG(attachmentDescriptionIndex < attachmentDescriptions.size() && "Render pass attachment descriptions out of range");
+ auto stencilTexture = VulkanCast<Vulkan::Texture>(mCreateInfo.depthStencilAttachment.stencilTexture);
+ std::unique_ptr<ImageView> imageView = stencilTexture->CreateImageView();
+ depthStencilAttachment = FramebufferAttachmentHandle(FramebufferAttachment::NewDepthAttachment(imageView, depthClearValue, &attachmentDescriptions[attachmentDescriptionIndex++]));
+ }
+
+ RenderPassImpl::CreateInfo createInfo;
+ RenderPassImpl::CreateCompatibleCreateInfo(createInfo, colorAttachments, depthStencilAttachment, true);
+ auto renderPassImpl = RenderPassHandle(RenderPassImpl::New(device, createInfo));
+
+ if(firstRenderPass)
+ {
+ // Create a framebuffer using the first render pass. Subsequent render passes will be created that
+ // are compatible.
+ mFramebufferImpl = FramebufferImpl::New(device, renderPassImpl, colorAttachments, depthStencilAttachment, mCreateInfo.size.width, mCreateInfo.size.height);
+ }
+
+ // Add the renderPass/renderPassImpl pair to the framebuffer.
+ if(mFramebufferImpl)
+ {
+ mFramebufferImpl->AddRenderPass(renderPass, renderPassImpl);
+ }
+ firstRenderPass = false;
+ }
return true;
}
*/
#include <dali/internal/graphics/vulkan-impl/vulkan-graphics-resource.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-render-pass-impl.h>
#include <dali/graphics-api/graphics-framebuffer-create-info.h>
#include <dali/graphics-api/graphics-framebuffer.h>
}
private:
- FramebufferImpl* mFramebufferImpl;
+ FramebufferImpl* mFramebufferImpl;
+ std::vector<RenderPassHandle> mRenderPasses;
};
} // namespace Vulkan
#include <dali/internal/graphics/vulkan-impl/vulkan-render-target.h>
#include <dali/internal/graphics/vulkan-impl/vulkan-sampler.h>
#include <dali/internal/graphics/vulkan-impl/vulkan-shader.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-texture-dependency-checker.h>
#include <dali/internal/graphics/vulkan-impl/vulkan-texture.h>
#include <dali/internal/window-system/common/window-render-surface.h>
struct VulkanGraphicsController::Impl
{
explicit Impl(VulkanGraphicsController& controller)
- : mGraphicsController(controller)
+ : mGraphicsController(controller),
+ mDependencyChecker(controller)
{
}
commandBuffer->End();
// submit to the queue
- mGraphicsDevice->Submit(mGraphicsDevice->GetTransferQueue(0u), {Vulkan::SubmissionData{{}, {}, {commandBuffer->GetImpl()}, {}}}, fence.get());
+ mGraphicsDevice->GetTransferQueue(0u).Submit({Vulkan::SubmissionData{{}, {}, {commandBuffer->GetImpl()}, {}}}, fence.get());
fence->Wait();
fence->Reset();
}
VulkanGraphicsController& mGraphicsController;
Vulkan::Device* mGraphicsDevice{nullptr};
+ Vulkan::TextureDependencyChecker mDependencyChecker; ///< Dependencies between framebuffers/scene
+
// used for texture<->buffer<->memory transfers
std::vector<ResourceTransferRequest> mResourceTransferRequests;
std::recursive_mutex mResourceTransferMutex{};
void VulkanGraphicsController::FrameStart()
{
+ mImpl->mDependencyChecker.Reset(); // Clean down the dependency graph.
+
mImpl->mCapacity = 0;
}
void VulkanGraphicsController::SubmitCommandBuffers(const SubmitInfo& submitInfo)
{
- // Figure out where to submit each command buffer.
+ SubmissionData submitData;
for(auto gfxCmdBuffer : submitInfo.cmdBuffer)
{
- auto cmdBuffer = static_cast<const CommandBuffer*>(gfxCmdBuffer);
- auto swapchain = cmdBuffer->GetLastSwapchain();
- if(swapchain)
+ auto cmdBuffer = static_cast<const CommandBuffer*>(gfxCmdBuffer);
+ auto renderTarget = cmdBuffer->GetRenderTarget();
+ DALI_ASSERT_DEBUG(renderTarget && "Cmd buffer has no render target set.");
+ if(renderTarget)
{
- swapchain->Submit(cmdBuffer->GetImpl());
+ // Currently, this will call vkQueueSubmit per cmd buf.
+ // @todo Roll up each into single submission for fewer calls to driver
+ renderTarget->Submit(cmdBuffer);
}
}
void VulkanGraphicsController::PresentRenderTarget(Graphics::RenderTarget* renderTarget)
{
- auto surface = static_cast<Vulkan::RenderTarget*>(renderTarget)->GetSurface();
- auto surfaceId = static_cast<Internal::Adaptor::WindowRenderSurface*>(surface)->GetSurfaceId();
- auto swapchain = mImpl->mGraphicsDevice->GetSwapchainForSurfaceId(surfaceId);
-
- swapchain->Present();
+ auto surface = static_cast<Vulkan::RenderTarget*>(renderTarget)->GetSurface();
+ if(surface)
+ {
+ auto surfaceId = static_cast<Internal::Adaptor::WindowRenderSurface*>(surface)->GetSurfaceId();
+ auto swapchain = mImpl->mGraphicsDevice->GetSwapchainForSurfaceId(surfaceId);
+ swapchain->Present();
+ }
+ // else no presentation required for framebuffer render target.
}
void VulkanGraphicsController::WaitIdle()
UniquePtr<Graphics::RenderTarget> VulkanGraphicsController::CreateRenderTarget(const Graphics::RenderTargetCreateInfo& renderTargetCreateInfo, UniquePtr<Graphics::RenderTarget>&& oldRenderTarget)
{
- return NewGraphicsObject<Vulkan::RenderTarget>(renderTargetCreateInfo, *this, std::move(oldRenderTarget));
+ auto renderTarget = NewGraphicsObject<Vulkan::RenderTarget>(renderTargetCreateInfo, *this, std::move(oldRenderTarget));
+ mImpl->mDependencyChecker.AddRenderTarget(CastObject<Vulkan::RenderTarget>(renderTarget.get()));
+ return renderTarget;
}
UniquePtr<Graphics::CommandBuffer> VulkanGraphicsController::CreateCommandBuffer(const Graphics::CommandBufferCreateInfo& commandBufferCreateInfo, UniquePtr<Graphics::CommandBuffer>&& oldCommandBuffer)
UniquePtr<Graphics::Framebuffer> VulkanGraphicsController::CreateFramebuffer(const Graphics::FramebufferCreateInfo& framebufferCreateInfo, UniquePtr<Graphics::Framebuffer>&& oldFramebuffer)
{
- return UniquePtr<Graphics::Framebuffer>{};
+ return NewGraphicsObject<Vulkan::Framebuffer>(framebufferCreateInfo, *this, std::move(oldFramebuffer));
}
UniquePtr<Graphics::Pipeline> VulkanGraphicsController::CreatePipeline(const Graphics::PipelineCreateInfo& pipelineCreateInfo, UniquePtr<Graphics::Pipeline>&& oldPipeline)
return CLIP_MATRIX;
}
+void VulkanGraphicsController::AddTextureDependencies(RenderTarget* renderTarget)
+{
+ auto framebuffer = renderTarget->GetFramebuffer();
+ DALI_ASSERT_DEBUG(framebuffer);
+
+ for(auto attachment : framebuffer->GetCreateInfo().colorAttachments)
+ {
+ auto texture = CastObject<Vulkan::Texture>(attachment.texture);
+ mImpl->mDependencyChecker.AddTexture(texture, renderTarget);
+ }
+ auto depthAttachment = framebuffer->GetCreateInfo().depthStencilAttachment;
+ if(depthAttachment.depthTexture)
+ {
+ mImpl->mDependencyChecker.AddTexture(CastObject<Vulkan::Texture>(depthAttachment.depthTexture), renderTarget);
+ }
+ if(depthAttachment.stencilTexture)
+ {
+ mImpl->mDependencyChecker.AddTexture(CastObject<Vulkan::Texture>(depthAttachment.stencilTexture), renderTarget);
+ }
+}
+
+void VulkanGraphicsController::CheckTextureDependencies(
+ const std::vector<Graphics::TextureBinding>& textureBindings,
+ RenderTarget* renderTarget)
+{
+ for(auto& binding : textureBindings)
+ {
+ if(binding.texture)
+ {
+ auto texture = CastObject<const Vulkan::Texture>(binding.texture);
+ mImpl->mDependencyChecker.CheckNeedsSync(texture, renderTarget);
+ }
+ }
+}
+
+void VulkanGraphicsController::RemoveRenderTarget(RenderTarget* renderTarget)
+{
+ mImpl->mDependencyChecker.RemoveRenderTarget(renderTarget);
+}
+
} // namespace Dali::Graphics::Vulkan
class Sampler;
class Texture;
+/**
+ * Class to manage the vulkan graphics backend. This is the main object that clients interact
+ * with to get work done on the GPU.
+ */
class VulkanGraphicsController : public Graphics::Controller, public Integration::GraphicsConfig
{
public:
*/
const Matrix& GetClipMatrix() const override;
+public: // Other API
+ /**
+ * Adds the render-target's fbo attachments into the dependency graph.
+ */
+ void AddTextureDependencies(RenderTarget* renderTarget);
+
+ /**
+ * Check if any of the textures are fbo attachments and update dependency graph
+ */
+ void CheckTextureDependencies(const std::vector<Graphics::TextureBinding>& textureBindings,
+ RenderTarget* renderTarget);
+
+ /**
+ * Remove the render target from dependency graph
+ */
+ void RemoveRenderTarget(RenderTarget* renderTarget);
+
public: // For debug
void FrameStart();
if(gfxPipelineInfo.pColorBlendState)
{
- auto attachmentCount = impl->GetAttachments().size();
+ auto attachmentCount = impl->GetAttachmentCount();
if(impl->HasDepthAttachment())
{
attachmentCount--;
*/
// INTERNAL INCLUDES
+#include <dali/internal/graphics/vulkan-impl/vulkan-command-buffer-impl.h>
#include <dali/internal/graphics/vulkan-impl/vulkan-fence-impl.h>
#include <dali/internal/graphics/vulkan-impl/vulkan-queue-impl.h>
namespace Dali::Graphics::Vulkan
{
-
// submission
SubmissionData::SubmissionData(const std::vector<vk::Semaphore>& waitSemaphores_,
vk::PipelineStageFlags waitDestinationStageMask_,
return mQueue.presentKHR(&presentInfo);
}
-vk::Result Queue::Submit(std::vector<vk::SubmitInfo>& info, FenceImpl* fence)
+vk::Result Queue::Submit(const std::vector<SubmissionData>& submissionData, FenceImpl* fence)
{
- return VkAssert(mQueue.submit(info, fence ? fence->GetVkHandle() : nullptr));
+ auto lock(Lock());
+
+ auto submitInfos = std::vector<vk::SubmitInfo>{};
+ submitInfos.reserve(submissionData.size());
+ auto commandBufferHandles = std::vector<vk::CommandBuffer>{};
+
+ // prepare memory
+ auto bufferSize = 0u;
+ for(auto& data : submissionData)
+ {
+ bufferSize += uint32_t(data.commandBuffers.size());
+ }
+ commandBufferHandles.reserve(bufferSize);
+
+ // Transform SubmissionData to vk::SubmitInfo
+ for(const auto& subData : submissionData)
+ {
+ auto currentBufferIndex = commandBufferHandles.size();
+
+ // Extract the command buffer handles
+ std::transform(subData.commandBuffers.cbegin(),
+ subData.commandBuffers.cend(),
+ std::back_inserter(commandBufferHandles),
+ [&](CommandBufferImpl* entry) {
+ return entry->GetVkHandle();
+ });
+
+ // clang-format=off
+ auto submitInfo = vk::SubmitInfo()
+ .setWaitSemaphoreCount(U32(subData.waitSemaphores.size()))
+ .setPWaitSemaphores(subData.waitSemaphores.data())
+ .setPWaitDstStageMask(&subData.waitDestinationStageMask)
+ .setCommandBufferCount(U32(subData.commandBuffers.size()))
+ .setPCommandBuffers(&commandBufferHandles[currentBufferIndex])
+ .setSignalSemaphoreCount(U32(subData.signalSemaphores.size()))
+ .setPSignalSemaphores(subData.signalSemaphores.data());
+
+ submitInfos.push_back(submitInfo);
+ // clang-format=on
+ }
+
+ return VkAssert(mQueue.submit(submitInfos, fence ? fence->GetVkHandle() : nullptr));
}
} // namespace Dali::Graphics::Vulkan
vk::Result Present(vk::PresentInfoKHR& presentInfo);
- vk::Result Submit(std::vector<vk::SubmitInfo>& info, FenceImpl* fence);
+ vk::Result Submit(const std::vector<SubmissionData>& submissionData, FenceImpl* fence);
private:
vk::Queue mQueue;
return RenderPassHandle(renderPass);
}
+RenderPassHandle RenderPassImpl::New(
+ Vulkan::Device& device,
+ const RenderPassImpl::CreateInfo& createInfo)
+{
+ auto renderPass = new RenderPassImpl(device, createInfo);
+ return RenderPassHandle(renderPass);
+}
+
RenderPassImpl::RenderPassImpl(Vulkan::Device& device,
const SharedAttachments& colorAttachments,
FramebufferAttachmentHandle depthAttachment)
: mGraphicsDevice(&device),
mHasDepthAttachment(bool(depthAttachment))
{
- CreateCompatibleCreateInfo(colorAttachments, depthAttachment);
+ // Default case is creating render pass for swapchain.
+ CreateCompatibleCreateInfo(mCreateInfo, colorAttachments, depthAttachment, false);
+ CreateRenderPass();
+}
+
+RenderPassImpl::RenderPassImpl(Vulkan::Device& device,
+ const RenderPassImpl::CreateInfo& createInfo)
+: mGraphicsDevice(&device),
+ mCreateInfo(createInfo)
+{
CreateRenderPass();
}
return mVkRenderPass;
}
-std::vector<vk::ImageView>& RenderPassImpl::GetAttachments()
+size_t RenderPassImpl::GetAttachmentCount()
{
- return mAttachments;
+ return mHasDepthAttachment + mCreateInfo.colorAttachmentReferences.size();
}
void RenderPassImpl::CreateCompatibleCreateInfo(
- const SharedAttachments& colorAttachments,
- FramebufferAttachmentHandle depthAttachment)
+ CreateInfo& createInfo,
+ const SharedAttachments& colorAttachments,
+ const FramebufferAttachmentHandle& depthAttachment,
+ bool subpassForOffscreen)
{
auto hasDepth = false;
if(depthAttachment)
// The total number of attachments
auto totalAttachmentCount = hasDepth ? colorAttachments.size() + 1 : colorAttachments.size();
- mAttachments.clear();
- mAttachments.reserve(totalAttachmentCount);
+
+ createInfo.attachmentHandles.reserve(colorAttachments.size() + depthAttachment);
+ for(auto& handle : colorAttachments)
+ {
+ createInfo.attachmentHandles.push_back(handle);
+ }
+ if(depthAttachment)
+ {
+ createInfo.attachmentHandles.push_back(depthAttachment);
+ }
// This vector stores the attachment references
- mCreateInfo.colorAttachmentReferences.reserve(colorAttachments.size());
+ createInfo.colorAttachmentReferences.reserve(colorAttachments.size());
// This vector stores the attachment descriptions
- mCreateInfo.attachmentDescriptions.reserve(totalAttachmentCount);
+ createInfo.attachmentDescriptions.reserve(totalAttachmentCount);
// For each color attachment...
for(auto i = 0u; i < colorAttachments.size(); ++i)
assert(imageLayout == vk::ImageLayout::eColorAttachmentOptimal);
// Add a reference and a descriptions and image views to their respective vectors
- mCreateInfo.colorAttachmentReferences.push_back(vk::AttachmentReference{}.setLayout(imageLayout).setAttachment(U32(i)));
-
- mCreateInfo.attachmentDescriptions.push_back(colorAttachments[i]->GetDescription());
+ createInfo.colorAttachmentReferences.push_back(vk::AttachmentReference{}.setLayout(imageLayout).setAttachment(U32(i)));
- mAttachments.push_back(colorAttachments[i]->GetImageView()->GetVkHandle());
+ createInfo.attachmentDescriptions.push_back(colorAttachments[i]->GetDescription());
}
// Follow the exact same procedure as color attachments
assert(imageLayout == vk::ImageLayout::eDepthStencilAttachmentOptimal);
- mCreateInfo.depthAttachmentReference.setLayout(imageLayout);
- mCreateInfo.depthAttachmentReference.setAttachment(U32(mCreateInfo.colorAttachmentReferences.size()));
-
- mCreateInfo.attachmentDescriptions.push_back(depthAttachment->GetDescription());
+ createInfo.depthAttachmentReference.setLayout(imageLayout);
+ createInfo.depthAttachmentReference.setAttachment(U32(createInfo.colorAttachmentReferences.size()));
- mAttachments.push_back(depthAttachment->GetImageView()->GetVkHandle());
+ createInfo.attachmentDescriptions.push_back(depthAttachment->GetDescription());
}
- // Creating a single subpass per framebuffer
- mCreateInfo.subpassDesc.setPipelineBindPoint(vk::PipelineBindPoint::eGraphics);
- mCreateInfo.subpassDesc.setColorAttachmentCount(U32(colorAttachments.size()));
+ createInfo.subpassDesc.setPipelineBindPoint(vk::PipelineBindPoint::eGraphics);
+ createInfo.subpassDesc.setColorAttachmentCount(U32(colorAttachments.size()));
if(hasDepth)
{
- mCreateInfo.subpassDesc.setPDepthStencilAttachment(&mCreateInfo.depthAttachmentReference);
+ createInfo.subpassDesc.setPDepthStencilAttachment(&createInfo.depthAttachmentReference);
}
- mCreateInfo.subpassDesc.setPColorAttachments(mCreateInfo.colorAttachmentReferences.data());
+ createInfo.subpassDesc.setPColorAttachments(createInfo.colorAttachmentReferences.data());
- // Creating 2 subpass dependencies using VK_SUBPASS_EXTERNAL to leverage the implicit image layout
- // transitions provided by the driver
- vk::AccessFlags accessMask(vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite);
- vk::PipelineStageFlags stageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput);
- if(hasDepth)
- {
- accessMask |= vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
- stageMask |= vk::PipelineStageFlagBits::eEarlyFragmentTests;
- }
+ auto dependencyCount = CreateSubPassDependencies(createInfo, hasDepth, subpassForOffscreen);
- mCreateInfo.subpassDependencies = {
- vk::SubpassDependency{}
- .setSrcSubpass(VK_SUBPASS_EXTERNAL)
- .setDstSubpass(0)
- .setSrcStageMask(vk::PipelineStageFlagBits::eBottomOfPipe)
- .setDstStageMask(stageMask)
- .setSrcAccessMask(vk::AccessFlagBits::eMemoryRead)
- .setDstAccessMask(accessMask)
- .setDependencyFlags(vk::DependencyFlagBits::eByRegion),
-
- vk::SubpassDependency{}
- .setSrcSubpass(0)
- .setDstSubpass(VK_SUBPASS_EXTERNAL)
- .setSrcStageMask(stageMask)
- .setDstStageMask(vk::PipelineStageFlagBits::eBottomOfPipe)
- .setSrcAccessMask(accessMask)
- .setDstAccessMask(vk::AccessFlagBits::eMemoryRead)
- .setDependencyFlags(vk::DependencyFlagBits::eByRegion)};
-
- mCreateInfo.createInfo
- .setAttachmentCount(U32(mCreateInfo.attachmentDescriptions.size()))
- .setPAttachments(mCreateInfo.attachmentDescriptions.data())
- .setPSubpasses(&mCreateInfo.subpassDesc)
+ createInfo.createInfo
+ .setAttachmentCount(U32(createInfo.attachmentDescriptions.size()))
+ .setPAttachments(createInfo.attachmentDescriptions.data())
+ .setPSubpasses(&createInfo.subpassDesc)
.setSubpassCount(1)
- .setDependencyCount(2)
- .setPDependencies(mCreateInfo.subpassDependencies.data());
+ .setDependencyCount(dependencyCount)
+ .setPDependencies(createInfo.subpassDependencies.data());
}
void RenderPassImpl::CreateRenderPass()
mVkRenderPass = VkAssert(mGraphicsDevice->GetLogicalDevice().createRenderPass(mCreateInfo.createInfo, mGraphicsDevice->GetAllocator()));
}
+int RenderPassImpl::CreateSubPassDependencies(CreateInfo& createInfo, bool hasDepth, bool subpassForOffscreen)
+{
+ int dependencyCount = 0;
+
+ if(subpassForOffscreen)
+ {
+ createInfo.subpassDependencies = {
+ vk::SubpassDependency{}
+ .setSrcSubpass(vk::SubpassExternal)
+ .setDstSubpass(0)
+ .setSrcStageMask(vk::PipelineStageFlagBits::eFragmentShader)
+ .setDstStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests)
+ .setSrcAccessMask(vk::AccessFlagBits::eNone)
+ .setDstAccessMask(vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite),
+ vk::SubpassDependency{}
+ .setSrcSubpass(0)
+ .setDstSubpass(vk::SubpassExternal)
+ .setSrcStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests)
+ .setDstStageMask(vk::PipelineStageFlagBits::eFragmentShader)
+ .setSrcAccessMask(vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite)
+ .setDstAccessMask(vk::AccessFlagBits::eMemoryRead)};
+ dependencyCount = 2;
+ }
+ else // Subpass for swapchain
+ {
+ // Creating 2 subpass dependencies using VK_SUBPASS_EXTERNAL to leverage the implicit image layout
+ // transitions provided by the driver
+ vk::AccessFlags accessMask(vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite);
+ vk::PipelineStageFlags stageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput);
+ if(hasDepth)
+ {
+ accessMask |= vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
+ stageMask |= vk::PipelineStageFlagBits::eEarlyFragmentTests;
+ }
+
+ createInfo.subpassDependencies = {
+ vk::SubpassDependency{}
+ .setSrcSubpass(VK_SUBPASS_EXTERNAL)
+ .setDstSubpass(0)
+ .setSrcStageMask(vk::PipelineStageFlagBits::eBottomOfPipe)
+ .setDstStageMask(stageMask)
+ .setSrcAccessMask(vk::AccessFlagBits::eMemoryRead)
+ .setDstAccessMask(accessMask)
+ .setDependencyFlags(vk::DependencyFlagBits::eByRegion),
+
+ vk::SubpassDependency{}
+ .setSrcSubpass(0)
+ .setDstSubpass(VK_SUBPASS_EXTERNAL)
+ .setSrcStageMask(stageMask)
+ .setDstStageMask(vk::PipelineStageFlagBits::eBottomOfPipe)
+ .setSrcAccessMask(accessMask)
+ .setDstAccessMask(vk::AccessFlagBits::eMemoryRead)
+ .setDependencyFlags(vk::DependencyFlagBits::eByRegion)};
+ dependencyCount = 2;
+ }
+ return dependencyCount;
+}
+
} // namespace Dali::Graphics::Vulkan
class RenderPassImpl : public VkSharedResource
{
public:
- struct CreateInfo
+ class CreateInfo
{
+ public:
+ SharedAttachments attachmentHandles;
std::vector<vk::AttachmentReference> colorAttachmentReferences;
vk::AttachmentReference depthAttachmentReference;
std::vector<vk::AttachmentDescription> attachmentDescriptions;
const SharedAttachments& colorAttachments,
FramebufferAttachmentHandle depthAttachment);
+ static RenderPassHandle New(Vulkan::Device& device,
+ const RenderPassImpl::CreateInfo& createInfo);
+
RenderPassImpl(Vulkan::Device& device, const SharedAttachments& colorAttachments, FramebufferAttachmentHandle depthAttachment);
+ RenderPassImpl(Vulkan::Device& device, const RenderPassImpl::CreateInfo& createInfo);
+
~RenderPassImpl();
bool OnDestroy() override;
vk::RenderPass GetVkHandle();
- std::vector<vk::ImageView>& GetAttachments();
+ size_t GetAttachmentCount();
bool HasDepthAttachment()
{
return mCreateInfo;
}
-private:
- void CreateCompatibleCreateInfo(
- const SharedAttachments& colorAttachments,
- FramebufferAttachmentHandle depthAttachment);
+ static void CreateCompatibleCreateInfo(
+ CreateInfo& createInfo,
+ const SharedAttachments& colorAttachments,
+ const FramebufferAttachmentHandle& depthAttachment,
+ bool subpassForOffscreen);
- void CreateRenderPass();
+private:
+ void CreateRenderPass();
+ static int CreateSubPassDependencies(CreateInfo& createInfo, bool hasDepth, bool subpassForOffscreen);
private:
- Device* mGraphicsDevice;
- CreateInfo mCreateInfo;
- vk::RenderPass mVkRenderPass;
- std::vector<vk::ImageView> mAttachments{};
- bool mHasDepthAttachment;
+ Device* mGraphicsDevice;
+ CreateInfo mCreateInfo;
+ vk::RenderPass mVkRenderPass;
+ bool mHasDepthAttachment{false};
};
} // namespace Dali::Graphics::Vulkan
// INTERNAL INCLUDES
#include <dali/integration-api/adaptor-framework/render-surface-interface.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-command-buffer.h>
#include <dali/internal/graphics/vulkan-impl/vulkan-framebuffer-impl.h>
#include <dali/internal/graphics/vulkan-impl/vulkan-framebuffer.h>
#include <dali/internal/graphics/vulkan-impl/vulkan-graphics-controller.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-queue-impl.h>
#include <dali/internal/graphics/vulkan-impl/vulkan-render-pass.h>
#include <dali/internal/graphics/vulkan/vulkan-device.h>
#include <dali/internal/window-system/common/window-render-surface.h>
// Do creation stuff!
// Create Swapchain?!
}
+ else
+ {
+ // Non-surface render targets use own semaphore to signal cmd buffer completion.
+ auto& graphicsDevice = controller.GetGraphicsDevice();
+ mSubmitSemaphore = graphicsDevice.GetLogicalDevice().createSemaphore({}, graphicsDevice.GetAllocator()).value;
+ }
}
RenderTarget::~RenderTarget() = default;
void RenderTarget::DiscardResource()
{
mController.DiscardResource(this);
+ mController.RemoveRenderTarget(this); // Remove from dependency graph
// The surface context should be deleted now
if(mCreateInfo.surface)
{
- //mController.DeleteSurfaceContext(static_cast<Dali::RenderSurfaceInterface*>(mCreateInfo.surface));
mCreateInfo.surface = nullptr;
}
}
return framebufferImpl->GetImplFromRenderPass(renderPass);
}
+void RenderTarget::Submit(const CommandBuffer* cmdBuffer)
+{
+ auto& graphicsDevice = mController.GetGraphicsDevice();
+ auto surface = GetSurface();
+
+ if(surface)
+ {
+ auto surfaceId = static_cast<Internal::Adaptor::WindowRenderSurface*>(surface)->GetSurfaceId();
+ auto swapchain = graphicsDevice.GetSwapchainForSurfaceId(surfaceId);
+ swapchain->Submit(cmdBuffer->GetImpl());
+ }
+ else
+ {
+ std::vector<vk::Semaphore> waitSemaphores;
+ for(auto renderTarget : mDependencies)
+ {
+ waitSemaphores.push_back(renderTarget->mSubmitSemaphore);
+ }
+
+ std::vector<vk::Semaphore> signalSemaphores{mSubmitSemaphore};
+ graphicsDevice.GetGraphicsQueue(0).Submit(
+ {SubmissionData{
+ waitSemaphores,
+ {vk::PipelineStageFlagBits::eFragmentShader},
+ {cmdBuffer->GetImpl()},
+ signalSemaphores}},
+ nullptr);
+ }
+}
+
} // namespace Dali::Graphics::Vulkan
{
class Framebuffer;
class Surface;
+class CommandBuffer;
using RenderTargetResource = Resource<Graphics::RenderTarget, Graphics::RenderTargetCreateInfo>;
* @return a matching render pass implementation from the current framebuffer
*/
[[nodiscard]] Vulkan::RenderPassHandle GetRenderPass(const Graphics::RenderPass* renderPass) const;
+ /**
+ * Submit the command buffer to the graphics queue using the right sync.
+ */
+ void Submit(const CommandBuffer* commandBuffer);
+
+ void ResetDependencies()
+ {
+ mDependencies.clear();
+ }
+
+ void AddDependency(RenderTarget* dependency)
+ {
+ mDependencies.push_back(dependency);
+ }
+ void RemoveDependency(RenderTarget* dependency)
+ {
+ auto iter = std::find(mDependencies.begin(), mDependencies.end(), dependency);
+ if(iter != mDependencies.end())
+ {
+ mDependencies.erase(iter);
+ }
+ }
+
+private:
+ vk::Semaphore mSubmitSemaphore; ///< Signaled when the command buffer for this target is processed
+ std::vector<RenderTarget*> mDependencies; ///< Render targets whose output is used as input to this task.
};
} // namespace Dali::Graphics::Vulkan
SharedAttachments attachments;
attachments.emplace_back(FramebufferAttachment::NewColorAttachment(colorImageView,
clearColor,
+ nullptr,
true));
std::unique_ptr<FramebufferImpl, void (*)(FramebufferImpl*)> framebuffer(
swapchainBuffer->endOfFrameFence->Reset();
- // @todo Should we allow multiple submits per swapchain per frame?
- // If so, should change the fence, or at least wait for the fence
- // prior to the reset above.
- mGraphicsDevice.Submit(*mQueue,
- {Vulkan::SubmissionData{
- {swapchainBuffer->acquireNextImageSemaphore},
- {vk::PipelineStageFlagBits::eFragmentShader},
- {commandBuffer},
- {swapchainBuffer->submitSemaphore}}},
- swapchainBuffer->endOfFrameFence.get());
+ mQueue->Submit({Vulkan::SubmissionData{
+ {swapchainBuffer->acquireNextImageSemaphore},
+ {vk::PipelineStageFlagBits::eFragmentShader},
+ {commandBuffer},
+ {swapchainBuffer->submitSemaphore}}},
+ swapchainBuffer->endOfFrameFence.get());
}
void Swapchain::Present()
auto depthClearValue = vk::ClearDepthStencilValue{}.setDepth(0.0).setStencil(STENCIL_DEFAULT_CLEAR_VALUE);
// A single depth attachment for the swapchain. Takes ownership of the image view
- depthAttachment = FramebufferAttachmentHandle(FramebufferAttachment::NewDepthAttachment(depthStencilImageView, depthClearValue));
+ depthAttachment = FramebufferAttachmentHandle(FramebufferAttachment::NewDepthAttachment(depthStencilImageView, depthClearValue, nullptr));
}
// Before replacing framebuffers in the swapchain, wait until all is done
--- /dev/null
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include <dali/internal/graphics/vulkan-impl/vulkan-texture-dependency-checker.h>
+
+// EXTERNAL INCLUDES
+#include <dali/internal/graphics/vulkan-impl/vulkan-render-target.h>
+#include <dali/internal/graphics/vulkan-impl/vulkan-texture.h>
+
+// INTERNAL INCLUDES
+
+namespace Dali::Graphics::Vulkan
+{
+void TextureDependencyChecker::Reset()
+{
+ for(auto& dependency : mDependencies)
+ {
+ dependency.generator->ResetDependencies();
+ }
+
+ mDependencies.clear();
+ mCurrentIndex = 0;
+}
+
+void TextureDependencyChecker::AddTexture(const Vulkan::Texture* texture, Vulkan::RenderTarget* renderTarget)
+{
+ // We only push attachments into this struct, so total size should be low.
+ // Note, Textures may be written to more than once in a frame, so always add new target generator
+ mDependencies.push_back(TextureDependency{texture, renderTarget});
+}
+
+void TextureDependencyChecker::CheckNeedsSync(const Vulkan::Texture* texture, Vulkan::RenderTarget* renderTarget)
+{
+ // Check if this texture was generated as a dependency. It could be generated by more than one
+ // target in a frame, so work backwards from the latest render pass.
+ for(auto iter = mDependencies.rbegin(); iter < mDependencies.rend(); ++iter)
+ {
+ if(iter->texture == texture)
+ {
+ renderTarget->AddDependency(iter->generator);
+ break;
+ }
+ }
+}
+
+void TextureDependencyChecker::AddRenderTarget(Vulkan::RenderTarget* renderTarget)
+{
+ // We only add/remove on create/destroy, so guarantee uniqueness
+ mRenderTargets.push_back(renderTarget);
+}
+
+void TextureDependencyChecker::RemoveRenderTarget(Vulkan::RenderTarget* renderTarget)
+{
+ for(auto iter = mRenderTargets.begin(); iter != mRenderTargets.end(); ++iter)
+ {
+ if(*iter == renderTarget)
+ {
+ mRenderTargets.erase(iter);
+ break;
+ }
+ else
+ {
+ (*iter)->RemoveDependency(renderTarget);
+ }
+ }
+}
+
+} // namespace Dali::Graphics::Vulkan
--- /dev/null
+#pragma once
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dali/public-api/common/vector-wrapper.h>
+#include <cstdint>
+
+namespace Dali::Graphics::Vulkan
+{
+class VulkanGraphicsController;
+class Texture;
+class RenderTarget;
+
+/**
+ * Class to handle dependency checks between textures from different render targets.
+ *
+ * When submitting a render target to generate a framebuffer attachment, the controller
+ * will create a signal semaphore that is triggered once the command buffer has completed
+ * execution.
+ * The dependency checker ensures that any dependent render targets that use the attachment
+ * texture listen to this semaphore on their cmd buffer submission.
+ */
+class TextureDependencyChecker
+{
+public:
+ explicit TextureDependencyChecker(VulkanGraphicsController& controller)
+ : mController(controller)
+ {
+ }
+ ~TextureDependencyChecker() = default;
+
+ /**
+ * Resets the dependency graph for regeneration this frame
+ */
+ void Reset();
+
+ /**
+ * Add Texture dependencies.
+ *
+ * @param[in] texture The texture that's output by this render target
+ * @param[in] renderTarget The render target that generates this texture
+ */
+ void AddTexture(const Vulkan::Texture* texture, Vulkan::RenderTarget* renderTarget);
+
+ /**
+ * Check if the given texture needs syncing before being read. This will add any
+ * existing dependencies to the given render target (will be used in command
+ * submission to set up waiting semaphores)
+ */
+ void CheckNeedsSync(const Vulkan::Texture* texture, Vulkan::RenderTarget* renderTarget);
+
+ const uint32_t INVALID_DEPENDENCY_INDEX = 0xffffffff;
+
+ /**
+ * Add render target.
+ *
+ * @param[in] renderTarget The render target to add to the dependency graph
+ */
+ void AddRenderTarget(Vulkan::RenderTarget* renderTarget);
+
+ /**
+ * Remove render target
+ * @param[in] renderTarget The render target to remove from the dependency graph
+ */
+ void RemoveRenderTarget(Vulkan::RenderTarget* renderTarget);
+
+private:
+ struct TextureDependency
+ {
+ const Vulkan::Texture* texture;
+ Vulkan::RenderTarget* generator;
+ };
+ VulkanGraphicsController& mController;
+ std::vector<TextureDependency> mDependencies;
+ std::vector<RenderTarget*> mRenderTargets; ///< Maintained list of all current render targets
+ uint32_t mCurrentIndex{0};
+};
+
+} // namespace Dali::Graphics::Vulkan
}
}
+std::unique_ptr<Vulkan::ImageView> Texture::CreateImageView()
+{
+ if(!mImageView)
+ {
+ // Ensure we have initialized the image:
+ InitializeImageView();
+ }
+ std::unique_ptr<Vulkan::ImageView> imageView(ImageView::NewFromImage(mDevice, *mImage, mComponentMapping));
+ return imageView;
+}
+
Vulkan::Image* Texture::GetImage() const
{
return mImage;
ImageView* GetImageView() const;
+ /**
+ * Create a new image view onto the image.
+ */
+ std::unique_ptr<ImageView> CreateImageView();
+
SamplerImpl* GetSampler() const;
// @todo Temporary. We need to use the newer APIs
return extensions;
}
-vk::Result Device::Submit(Queue& queue, const std::vector<SubmissionData>& submissionData, FenceImpl* fence)
-{
- auto lock(queue.Lock());
-
- auto submitInfos = std::vector<vk::SubmitInfo>{};
- submitInfos.reserve(submissionData.size());
- auto commandBufferHandles = std::vector<vk::CommandBuffer>{};
-
- // prepare memory
- auto bufferSize = 0u;
- for(auto& data : submissionData)
- {
- bufferSize += uint32_t(data.commandBuffers.size());
- }
- commandBufferHandles.reserve(bufferSize);
-
- // Transform SubmissionData to vk::SubmitInfo
- for(const auto& subData : submissionData)
- {
- auto currentBufferIndex = commandBufferHandles.size();
-
- // Extract the command buffer handles
- std::transform(subData.commandBuffers.cbegin(),
- subData.commandBuffers.cend(),
- std::back_inserter(commandBufferHandles),
- [&](CommandBufferImpl* entry) {
- return entry->GetVkHandle();
- });
-
- // clang-format=off
- auto submitInfo = vk::SubmitInfo()
- .setWaitSemaphoreCount(U32(subData.waitSemaphores.size()))
- .setPWaitSemaphores(subData.waitSemaphores.data())
- .setPWaitDstStageMask(&subData.waitDestinationStageMask)
- .setCommandBufferCount(U32(subData.commandBuffers.size()))
- .setPCommandBuffers(&commandBufferHandles[currentBufferIndex])
- .setSignalSemaphoreCount(U32(subData.signalSemaphores.size()))
- .setPSignalSemaphores(subData.signalSemaphores.data());
-
- submitInfos.push_back(submitInfo);
- // clang-format=on
- }
-
- return VkAssert(queue.Submit(submitInfos, fence));
-}
-
/**
* Helper function which returns GPU heap index that can be used to allocate
* particular type of resource
Swapchain* CreateSwapchain(SurfaceImpl* surface, vk::Format requestedFormat, vk::PresentModeKHR presentMode, uint32_t bufferCount, Swapchain*&& oldSwapchain);
- vk::Result Submit(Queue& queue, const std::vector<SubmissionData>& submissionData, FenceImpl* fence = nullptr);
vk::Result Present(Queue& queue, vk::PresentInfoKHR presentInfo);
vk::Result QueueWaitIdle(Queue& queue);
vk::Result DeviceWaitIdle();