X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali%2Finternal%2Frender%2Fcommon%2Frender-manager.cpp;h=ba07eac79f938dc82a5939481742b9f34ddc55cb;hb=6c5c060ed2522dd0df1de807296079c9a18c1ee5;hp=56495cc4693a4c4ecac8327f27467ea1c8595fae;hpb=c730e6ed274d11a6ae8196d4d2812fc05c64bc23;p=platform%2Fcore%2Fuifw%2Fdali-core.git diff --git a/dali/internal/render/common/render-manager.cpp b/dali/internal/render/common/render-manager.cpp index 56495cc..ba07eac 100644 --- a/dali/internal/render/common/render-manager.cpp +++ b/dali/internal/render/common/render-manager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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. @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -35,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +44,7 @@ #include #include -#include +#include namespace Dali { @@ -57,6 +59,44 @@ Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_REN } // unnamed namespace #endif +namespace +{ +inline Graphics::Rect2D RecalculateScissorArea(Graphics::Rect2D scissorArea, int orientation, Rect viewportRect) +{ + Graphics::Rect2D newScissorArea; + + if(orientation == 90) + { + newScissorArea.x = viewportRect.height - (scissorArea.y + scissorArea.height); + newScissorArea.y = scissorArea.x; + newScissorArea.width = scissorArea.height; + newScissorArea.height = scissorArea.width; + } + else if(orientation == 180) + { + newScissorArea.x = viewportRect.width - (scissorArea.x + scissorArea.width); + newScissorArea.y = viewportRect.height - (scissorArea.y + scissorArea.height); + newScissorArea.width = scissorArea.width; + newScissorArea.height = scissorArea.height; + } + else if(orientation == 270) + { + newScissorArea.x = scissorArea.y; + newScissorArea.y = viewportRect.width - (scissorArea.x + scissorArea.width); + newScissorArea.width = scissorArea.height; + newScissorArea.height = scissorArea.width; + } + else + { + newScissorArea.x = scissorArea.x; + newScissorArea.y = scissorArea.y; + newScissorArea.width = scissorArea.width; + newScissorArea.height = scissorArea.height; + } + return newScissorArea; +} +} // namespace + /** * Structure to contain internal data */ @@ -83,10 +123,11 @@ struct RenderManager::Impl partialUpdateAvailable(partialUpdateAvailableParam) { // Create thread pool with just one thread ( there may be a need to create more threads in the future ). - threadPool = std::unique_ptr(new Dali::ThreadPool()); + threadPool = std::make_unique(); threadPool->Initialize(1u); - uniformBufferManager.reset(new Render::UniformBufferManager(&graphicsController)); + uniformBufferManager = std::make_unique(&graphicsController); + pipelineCache = std::make_unique(graphicsController); } ~Impl() @@ -139,6 +180,7 @@ struct RenderManager::Impl Render::ShaderCache shaderCache; ///< The cache for the graphics shaders std::unique_ptr uniformBufferManager; ///< The uniform buffer manager + std::unique_ptr pipelineCache; Integration::DepthBufferAvailable depthBufferAvailable; ///< Whether the depth buffer is available Integration::StencilBufferAvailable stencilBufferAvailable; ///< Whether the stencil buffer is available @@ -147,6 +189,8 @@ struct RenderManager::Impl std::unique_ptr threadPool; ///< The thread pool Vector boundTextures; ///< The textures bound for rendering Vector textureDependencyList; ///< The dependency list of bound textures + + bool commandBufferSubmitted{false}; }; RenderManager* RenderManager::New(Graphics::Controller& graphicsController, @@ -184,7 +228,7 @@ void RenderManager::SetShaderSaver(ShaderSaver& upstream) void RenderManager::AddRenderer(OwnerPointer& renderer) { // Initialize the renderer as we are now in render thread - renderer->Initialize(mImpl->graphicsController, mImpl->programController, mImpl->shaderCache, *(mImpl->uniformBufferManager.get())); + renderer->Initialize(mImpl->graphicsController, mImpl->programController, mImpl->shaderCache, *(mImpl->uniformBufferManager.get()), *(mImpl->pipelineCache.get())); mImpl->rendererContainer.PushBack(renderer.Release()); } @@ -215,15 +259,13 @@ void RenderManager::RemoveTexture(Render::Texture* texture) { DALI_ASSERT_DEBUG(NULL != texture); - // Find the texture, use reference to pointer so we can do the erase safely - for(auto&& iter : mImpl->textureContainer) + // Find the texture, use std::find so we can do the erase safely + auto iter = std::find(mImpl->textureContainer.begin(), mImpl->textureContainer.end(), texture); + + if(iter != mImpl->textureContainer.end()) { - if(iter == texture) - { - texture->Destroy(); - mImpl->textureContainer.Erase(&iter); // Texture found; now destroy it - return; - } + texture->Destroy(); + mImpl->textureContainer.Erase(iter); // Texture found; now destroy it } } @@ -261,16 +303,13 @@ void RenderManager::RemoveFrameBuffer(Render::FrameBuffer* frameBuffer) { DALI_ASSERT_DEBUG(nullptr != frameBuffer); - // Find the sampler, use reference so we can safely do the erase - for(auto&& iter : mImpl->frameBufferContainer) - { - if(iter == frameBuffer) - { - frameBuffer->Destroy(); - mImpl->frameBufferContainer.Erase(&iter); // frameBuffer found; now destroy it + // Find the framebuffer, use std:find so we can safely do the erase + auto iter = std::find(mImpl->frameBufferContainer.begin(), mImpl->frameBufferContainer.end(), frameBuffer); - break; - } + if(iter != mImpl->frameBufferContainer.end()) + { + frameBuffer->Destroy(); + mImpl->frameBufferContainer.Erase(iter); // frameBuffer found; now destroy it } } @@ -309,6 +348,11 @@ void RenderManager::AttachDepthStencilTextureToFrameBuffer(Render::FrameBuffer* frameBuffer->AttachDepthStencilTexture(texture, mipmapLevel); } +void RenderManager::SetMultiSamplingLevelToFrameBuffer(Render::FrameBuffer* frameBuffer, uint8_t multiSamplingLevel) +{ + frameBuffer->SetMultiSamplingLevel(multiSamplingLevel); +} + void RenderManager::AddVertexBuffer(OwnerPointer& vertexBuffer) { mImpl->vertexBufferContainer.PushBack(vertexBuffer.Release()); @@ -341,7 +385,12 @@ void RenderManager::AddGeometry(OwnerPointer& geometry) void RenderManager::RemoveGeometry(Render::Geometry* geometry) { - mImpl->geometryContainer.EraseObject(geometry); + auto iter = std::find(mImpl->geometryContainer.begin(), mImpl->geometryContainer.end(), geometry); + + if(iter != mImpl->geometryContainer.end()) + { + mImpl->geometryContainer.Erase(iter); + } } void RenderManager::AttachVertexBuffer(Render::Geometry* geometry, Render::VertexBuffer* vertexBuffer) @@ -389,7 +438,7 @@ void RenderManager::RemoveRenderTracker(Render::RenderTracker* renderTracker) mImpl->RemoveRenderTracker(renderTracker); } -void RenderManager::PreRender(Integration::RenderStatus& status, bool forceClear, bool uploadOnly) +void RenderManager::PreRender(Integration::RenderStatus& status, bool forceClear) { DALI_PRINT_RENDER_START(mImpl->renderBufferIndex); @@ -418,45 +467,13 @@ void RenderManager::PreRender(Integration::RenderStatus& status, bool forceClear DALI_LOG_INFO(gLogFilter, Debug::General, "Render: Processing\n"); // Upload the geometries - for(auto& i : mImpl->sceneContainer) + for(auto&& geom : mImpl->geometryContainer) { - RenderInstructionContainer& instructions = i->GetRenderInstructions(); - for(uint32_t j = 0; j < instructions.Count(mImpl->renderBufferIndex); ++j) - { - RenderInstruction& instruction = instructions.At(mImpl->renderBufferIndex, j); - - const Matrix* viewMatrix = instruction.GetViewMatrix(mImpl->renderBufferIndex); - const Matrix* projectionMatrix = instruction.GetProjectionMatrix(mImpl->renderBufferIndex); - - DALI_ASSERT_DEBUG(viewMatrix); - DALI_ASSERT_DEBUG(projectionMatrix); - - if(viewMatrix && projectionMatrix) - { - const RenderListContainer::SizeType renderListCount = instruction.RenderListCount(); - - // Iterate through each render list. - for(RenderListContainer::SizeType index = 0; index < renderListCount; ++index) - { - const RenderList* renderList = instruction.GetRenderList(index); - - if(renderList && !renderList->IsEmpty()) - { - const std::size_t itemCount = renderList->Count(); - for(uint32_t itemIndex = 0u; itemIndex < itemCount; ++itemIndex) - { - const RenderItem& item = renderList->GetItem(itemIndex); - if(DALI_LIKELY(item.mRenderer)) - { - item.mRenderer->Upload(); - } - } - } - } - } - } + geom->Upload(mImpl->graphicsController); } } + + mImpl->commandBufferSubmitted = false; } void RenderManager::PreRender(Integration::Scene& scene, std::vector>& damagedRects) @@ -478,8 +495,9 @@ void RenderManager::PreRender(Integration::Scene& scene, std::vector>& class DamagedRectsCleaner { public: - explicit DamagedRectsCleaner(std::vector>& damagedRects) + explicit DamagedRectsCleaner(std::vector>& damagedRects, Rect& surfaceRect) : mDamagedRects(damagedRects), + mSurfaceRect(surfaceRect), mCleanOnReturn(true) { } @@ -494,22 +512,25 @@ void RenderManager::PreRender(Integration::Scene& scene, std::vector>& if(mCleanOnReturn) { mDamagedRects.clear(); + mDamagedRects.push_back(mSurfaceRect); } } private: std::vector>& mDamagedRects; + Rect mSurfaceRect; bool mCleanOnReturn; }; Rect surfaceRect = sceneObject->GetSurfaceRect(); // Clean collected dirty/damaged rects on exit if 3d layer or 3d node or other conditions. - DamagedRectsCleaner damagedRectCleaner(damagedRects); + DamagedRectsCleaner damagedRectCleaner(damagedRects, surfaceRect); + bool cleanDamagedRect = false; // Mark previous dirty rects in the sorted array. The array is already sorted by node and renderer, frame number. // so you don't need to sort: std::stable_sort(itemsDirtyRects.begin(), itemsDirtyRects.end()); - std::vector& itemsDirtyRects = sceneInternal.GetItemsDirtyRects(); + std::vector& itemsDirtyRects = sceneObject->GetItemsDirtyRects(); for(DirtyRect& dirtyRect : itemsDirtyRects) { dirtyRect.visited = false; @@ -522,37 +543,36 @@ void RenderManager::PreRender(Integration::Scene& scene, std::vector>& if(instruction.mFrameBuffer) { - return; // TODO: reset, we don't deal with render tasks with framebuffers (for now) + cleanDamagedRect = true; + continue; // TODO: reset, we don't deal with render tasks with framebuffers (for now) } const Camera* camera = instruction.GetCamera(); - if(camera->mType == Camera::DEFAULT_TYPE && camera->mTargetPosition == Camera::DEFAULT_TARGET_POSITION) + if(camera && camera->mType == Camera::DEFAULT_TYPE && camera->mTargetPosition == Camera::DEFAULT_TARGET_POSITION) { - const Node* node = instruction.GetCamera()->GetNode(); - if(node) + Vector3 position; + Vector3 scale; + Quaternion orientation; + camera->GetWorldMatrix(mImpl->renderBufferIndex).GetTransformComponents(position, orientation, scale); + + Vector3 orientationAxis; + Radian orientationAngle; + orientation.ToAxisAngle(orientationAxis, orientationAngle); + + if(position.x > Math::MACHINE_EPSILON_10000 || + position.y > Math::MACHINE_EPSILON_10000 || + orientationAxis != Vector3(0.0f, 1.0f, 0.0f) || + orientationAngle != ANGLE_180 || + scale != Vector3(1.0f, 1.0f, 1.0f)) { - Vector3 position; - Vector3 scale; - Quaternion orientation; - node->GetWorldMatrix(mImpl->renderBufferIndex).GetTransformComponents(position, orientation, scale); - - Vector3 orientationAxis; - Radian orientationAngle; - orientation.ToAxisAngle(orientationAxis, orientationAngle); - - if(position.x > Math::MACHINE_EPSILON_10000 || - position.y > Math::MACHINE_EPSILON_10000 || - orientationAxis != Vector3(0.0f, 1.0f, 0.0f) || - orientationAngle != ANGLE_180 || - scale != Vector3(1.0f, 1.0f, 1.0f)) - { - return; - } + cleanDamagedRect = true; + continue; } } else { - return; + cleanDamagedRect = true; + continue; } Rect viewportRect; @@ -562,7 +582,8 @@ void RenderManager::PreRender(Integration::Scene& scene, std::vector>& viewportRect.Set(instruction.mViewport.x, y, instruction.mViewport.width, instruction.mViewport.height); if(viewportRect.IsEmpty() || !viewportRect.IsValid()) { - return; // just skip funny use cases for now, empty viewport means it is set somewhere else + cleanDamagedRect = true; + continue; // just skip funny use cases for now, empty viewport means it is set somewhere else } } else @@ -578,85 +599,97 @@ void RenderManager::PreRender(Integration::Scene& scene, std::vector>& for(RenderListContainer::SizeType index = 0u; index < count; ++index) { const RenderList* renderList = instruction.GetRenderList(index); - if(renderList && !renderList->IsEmpty()) + if(renderList) { - const std::size_t listCount = renderList->Count(); - for(uint32_t listIndex = 0u; listIndex < listCount; ++listIndex) + if(!renderList->IsEmpty()) { - RenderItem& item = renderList->GetItem(listIndex); - // If the item does 3D transformation, do early exit and clean the damaged rect array - if(item.mUpdateSize == Vector3::ZERO) + const std::size_t listCount = renderList->Count(); + for(uint32_t listIndex = 0u; listIndex < listCount; ++listIndex) { - return; - } + RenderItem& item = renderList->GetItem(listIndex); + // If the item does 3D transformation, make full update + if(item.mUpdateArea == Vector4::ZERO) + { + cleanDamagedRect = true; - Rect rect; - DirtyRect dirtyRect(item.mNode, item.mRenderer, mImpl->frameCount, rect); - // If the item refers to updated node or renderer. - if(item.mIsUpdated || - (item.mNode && - (item.mNode->Updated() || (item.mRenderer && item.mRenderer->Updated(mImpl->renderBufferIndex, item.mNode))))) - { - item.mIsUpdated = false; - item.mNode->SetUpdated(false); + // Save the full rect in the damaged list. We need it when this item is removed + DirtyRect dirtyRect(item.mNode, item.mRenderer, surfaceRect); + auto dirtyRectPos = std::lower_bound(itemsDirtyRects.begin(), itemsDirtyRects.end(), dirtyRect); + if(dirtyRectPos != itemsDirtyRects.end() && dirtyRectPos->node == item.mNode && dirtyRectPos->renderer == item.mRenderer) + { + // Replace the rect + dirtyRectPos->visited = true; + dirtyRectPos->rect = dirtyRect.rect; + } + else + { + // Else, just insert the new dirtyrect in the correct position + itemsDirtyRects.insert(dirtyRectPos, dirtyRect); + } + continue; + } - rect = item.CalculateViewportSpaceAABB(item.mUpdateSize, viewportRect.width, viewportRect.height); - if(rect.IsValid() && rect.Intersect(viewportRect) && !rect.IsEmpty()) + Rect rect; + DirtyRect dirtyRect(item.mNode, item.mRenderer, rect); + // If the item refers to updated node or renderer. + if(item.mIsUpdated || + (item.mNode && + (item.mNode->Updated() || (item.mRenderer && item.mRenderer->Updated(mImpl->renderBufferIndex))))) { - const int left = rect.x; - const int top = rect.y; - const int right = rect.x + rect.width; - const int bottom = rect.y + rect.height; - rect.x = (left / 16) * 16; - rect.y = (top / 16) * 16; - rect.width = ((right + 16) / 16) * 16 - rect.x; - rect.height = ((bottom + 16) / 16) * 16 - rect.y; - - // Found valid dirty rect. - // 1. Insert it in the sorted array of the dirty rects. - // 2. Mark the related dirty rects as visited so they will not be removed below. - // 3. Keep only last 3 dirty rects for the same node and renderer (Tizen uses 3 back buffers, Ubuntu 1). - dirtyRect.rect = rect; - auto dirtyRectPos = std::lower_bound(itemsDirtyRects.begin(), itemsDirtyRects.end(), dirtyRect); - dirtyRectPos = itemsDirtyRects.insert(dirtyRectPos, dirtyRect); + item.mIsUpdated = false; - int c = 1; - while(++dirtyRectPos != itemsDirtyRects.end()) + Vector4 updateArea = item.mRenderer ? item.mRenderer->GetVisualTransformedUpdateArea(mImpl->renderBufferIndex, item.mUpdateArea) : item.mUpdateArea; + + rect = RenderItem::CalculateViewportSpaceAABB(item.mModelViewMatrix, Vector3(updateArea.x, updateArea.y, 0.0f), Vector3(updateArea.z, updateArea.w, 0.0f), viewportRect.width, viewportRect.height); + if(rect.IsValid() && rect.Intersect(viewportRect) && !rect.IsEmpty()) { - if(dirtyRectPos->node != item.mNode || dirtyRectPos->renderer != item.mRenderer) + const int left = rect.x; + const int top = rect.y; + const int right = rect.x + rect.width; + const int bottom = rect.y + rect.height; + rect.x = (left / 16) * 16; + rect.y = (top / 16) * 16; + rect.width = ((right + 16) / 16) * 16 - rect.x; + rect.height = ((bottom + 16) / 16) * 16 - rect.y; + + // Found valid dirty rect. + dirtyRect.rect = rect; + auto dirtyRectPos = std::lower_bound(itemsDirtyRects.begin(), itemsDirtyRects.end(), dirtyRect); + + if(dirtyRectPos != itemsDirtyRects.end() && dirtyRectPos->node == item.mNode && dirtyRectPos->renderer == item.mRenderer) { - break; - } - - dirtyRectPos->visited = true; - Rect& dirtRect = dirtyRectPos->rect; - rect.Merge(dirtRect); + // Same item, merge it with the previous rect + rect.Merge(dirtyRectPos->rect); - c++; - if(c > 3) // no more then 3 previous rects + // Replace the rect + dirtyRectPos->visited = true; + dirtyRectPos->rect = dirtyRect.rect; + } + else { - itemsDirtyRects.erase(dirtyRectPos); - break; + // Else, just insert the new dirtyrect in the correct position + itemsDirtyRects.insert(dirtyRectPos, dirtyRect); } - } - damagedRects.push_back(rect); + damagedRects.push_back(rect); + } } - } - else - { - // 1. The item is not dirty, the node and renderer referenced by the item are still exist. - // 2. Mark the related dirty rects as visited so they will not be removed below. - auto dirtyRectPos = std::lower_bound(itemsDirtyRects.begin(), itemsDirtyRects.end(), dirtyRect); - while(dirtyRectPos != itemsDirtyRects.end()) + else { - if(dirtyRectPos->node != item.mNode || dirtyRectPos->renderer != item.mRenderer) + // 1. The item is not dirty, the node and renderer referenced by the item are still exist. + // 2. Mark the related dirty rects as visited so they will not be removed below. + auto dirtyRectPos = std::lower_bound(itemsDirtyRects.begin(), itemsDirtyRects.end(), dirtyRect); + if(dirtyRectPos != itemsDirtyRects.end() && dirtyRectPos->node == item.mNode && dirtyRectPos->renderer == item.mRenderer) { - break; + dirtyRectPos->visited = true; + } + else + { + // The item is not in the list for some reason. Add it! + dirtyRect.rect = surfaceRect; + itemsDirtyRects.insert(dirtyRectPos, dirtyRect); + cleanDamagedRect = true; // And make full update at this frame } - - dirtyRectPos->visited = true; - dirtyRectPos++; } } } @@ -683,17 +716,29 @@ void RenderManager::PreRender(Integration::Scene& scene, std::vector>& } itemsDirtyRects.resize(j - itemsDirtyRects.begin()); - damagedRectCleaner.SetCleanOnReturn(false); + + if(!cleanDamagedRect) + { + damagedRectCleaner.SetCleanOnReturn(false); + } } void RenderManager::RenderScene(Integration::RenderStatus& status, Integration::Scene& scene, bool renderToFbo) { - Rect clippingRect; + SceneGraph::Scene* sceneObject = GetImplementation(scene).GetSceneObject(); + Rect clippingRect = sceneObject->GetSurfaceRect(); + RenderScene(status, scene, renderToFbo, clippingRect); } void RenderManager::RenderScene(Integration::RenderStatus& status, Integration::Scene& scene, bool renderToFbo, Rect& clippingRect) { + if(mImpl->partialUpdateAvailable == Integration::PartialUpdateAvailable::TRUE && !renderToFbo && clippingRect.IsEmpty()) + { + // ClippingRect is empty. Skip rendering + return; + } + // Reset main algorithms command buffer mImpl->renderAlgorithms.ResetCommandBuffer(); @@ -706,6 +751,18 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration:: std::vector targetstoPresent; + Rect surfaceRect = sceneObject->GetSurfaceRect(); + if(clippingRect == surfaceRect) + { + // Full rendering case + // Make clippingRect empty because we're doing full rendering now if the clippingRect is empty. + // To reduce side effects, keep this logic now. + clippingRect = Rect(); + } + + // Prepare to lock and map standalone uniform buffer. + mImpl->uniformBufferManager->ReadyToLockUniformBuffer(mImpl->renderBufferIndex); + for(uint32_t i = 0; i < count; ++i) { RenderInstruction& instruction = sceneObject->GetRenderInstructions().At(mImpl->renderBufferIndex, i); @@ -720,8 +777,11 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration:: Rect viewportRect; - Rect surfaceRect = sceneObject->GetSurfaceRect(); - int32_t surfaceOrientation = sceneObject->GetSurfaceOrientation(); + int32_t surfaceOrientation = sceneObject->GetSurfaceOrientation() + sceneObject->GetScreenOrientation(); + if(surfaceOrientation >= 360) + { + surfaceOrientation -= 360; + } // @todo Should these be part of scene? Integration::DepthBufferAvailable depthBufferAvailable = mImpl->depthBufferAvailable; @@ -877,6 +937,11 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration:: } } + // Scissor's value should be set based on the default system coordinates. + // When the surface is rotated, the input values already were set with the rotated angle. + // So, re-calculation is needed. + scissorArea = RecalculateScissorArea(scissorArea, surfaceOrientation, surfaceRect); + // Begin render pass mainCommandBuffer->BeginRenderPass( currentRenderPass, @@ -900,7 +965,8 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration:: mImpl->boundTextures, viewportRect, clippingRect, - surfaceOrientation); + surfaceOrientation, + Uint16Pair(surfaceRect.width, surfaceRect.height)); Graphics::SyncObject* syncObject{nullptr}; // If the render instruction has an associated render tracker (owned separately) @@ -913,7 +979,12 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration:: } mainCommandBuffer->EndRenderPass(syncObject); } + + // Unlock standalone uniform buffer. + mImpl->uniformBufferManager->UnlockUniformBuffer(mImpl->renderBufferIndex); + mImpl->renderAlgorithms.SubmitCommandBuffer(); + mImpl->commandBufferSubmitted = true; std::sort(targetstoPresent.begin(), targetstoPresent.end()); @@ -928,14 +999,31 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration:: } } -void RenderManager::PostRender(bool uploadOnly) +void RenderManager::PostRender() { + if(!mImpl->commandBufferSubmitted) + { + // Rendering is skipped but there may be pending tasks. Flush them. + Graphics::SubmitInfo submitInfo; + submitInfo.cmdBuffer.clear(); // Only flush + submitInfo.flags = 0 | Graphics::SubmitFlagBits::FLUSH; + mImpl->graphicsController.SubmitCommandBuffers(submitInfo); + + mImpl->commandBufferSubmitted = true; + } + // Notify RenderGeometries that rendering has finished for(auto&& iter : mImpl->geometryContainer) { iter->OnRenderFinished(); } + // Notify RenderTexture that rendering has finished + for(auto&& iter : mImpl->textureContainer) + { + iter->OnRenderFinished(); + } + mImpl->UpdateTrackers(); uint32_t count = 0u;