#define DALI_INTEGRATION_RENDER_SURFACE_INTERFACE_H
/*
- * 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.
mFullSwapNextFrame = true;
}
+ bool GetFullSwapNextFrame() const
+ {
+ return mFullSwapNextFrame;
+ }
+
private:
/**
* @brief Undefined copy constructor. RenderSurface cannot be copied
mDamagedRects.clear();
// Collect damage rects
- bool willRender = mCore.PreRender(scene, mDamagedRects);
+ bool willRender = mCore.PreRender(scene, mDamagedRects) || windowSurface->GetFullSwapNextFrame();
+ ;
if(willRender)
{
graphics.AcquireNextImage(windowSurface);
// Render off-screen frame buffers first if any
mCore.RenderScene(windowRenderStatus, scene, true);
- Rect<int> clippingRect; // Empty for fbo rendering
+ if(willRender)
+ {
+ Rect<int> clippingRect; // Empty for fbo rendering
- // Ensure surface can be drawn to; merge damaged areas for previous frames
- windowSurface->PreRender(sceneSurfaceResized > 0u, mDamagedRects, clippingRect);
+ // Ensure surface can be drawn to; merge damaged areas for previous frames
+ windowSurface->PreRender(sceneSurfaceResized > 0u, mDamagedRects, clippingRect);
- // Render the surface
- mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
+ // Render the surface (Present & SwapBuffers)
+ mCore.RenderScene(windowRenderStatus, scene, false, clippingRect);
+ }
// If surface is resized, the surface resized count is decreased.
if(DALI_UNLIKELY(sceneSurfaceResized > 0u))
mRenderTarget = ConstGraphicsCast<Vulkan::RenderTarget, Graphics::RenderTarget>(info.renderTarget);
uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
DALI_ASSERT_ALWAYS(bufferIndex < mCommandBufferImpl.size());
+ DALI_LOG_INFO(gLogCmdBufferFilter, Debug::Verbose, "CommandBuffer::Begin: ptr:%p bufferIndex=%d\n", GetImpl(), bufferIndex);
+ mCmdCount++; // Debug info
if(mCommandBufferImpl[bufferIndex])
{
- DALI_LOG_INFO(gLogCmdBufferFilter, Debug::Verbose, "CommandBuffer::Begin: ptr:%p bufferIndex=%d", this, bufferIndex);
vk::CommandBufferInheritanceInfo inheritanceInfo{};
if(info.renderPass)
{
{
uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
DALI_ASSERT_ALWAYS(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex]);
+ DALI_LOG_INFO(gLogCmdBufferFilter, Debug::Verbose, "CommandBuffer::End: ptr:%p bufferIndex=%d\n", GetImpl(), bufferIndex);
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->End();
}
void CommandBuffer::Reset()
{
uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
- DALI_LOG_INFO(gLogCmdBufferFilter, Debug::Verbose, "CommandBuffer::Reset: ptr:%p bufferIndex=%d", this, bufferIndex);
+ DALI_LOG_INFO(gLogCmdBufferFilter, Debug::Verbose, "CommandBuffer::Reset: ptr:%p bufferIndex=%d", GetImpl(), bufferIndex);
DALI_ASSERT_ALWAYS(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex]);
mCommandBufferImpl[bufferIndex]->Reset();
mDynamicStateMask = CommandBuffer::INITIAL_DYNAMIC_MASK_VALUE;
mRenderTarget = nullptr;
+ mCmdCount = 1;
}
void CommandBuffer::BindVertexBuffers(uint32_t firstBinding,
buffers.push_back(ConstGraphicsCast<Buffer, Graphics::Buffer>(gfxBuffer)->GetImpl());
}
DALI_ASSERT_ALWAYS(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex]);
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->BindVertexBuffers(firstBinding, buffers, offsets);
}
auto indexBuffer = ConstGraphicsCast<Buffer, Graphics::Buffer>(&gfxBuffer);
DALI_ASSERT_DEBUG(indexBuffer && indexBuffer->GetImpl());
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->BindIndexBuffer(*indexBuffer->GetImpl(), offset, format);
}
const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
DALI_ASSERT_ALWAYS(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex]);
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->BindPipeline(&pipeline);
}
mCommandBufferImpl[bufferIndex]->BindTextures(textureBindings);
+ mCmdCount++; // Debug info
mController.CheckTextureDependencies(textureBindings, mRenderTarget);
}
const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
DALI_ASSERT_ALWAYS(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex]);
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->BindSamplers(samplerBindings);
}
}
}
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->BeginRenderPass(vk::RenderPassBeginInfo{}
.setFramebuffer(framebuffer->GetVkHandle())
.setRenderPass(renderPassImpl->GetVkHandle())
const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
DALI_ASSERT_ALWAYS(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex]);
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->EndRenderPass();
}
DALI_ASSERT_ALWAYS(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex]);
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->ReadPixels(buffer);
}
{
vkCommandBuffers.push_back(ConstGraphicsCast<CommandBuffer, Graphics::CommandBuffer>(gfxCmdBuf)->GetImpl()->GetVkHandle());
}
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->ExecuteCommandBuffers(vkCommandBuffers);
}
const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
DALI_ASSERT_ALWAYS(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex]);
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->Draw(vertexCount, instanceCount, firstVertex, firstInstance);
}
const uint32_t bufferIndex = mController.GetGraphicsDevice().GetCurrentBufferIndex();
DALI_ASSERT_ALWAYS(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex]);
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->DrawIndexed(indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);
}
DALI_ASSERT_ALWAYS(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex]);
auto buffer = ConstGraphicsCast<Buffer, Graphics::Buffer>(&gfxBuffer)->GetImpl();
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->DrawIndexedIndirect(*buffer, offset, drawCount, stride);
}
if(SetDynamicState(mDynamicState.scissor, value, DynamicStateMaskBits::SCISSOR))
{
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->SetScissor(value);
}
}
if(SetDynamicState(mDynamicState.viewport, correctedValue, DynamicStateMaskBits::VIEWPORT))
{
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->SetViewport(correctedValue);
}
}
if(SetDynamicState(mDynamicState.stencilTest, stencilEnable, DynamicStateMaskBits::STENCIL_TEST))
{
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->SetStencilTestEnable(stencilEnable);
}
}
if(SetDynamicState(mDynamicState.stencilWriteMask, writeMask, DynamicStateMaskBits::STENCIL_WRITE_MASK))
{
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->SetStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, writeMask);
}
}
if(SetDynamicState(mDynamicState.stencilCompareMask, compareMask, DynamicStateMaskBits::STENCIL_COMP_MASK))
{
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->SetStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, compareMask);
}
if(SetDynamicState(mDynamicState.stencilReference, reference, DynamicStateMaskBits::STENCIL_REF))
{
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->SetStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, reference);
}
SetDynamicState(mDynamicState.stencilDepthFailOp, depthFailOp, DynamicStateMaskBits::STENCIL_OP_DEPTH_FAIL) ||
SetDynamicState(mDynamicState.stencilCompareOp, compareOp, DynamicStateMaskBits::STENCIL_OP_COMP))
{
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->SetStencilOp(vk::StencilFaceFlagBits::eFrontAndBack,
VkStencilOpType(failOp).op,
VkStencilOpType(passOp).op,
DALI_ASSERT_ALWAYS(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex]);
if(SetDynamicState(mDynamicState.depthCompareOp, compareOp, DynamicStateMaskBits::DEPTH_OP_COMP))
{
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->SetDepthCompareOp(VkCompareOpType(compareOp).op);
}
}
DALI_ASSERT_ALWAYS(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex]);
if(SetDynamicState(mDynamicState.depthTest, depthTestEnable, DynamicStateMaskBits::DEPTH_TEST))
{
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->SetDepthTestEnable(depthTestEnable);
}
}
DALI_ASSERT_ALWAYS(bufferIndex < mCommandBufferImpl.size() && mCommandBufferImpl[bufferIndex]);
if(SetDynamicState(mDynamicState.depthWrite, depthWriteEnable, DynamicStateMaskBits::DEPTH_WRITE))
{
+ mCmdCount++; // Debug info
mCommandBufferImpl[bufferIndex]->SetDepthWriteEnable(depthWriteEnable);
}
}
std::vector<CommandBufferImpl*> mCommandBufferImpl; ///< There are as many elements as there are swapchain images
RenderTarget* mRenderTarget{nullptr};
Swapchain* mLastSwapchain{nullptr};
+ uint32_t mCmdCount{0u};
};
} // namespace Dali::Graphics::Vulkan
if(!mTextureStagingBuffer ||
mTextureStagingBuffer->GetImpl()->GetSize() < size)
{
- auto workerFunc = [&, size](auto workerIndex)
- {
+ auto workerFunc = [&, size](auto workerIndex) {
Graphics::BufferCreateInfo createInfo{};
createInfo.SetSize(size)
.SetUsage(0u | Dali::Graphics::BufferUsage::TRANSFER_SRC);
}
assert(image);
- auto predicate = [&](auto& item) -> bool
- {
+ auto predicate = [&](auto& item) -> bool {
return image->GetVkHandle() == item.image.GetVkHandle();
};
auto it = std::find_if(requestMap.begin(), requestMap.end(), predicate);
void VulkanGraphicsController::FrameStart()
{
mImpl->mDependencyChecker.Reset(); // Clean down the dependency graph.
-
mImpl->mCapacity = 0;
+
+ DALI_LOG_INFO(gVulkanFilter, Debug::Verbose, "FrameStart: bufferIndex:%u\n", mImpl->mGraphicsDevice->GetCurrentBufferIndex());
// Check the size of the discard queues.
auto bufferCount = mImpl->mGraphicsDevice->GetBufferCount();
mImpl->mDiscardQueues.Resize(bufferCount);
void VulkanGraphicsController::SubmitCommandBuffers(const SubmitInfo& submitInfo)
{
std::vector<SubmissionData> submitData;
+ DALI_LOG_INFO(gVulkanFilter, Debug::Verbose, "SubmitCommandBuffers() bufferIndex:%d\n", mImpl->mGraphicsDevice->GetCurrentBufferIndex());
// Gather all command buffers targeting frame buffers into a single Submit request
for(auto gfxCmdBuffer : submitInfo.cmdBuffer)
DALI_ASSERT_DEBUG(renderTarget && "Cmd buffer has no render target set.");
if(renderTarget && renderTarget->GetSurface() == nullptr)
{
+ DALI_LOG_INFO(gVulkanFilter, Debug::Verbose, "CreateSubmissionData: FBO CmdBuffer:%p\n", cmdBuffer->GetImpl());
renderTarget->CreateSubmissionData(cmdBuffer, submitData);
}
}
- mImpl->mGraphicsDevice->GetGraphicsQueue(0).Submit(submitData, nullptr);
+ if(!submitData.empty())
+ {
+ mImpl->mGraphicsDevice->GetGraphicsQueue(0).Submit(submitData, nullptr);
+ }
// Submit each scene's cmd buffer separately, as these use EndOfFrameFence.
for(auto gfxCmdBuffer : submitInfo.cmdBuffer)
auto surfaceId = static_cast<Internal::Adaptor::WindowRenderSurface*>(surface)->GetSurfaceId();
auto swapchain = GetGraphicsDevice().GetSwapchainForSurfaceId(surfaceId);
+ DALI_LOG_INFO(gVulkanFilter, Debug::Verbose, "CreateSubmissionData: Surface CmdBuffer:%p\n", cmdBuffer->GetImpl());
renderTarget->CreateSubmissionData(cmdBuffer, submitData);
swapchain->GetQueue()->Submit(submitData, swapchain->GetEndOfFrameFence());
}
void VulkanGraphicsController::PresentRenderTarget(Graphics::RenderTarget* renderTarget)
{
- if(const auto surface = static_cast<Vulkan::RenderTarget*>(renderTarget)->GetSurface())
+ if(auto surface = static_cast<Vulkan::RenderTarget*>(renderTarget)->GetSurface())
{
const auto surfaceId = static_cast<Internal::Adaptor::WindowRenderSurface*>(surface)->GetSurfaceId();
auto swapchain = mImpl->mGraphicsDevice->GetSwapchainForSurfaceId(surfaceId);
swapchain->Present();
+ surface->PostRender();
}
// else no presentation required for framebuffer render target.
}
if(destTexture->GetProperties().directWriteAccessEnabled)
{
- auto taskLambda = [pInfo, sourcePtr, sourceInfoPtr, texture](auto workerIndex)
- {
+ auto taskLambda = [pInfo, sourcePtr, sourceInfoPtr, texture](auto workerIndex) {
const auto& properties = texture->GetProperties();
if(properties.emulated)
// The staging buffer is not allocated yet. The task knows pointer to the pointer which will point
// at staging buffer right before executing tasks. The function will either perform direct copy
// or will do suitable conversion if source format isn't supported and emulation is available.
- auto taskLambda = [ppStagingMemory, currentOffset, pInfo, sourcePtr, texture](auto workerThread)
- {
+ auto taskLambda = [ppStagingMemory, currentOffset, pInfo, sourcePtr, texture](auto workerThread) {
char* pStagingMemory = reinterpret_cast<char*>(*ppStagingMemory);
// Try to initialise` texture resources explicitly if they are not yet initialised
for(auto& item : updateMap)
{
auto pUpdates = &item.second;
- auto task = [pUpdates](auto workerIndex)
- {
+ auto task = [pUpdates](auto workerIndex) {
for(auto& update : *pUpdates)
{
update.copyTask(workerIndex);
namespace Dali::Graphics::Vulkan
{
-
/**
* SwapchainBuffer stores all per-buffer data
*/
auto presentModes = surface->GetSurfacePresentModes();
auto found = std::find_if(presentModes.begin(),
presentModes.end(),
- [&](vk::PresentModeKHR mode)
- {
+ [&](vk::PresentModeKHR mode) {
return presentMode == mode;
});
depthAttachment,
mSwapchainCreateInfoKHR.imageExtent.width,
mSwapchainCreateInfoKHR.imageExtent.height),
- [](FramebufferImpl* framebuffer1)
- {
+ [](FramebufferImpl* framebuffer1) {
framebuffer1->Destroy();
delete framebuffer1;
});
// prevent from using invalid swapchain
if(!mIsValid)
{
- DALI_LOG_INFO(gVulkanFilter, Debug::General, "Attempt to present invalid/expired swapchain: %p\n", static_cast<VkSwapchainKHR>(mSwapchainKHR));
+ DALI_LOG_INFO(gVulkanFilter, Debug::General, "Attempt to acquire from invalid/expired swapchain: %p\n", static_cast<VkSwapchainKHR>(mSwapchainKHR));
return nullptr;
}
constexpr auto TIMEOUT = 1'000'000'000; //'
- auto& swapchainBuffer = mSwapchainBuffers[mGraphicsDevice.GetCurrentBufferIndex()];
+ auto& swapchainBuffer = mSwapchainBuffers[GetCurrentBufferIndex()];
// First frames don't need waiting as they haven't been submitted
// yet. Note, that waiting on the fence without resetting it may
FenceImpl* Swapchain::GetEndOfFrameFence()
{
- auto& swapchainBuffer = mSwapchainBuffers[mGraphicsDevice.GetCurrentBufferIndex()];
+ auto& swapchainBuffer = mSwapchainBuffers[GetCurrentBufferIndex()];
return swapchainBuffer->endOfFrameFence.get();
}
std::vector<vk::PipelineStageFlags>& waitDstStageMask,
std::vector<SubmissionData>& submissionData)
{
- auto& swapchainBuffer = mSwapchainBuffers[mGraphicsDevice.GetCurrentBufferIndex()];
+ auto& swapchainBuffer = mSwapchainBuffers[GetCurrentBufferIndex()];
swapchainBuffer->endOfFrameFence->Reset();
swapchainBuffer->submitted = true;
submissionData.emplace_back(SubmissionData{waitSemaphores, waitDstStageMask, {commandBuffer}, {swapchainBuffer->submitSemaphore}});
}
+uint32_t Swapchain::GetCurrentBufferIndex() const
+{
+ return mSwapchainBuffers.empty() ? 0 : mFrameCounter % mSwapchainBuffers.size();
+}
+
void Swapchain::Present()
{
// prevent from using invalid swapchain
return;
}
- auto& swapchainBuffer = mSwapchainBuffers[mGraphicsDevice.GetCurrentBufferIndex()];
+ auto& swapchainBuffer = mSwapchainBuffers[GetCurrentBufferIndex()];
+
+ DALI_LOG_INFO(gVulkanFilter, Debug::Verbose, "Vulkan::Swapchain::Present() work submitted:%s\n", swapchainBuffer->submitted ? "True" : "False");
// Only present if we've submitted work
if(swapchainBuffer->submitted)
return mSurface;
}
+ [[nodiscard]] uint32_t GetCurrentBufferIndex() const;
+
private:
void CreateVkSwapchain(
vk::SwapchainKHR oldSwapchain,
Queue* mQueue;
SurfaceImpl* mSurface{};
- uint32_t mSwapchainImageIndex{}; ///< Swapchain image index returned by vkAcquireNextImageKHR
-
vk::SwapchainKHR mSwapchainKHR;
vk::SwapchainCreateInfoKHR mSwapchainCreateInfoKHR{};
* Array of swapchain buffers
*/
std::vector<std::unique_ptr<SwapchainBuffer>> mSwapchainBuffers;
- uint32_t mFrameCounter{0u}; ///< Current frame number
+ uint32_t mFrameCounter{0u}; ///< Current frame number
+ uint32_t mSwapchainImageIndex{}; ///< Swapchain image index returned by vkAcquireNextImageKHR
bool mIsValid; // indicates whether the swapchain is still valid or requires to be recreated
};
}
mSurfaceMap.clear();
- SwapBuffers();
ReleaseCommandPools();
if(mVmaAllocator)
uint32_t Device::GetCurrentBufferIndex() const
{
- return mCurrentBufferIndex;
+ uint32_t bufferIndex = 0;
+ if(!mSurfaceMap.empty())
+ {
+ // Use the main window's buffer index.
+ bufferIndex = mSurfaceMap.begin()->second.swapchain->GetCurrentBufferIndex();
+ }
+ return bufferIndex;
}
uint32_t Device::GetBufferCount() const
return mBufferCount;
}
-uint32_t Device::SwapBuffers()
-{
- // Increase the current buffer index. This should match the number of swapchain images in the main window.
- mCurrentBufferIndex = (mCurrentBufferIndex + 1) % mBufferCount;
- return mCurrentBufferIndex;
-}
-
void Device::CreateInstance(const std::vector<const char*>& extensions,
const std::vector<const char*>& validationLayers)
{
uint32_t GetBufferCount() const;
- uint32_t SwapBuffers();
-
const vk::PhysicalDeviceMemoryProperties& GetMemoryProperties() const
{
return mPhysicalDeviceMemoryProperties;
Graphics::SurfaceId mBaseSurfaceId{0u};
Platform mPlatform{Platform::UNDEFINED};
- uint32_t mCurrentBufferIndex{0u};
uint32_t mBufferCount{2};
std::mutex mMutex;
void VulkanGraphics::PostRender()
{
- mGraphicsDevice.SwapBuffers();
}
void VulkanGraphics::Shutdown()
void VulkanGraphics::SwapBuffers(Graphics::SurfaceId surfaceId)
{
- // Swapchain update comes from a different place in Vulkan backend
}
void VulkanGraphics::SwapBuffers(Graphics::SurfaceId surfaceId, const std::vector<Rect<int>>& damageRects)
{
- // Swapchain update comes from a different place in Vulkan backend
}
Dali::Graphics::Controller& VulkanGraphics::GetController()
{
Dali::Mutex::ScopedLock lock(mMutex);
- auto frameCallbackInfo = std::find_if(mFrameCallbackInfoContainer.begin(), mFrameCallbackInfoContainer.end(), [fileDescriptor](std::unique_ptr<FrameCallbackInfo>& callbackInfo)
- { return callbackInfo->fileDescriptor == fileDescriptor; });
+ auto frameCallbackInfo = std::find_if(mFrameCallbackInfoContainer.begin(), mFrameCallbackInfoContainer.end(), [fileDescriptor](std::unique_ptr<FrameCallbackInfo>& callbackInfo) { return callbackInfo->fileDescriptor == fileDescriptor; });
if(frameCallbackInfo != mFrameCallbackInfoContainer.end())
{
callbackInfo = std::move(*frameCallbackInfo);
void WindowRenderSurface::SwapBuffers(const std::vector<Rect<int>>& damagedRects)
{
- // @todo Need to do this differently as Vulkan does not need this; it's only required for GLES
-
if(Integration::PartialUpdateAvailable::FALSE == mGraphics->GetPartialUpdateRequired() ||
mFullSwapNextFrame)
{