2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/render/common/render-manager.h>
25 #include <dali/integration-api/core.h>
26 #include <dali/integration-api/ordered-set.h>
27 #include <dali/integration-api/trace.h>
29 #include <dali/internal/event/common/scene-impl.h>
31 #include <dali/internal/update/common/scene-graph-scene.h>
32 #include <dali/internal/update/nodes/scene-graph-layer.h>
33 #include <dali/internal/update/render-tasks/scene-graph-camera.h>
35 #include <dali/internal/common/owner-key-container.h>
37 #include <dali/internal/render/common/render-algorithms.h>
38 #include <dali/internal/render/common/render-debug.h>
39 #include <dali/internal/render/common/render-instruction.h>
40 #include <dali/internal/render/common/render-tracker.h>
41 #include <dali/internal/render/queue/render-queue.h>
42 #include <dali/internal/render/renderers/pipeline-cache.h>
43 #include <dali/internal/render/renderers/render-frame-buffer.h>
44 #include <dali/internal/render/renderers/render-texture.h>
45 #include <dali/internal/render/renderers/uniform-buffer-manager.h>
46 #include <dali/internal/render/renderers/uniform-buffer.h>
47 #include <dali/internal/render/shaders/program-controller.h>
57 #if defined(DEBUG_ENABLED)
60 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_RENDER_MANAGER");
61 } // unnamed namespace
66 constexpr uint32_t PROGRAM_CACHE_CLEAN_FRAME_COUNT = 300u; // 60fps * 5sec
68 constexpr uint32_t INITIAL_PROGRAM_CACHE_CLEAN_THRESHOLD = 64u; // Let we trigger program cache clean if the number of shader is bigger than this value
69 constexpr uint32_t MAXIMUM_PROGRAM_CACHE_CLEAN_THRESHOLD = 1024u;
71 constexpr uint32_t PROGRAM_CACHE_FORCE_CLEAN_FRAME_COUNT = PROGRAM_CACHE_CLEAN_FRAME_COUNT + MAXIMUM_PROGRAM_CACHE_CLEAN_THRESHOLD;
73 static_assert(PROGRAM_CACHE_CLEAN_FRAME_COUNT <= PROGRAM_CACHE_FORCE_CLEAN_FRAME_COUNT);
74 static_assert(MAXIMUM_PROGRAM_CACHE_CLEAN_THRESHOLD <= PROGRAM_CACHE_FORCE_CLEAN_FRAME_COUNT);
76 #if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
77 constexpr uint32_t SHRINK_TO_FIT_FRAME_COUNT = (1u << 8); ///< 256 frames. Make this value as power of 2.
80 * Flag whether property has changed, during the Render phase.
82 enum ContainerRemovedFlagBits : uint8_t
90 * @brief ContainerRemovedFlags alters behaviour of implementation
92 using ContainerRemovedFlags = uint8_t;
95 inline Graphics::Rect2D RecalculateScissorArea(const Graphics::Rect2D& scissorArea, int orientation, const Rect<int32_t>& viewportRect)
97 Graphics::Rect2D newScissorArea;
101 newScissorArea.x = viewportRect.height - (scissorArea.y + scissorArea.height);
102 newScissorArea.y = scissorArea.x;
103 newScissorArea.width = scissorArea.height;
104 newScissorArea.height = scissorArea.width;
106 else if(orientation == 180)
108 newScissorArea.x = viewportRect.width - (scissorArea.x + scissorArea.width);
109 newScissorArea.y = viewportRect.height - (scissorArea.y + scissorArea.height);
110 newScissorArea.width = scissorArea.width;
111 newScissorArea.height = scissorArea.height;
113 else if(orientation == 270)
115 newScissorArea.x = scissorArea.y;
116 newScissorArea.y = viewportRect.width - (scissorArea.x + scissorArea.width);
117 newScissorArea.width = scissorArea.height;
118 newScissorArea.height = scissorArea.width;
122 newScissorArea.x = scissorArea.x;
123 newScissorArea.y = scissorArea.y;
124 newScissorArea.width = scissorArea.width;
125 newScissorArea.height = scissorArea.height;
127 return newScissorArea;
130 inline Rect<int32_t> CalculateUpdateArea(RenderItem& item, BufferIndex renderBufferIndex, const Rect<int32_t>& viewportRect)
133 if(item.mNode && item.mNode->IsTextureUpdateAreaUsed() && item.mRenderer)
135 updateArea = item.mRenderer->GetTextureUpdateArea();
139 updateArea = item.mRenderer ? item.mRenderer->GetVisualTransformedUpdateArea(renderBufferIndex, item.mUpdateArea) : item.mUpdateArea;
142 return RenderItem::CalculateViewportSpaceAABB(item.mModelViewMatrix, Vector3(updateArea.x, updateArea.y, 0.0f), Vector3(updateArea.z, updateArea.w, 0.0f), viewportRect.width, viewportRect.height);
145 inline void AlignDamagedRect(Rect<int32_t>& rect)
147 const int left = rect.x;
148 const int top = rect.y;
149 const int right = rect.x + rect.width;
150 const int bottom = rect.y + rect.height;
151 rect.x = (left / 16) * 16;
152 rect.y = (top / 16) * 16;
153 rect.width = ((right + 16) / 16) * 16 - rect.x;
154 rect.height = ((bottom + 16) / 16) * 16 - rect.y;
157 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_RENDER_PROCESS, false);
161 * Structure to contain internal data
163 struct RenderManager::Impl
165 Impl(Graphics::Controller& graphicsController,
166 Integration::DepthBufferAvailable depthBufferAvailableParam,
167 Integration::StencilBufferAvailable stencilBufferAvailableParam,
168 Integration::PartialUpdateAvailable partialUpdateAvailableParam)
169 : graphicsController(graphicsController),
170 renderAlgorithms(graphicsController),
171 programController(graphicsController),
172 #if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
173 containerRemovedFlags(ContainerRemovedFlagBits::NOTHING),
175 depthBufferAvailable(depthBufferAvailableParam),
176 stencilBufferAvailable(stencilBufferAvailableParam),
177 partialUpdateAvailable(partialUpdateAvailableParam)
179 uniformBufferManager = std::make_unique<Render::UniformBufferManager>(&graphicsController);
180 pipelineCache = std::make_unique<Render::PipelineCache>(graphicsController);
185 rendererContainer.Clear(); // clear now before the program contoller and the pipeline cache are deleted
186 pipelineCache.reset(); // clear now before the program contoller is deleted
189 void AddRenderTracker(Render::RenderTracker* renderTracker)
191 DALI_ASSERT_DEBUG(renderTracker != nullptr);
192 mRenderTrackers.PushBack(renderTracker);
195 void RemoveRenderTracker(Render::RenderTracker* renderTracker)
197 mRenderTrackers.EraseObject(renderTracker);
200 void UpdateTrackers()
202 for(auto&& iter : mRenderTrackers)
204 iter->PollSyncObject();
209 * @brief Prepare to check used count of shader and program cache.
210 * It will be used when the size of shader and program cache is too big, so we need to collect garbages.
211 * Currently, we collect and remove programs only if the number of program/shader is bigger than threshold.
213 * @note Should be called at PreRender
215 void RequestProgramCacheCleanIfNeed()
217 if(programCacheCleanRequestedFrame == 0u)
219 if(DALI_UNLIKELY(programController.GetCachedProgramCount() > programCacheCleanRequiredThreshold))
221 // Mark current frame count
222 programCacheCleanRequestedFrame = frameCount;
224 DALI_LOG_RELEASE_INFO("Trigger ProgramCache GC. program : [%u]\n", programController.GetCachedProgramCount());
226 // Prepare to collect program used flag.
227 programController.ResetUsedFlag();
233 * @brief Cleanup unused program and shader cache if need.
235 * @note Should be called at PostRender
237 void ClearUnusedProgramCacheIfNeed()
239 // Remove unused shader and programs during we render PROGRAM_CACHE_CLEAN_FRAME_COUNT frames.
240 if(programCacheCleanRequestedFrame != 0u && programCacheCleanRequestedFrame + PROGRAM_CACHE_CLEAN_FRAME_COUNT - 1u <= frameCount)
242 // Clean cache incrementally, or force clean if we spend too much frames to collect them.
243 if(!programController.ClearUnusedCacheIncrementally(programCacheCleanRequestedFrame + PROGRAM_CACHE_FORCE_CLEAN_FRAME_COUNT - 1u <= frameCount))
245 // Reset current frame count.
246 programCacheCleanRequestedFrame = 0u;
248 DALI_LOG_RELEASE_INFO("ProgramCache GC finished. program : [%u]\n", programController.GetCachedProgramCount());
250 // Double up threshold
251 programCacheCleanRequiredThreshold <<= 1;
252 programCacheCleanRequiredThreshold = std::max(programCacheCleanRequiredThreshold, programController.GetCachedProgramCount());
254 if(programCacheCleanRequiredThreshold > MAXIMUM_PROGRAM_CACHE_CLEAN_THRESHOLD)
256 programCacheCleanRequiredThreshold = MAXIMUM_PROGRAM_CACHE_CLEAN_THRESHOLD;
263 * @brief Remove all owned render context.
265 * @note Should be called at ContextDestroyed case.
267 void ContextDestroyed()
269 sceneContainer.clear();
270 renderAlgorithms.DestroyCommandBuffer();
272 samplerContainer.Clear();
273 frameBufferContainer.Clear();
274 vertexBufferContainer.Clear();
275 geometryContainer.Clear();
276 rendererContainer.Clear();
277 textureContainer.Clear();
279 mRenderTrackers.Clear();
281 updatedTextures.Clear();
282 textureDiscardQueue.Clear();
284 pipelineCache.reset(); // clear now before the program contoller is deleted
286 if(DALI_LIKELY(uniformBufferManager))
288 uniformBufferManager->ContextDestroyed();
290 uniformBufferManager.reset();
293 // the order is important for destruction,
294 Graphics::Controller& graphicsController;
295 RenderQueue renderQueue; ///< A message queue for receiving messages from the update-thread.
296 std::vector<SceneGraph::Scene*> sceneContainer; ///< List of pointers to the scene graph objects of the scenes
297 Render::RenderAlgorithms renderAlgorithms; ///< The RenderAlgorithms object is used to action the renders required by a RenderInstruction
299 Integration::OrderedSet<Render::Sampler> samplerContainer; ///< List of owned samplers
300 Integration::OrderedSet<Render::FrameBuffer> frameBufferContainer; ///< List of owned framebuffers
301 Integration::OrderedSet<Render::VertexBuffer> vertexBufferContainer; ///< List of owned vertex buffers
302 Integration::OrderedSet<Render::Geometry> geometryContainer; ///< List of owned Geometries
303 OwnerKeyContainer<Render::Renderer> rendererContainer; ///< List of owned renderers
304 OwnerKeyContainer<Render::Texture> textureContainer; ///< List of owned textures
306 Integration::OrderedSet<Render::RenderTracker> mRenderTrackers; ///< List of owned render trackers
308 OwnerKeyContainer<Render::Texture> textureDiscardQueue; ///< Discarded textures
310 ProgramController programController; ///< Owner of the programs
312 std::unique_ptr<Render::UniformBufferManager> uniformBufferManager; ///< The uniform buffer manager
313 std::unique_ptr<Render::PipelineCache> pipelineCache;
315 #if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
316 ContainerRemovedFlags containerRemovedFlags; ///< cumulative container removed flags during current frame
319 Integration::DepthBufferAvailable depthBufferAvailable; ///< Whether the depth buffer is available
320 Integration::StencilBufferAvailable stencilBufferAvailable; ///< Whether the stencil buffer is available
321 Integration::PartialUpdateAvailable partialUpdateAvailable; ///< Whether the partial update is available
323 Vector<Render::TextureKey> updatedTextures{}; ///< The updated texture list
325 uint32_t frameCount{0u}; ///< The current frame count
326 BufferIndex renderBufferIndex{SceneGraphBuffers::INITIAL_UPDATE_BUFFER_INDEX}; ///< The index of the buffer to read from;
328 uint32_t programCacheCleanRequiredThreshold{INITIAL_PROGRAM_CACHE_CLEAN_THRESHOLD}; ///< The threshold to request program cache clean up operation.
329 ///< It will be automatically increased after we request cache clean.
331 uint32_t programCacheCleanRequestedFrame{0u}; ///< 0 mean, we didn't request program cache clean. otherwise, we request cache clean.
332 ///< otherwise, we request program cache clean at that frame, and now we are checking reference.
334 bool lastFrameWasRendered{false}; ///< Keeps track of the last frame being rendered due to having render instructions
335 bool commandBufferSubmitted{false};
338 RenderManager* RenderManager::New(Graphics::Controller& graphicsController,
339 Integration::DepthBufferAvailable depthBufferAvailable,
340 Integration::StencilBufferAvailable stencilBufferAvailable,
341 Integration::PartialUpdateAvailable partialUpdateAvailable)
343 auto* manager = new RenderManager;
344 manager->mImpl = new Impl(graphicsController,
345 depthBufferAvailable,
346 stencilBufferAvailable,
347 partialUpdateAvailable);
351 RenderManager::RenderManager()
356 RenderManager::~RenderManager()
360 // Ensure to release memory pool
361 Render::Renderer::ResetMemoryPool();
362 Render::Texture::ResetMemoryPool();
365 void RenderManager::ContextDestroyed()
367 // Call Destroy for some items.
368 for(auto&& item : mImpl->frameBufferContainer)
370 if(DALI_LIKELY(item))
375 for(auto&& item : mImpl->textureContainer)
377 if(DALI_LIKELY(item))
383 // Remove owned render context
384 mImpl->ContextDestroyed();
387 RenderQueue& RenderManager::GetRenderQueue()
389 return mImpl->renderQueue;
392 void RenderManager::SetShaderSaver(ShaderSaver& upstream)
396 void RenderManager::AddRenderer(const Render::RendererKey& renderer)
398 // Initialize the renderer as we are now in render thread
399 renderer->Initialize(mImpl->graphicsController, mImpl->programController, *(mImpl->uniformBufferManager.get()), *(mImpl->pipelineCache.get()));
401 mImpl->rendererContainer.PushBack(renderer);
404 void RenderManager::RemoveRenderer(const Render::RendererKey& renderer)
406 mImpl->rendererContainer.EraseKey(renderer);
407 #if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
408 mImpl->containerRemovedFlags |= ContainerRemovedFlagBits::RENDERER;
412 void RenderManager::AddSampler(OwnerPointer<Render::Sampler>& sampler)
414 sampler->Initialize(mImpl->graphicsController);
415 mImpl->samplerContainer.PushBack(sampler.Release());
418 void RenderManager::RemoveSampler(Render::Sampler* sampler)
420 mImpl->samplerContainer.EraseObject(sampler);
423 void RenderManager::AddTexture(const Render::TextureKey& textureKey)
425 DALI_ASSERT_DEBUG(textureKey && "Trying to add empty texture key");
427 textureKey->Initialize(mImpl->graphicsController, *this);
428 mImpl->textureContainer.PushBack(textureKey);
429 mImpl->updatedTextures.PushBack(textureKey);
432 void RenderManager::RemoveTexture(const Render::TextureKey& textureKey)
434 DALI_ASSERT_DEBUG(textureKey && "Trying to remove empty texture key");
436 // Find the texture, use std::find so we can do the erase by iterator safely
437 auto iter = std::find(mImpl->textureContainer.Begin(), mImpl->textureContainer.End(), textureKey);
439 if(iter != mImpl->textureContainer.End())
442 textureKey->Destroy();
444 // Transfer ownership to the discard queue, this keeps the object alive, until the render-thread has finished with it
445 mImpl->textureDiscardQueue.PushBack(mImpl->textureContainer.Release(iter));
447 #if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
448 mImpl->containerRemovedFlags |= ContainerRemovedFlagBits::TEXTURE;
453 void RenderManager::UploadTexture(const Render::TextureKey& textureKey, PixelDataPtr pixelData, const Graphics::UploadParams& params)
455 DALI_ASSERT_DEBUG(textureKey && "Trying to upload to empty texture key");
456 textureKey->Upload(pixelData, params);
458 mImpl->updatedTextures.PushBack(textureKey);
461 void RenderManager::GenerateMipmaps(const Render::TextureKey& textureKey)
463 DALI_ASSERT_DEBUG(textureKey && "Trying to generate mipmaps on empty texture key");
464 textureKey->GenerateMipmaps();
466 mImpl->updatedTextures.PushBack(textureKey);
469 void RenderManager::SetTextureSize(const Render::TextureKey& textureKey, const Dali::ImageDimensions& size)
471 DALI_ASSERT_DEBUG(textureKey && "Trying to set size on empty texture key");
472 textureKey->SetWidth(size.GetWidth());
473 textureKey->SetHeight(size.GetHeight());
476 void RenderManager::SetTextureFormat(const Render::TextureKey& textureKey, Dali::Pixel::Format pixelFormat)
478 DALI_ASSERT_DEBUG(textureKey && "Trying to set pixel format on empty texture key");
479 textureKey->SetPixelFormat(pixelFormat);
482 void RenderManager::SetTextureUpdated(const Render::TextureKey& textureKey)
484 DALI_ASSERT_DEBUG(textureKey && "Trying to set updated on empty texture key");
485 textureKey->SetUpdated(true);
487 mImpl->updatedTextures.PushBack(textureKey);
490 void RenderManager::SetFilterMode(Render::Sampler* sampler, uint32_t minFilterMode, uint32_t magFilterMode)
492 sampler->SetFilterMode(static_cast<Dali::FilterMode::Type>(minFilterMode),
493 static_cast<Dali::FilterMode::Type>(magFilterMode));
496 void RenderManager::SetWrapMode(Render::Sampler* sampler, uint32_t rWrapMode, uint32_t sWrapMode, uint32_t tWrapMode)
498 sampler->SetWrapMode(static_cast<Dali::WrapMode::Type>(rWrapMode),
499 static_cast<Dali::WrapMode::Type>(sWrapMode),
500 static_cast<Dali::WrapMode::Type>(tWrapMode));
503 void RenderManager::AddFrameBuffer(OwnerPointer<Render::FrameBuffer>& frameBuffer)
505 Render::FrameBuffer* frameBufferPtr = frameBuffer.Release();
506 mImpl->frameBufferContainer.PushBack(frameBufferPtr);
507 frameBufferPtr->Initialize(mImpl->graphicsController);
510 void RenderManager::RemoveFrameBuffer(Render::FrameBuffer* frameBuffer)
512 DALI_ASSERT_DEBUG(nullptr != frameBuffer);
514 // Find the framebuffer, use OrderedSet.Find so we can safely do the erase
515 auto iter = mImpl->frameBufferContainer.Find(frameBuffer);
517 if(iter != mImpl->frameBufferContainer.End())
519 frameBuffer->Destroy();
520 mImpl->frameBufferContainer.Erase(iter); // frameBuffer found; now destroy it
524 void RenderManager::InitializeScene(SceneGraph::Scene* scene)
526 scene->Initialize(mImpl->graphicsController, mImpl->depthBufferAvailable, mImpl->stencilBufferAvailable);
527 mImpl->sceneContainer.push_back(scene);
528 mImpl->uniformBufferManager->RegisterScene(scene);
531 void RenderManager::UninitializeScene(SceneGraph::Scene* scene)
533 mImpl->uniformBufferManager->UnregisterScene(scene);
534 auto iter = std::find(mImpl->sceneContainer.begin(), mImpl->sceneContainer.end(), scene);
535 if(iter != mImpl->sceneContainer.end())
537 mImpl->sceneContainer.erase(iter);
541 void RenderManager::SurfaceReplaced(SceneGraph::Scene* scene)
543 scene->Initialize(mImpl->graphicsController, mImpl->depthBufferAvailable, mImpl->stencilBufferAvailable);
546 void RenderManager::AttachColorTextureToFrameBuffer(Render::FrameBuffer* frameBuffer, Render::Texture* texture, uint32_t mipmapLevel, uint32_t layer)
548 frameBuffer->AttachColorTexture(texture, mipmapLevel, layer);
551 void RenderManager::AttachDepthTextureToFrameBuffer(Render::FrameBuffer* frameBuffer, Render::Texture* texture, uint32_t mipmapLevel)
553 frameBuffer->AttachDepthTexture(texture, mipmapLevel);
556 void RenderManager::AttachDepthStencilTextureToFrameBuffer(Render::FrameBuffer* frameBuffer, Render::Texture* texture, uint32_t mipmapLevel)
558 frameBuffer->AttachDepthStencilTexture(texture, mipmapLevel);
561 void RenderManager::SetMultiSamplingLevelToFrameBuffer(Render::FrameBuffer* frameBuffer, uint8_t multiSamplingLevel)
563 frameBuffer->SetMultiSamplingLevel(multiSamplingLevel);
566 void RenderManager::AddVertexBuffer(OwnerPointer<Render::VertexBuffer>& vertexBuffer)
568 mImpl->vertexBufferContainer.PushBack(vertexBuffer.Release());
571 void RenderManager::RemoveVertexBuffer(Render::VertexBuffer* vertexBuffer)
573 mImpl->vertexBufferContainer.EraseObject(vertexBuffer);
576 void RenderManager::SetVertexBufferFormat(Render::VertexBuffer* vertexBuffer, OwnerPointer<Render::VertexBuffer::Format>& format)
578 vertexBuffer->SetFormat(format.Release());
581 void RenderManager::SetVertexBufferData(Render::VertexBuffer* vertexBuffer, OwnerPointer<Vector<uint8_t>>& data, uint32_t size)
583 vertexBuffer->SetData(data.Release(), size);
586 void RenderManager::SetVertexBufferUpdateCallback(Render::VertexBuffer* vertexBuffer, Dali::VertexBufferUpdateCallback* callback)
588 vertexBuffer->SetVertexBufferUpdateCallback(callback);
591 void RenderManager::SetIndexBuffer(Render::Geometry* geometry, Render::Geometry::Uint16ContainerType& indices)
593 geometry->SetIndexBuffer(indices);
596 void RenderManager::SetIndexBuffer(Render::Geometry* geometry, Render::Geometry::Uint32ContainerType& indices)
598 geometry->SetIndexBuffer(indices);
601 void RenderManager::AddGeometry(OwnerPointer<Render::Geometry>& geometry)
603 mImpl->geometryContainer.PushBack(geometry.Release());
606 void RenderManager::RemoveGeometry(Render::Geometry* geometry)
608 mImpl->geometryContainer.EraseObject(geometry);
611 void RenderManager::AttachVertexBuffer(Render::Geometry* geometry, Render::VertexBuffer* vertexBuffer)
613 geometry->AddVertexBuffer(vertexBuffer);
616 void RenderManager::RemoveVertexBuffer(Render::Geometry* geometry, Render::VertexBuffer* vertexBuffer)
618 geometry->RemoveVertexBuffer(vertexBuffer);
621 void RenderManager::SetGeometryType(Render::Geometry* geometry, uint32_t geometryType)
623 geometry->SetType(Render::Geometry::Type(geometryType));
626 void RenderManager::AddRenderTracker(Render::RenderTracker* renderTracker)
628 mImpl->AddRenderTracker(renderTracker);
631 void RenderManager::RemoveRenderTracker(Render::RenderTracker* renderTracker)
633 mImpl->RemoveRenderTracker(renderTracker);
636 void RenderManager::PreRender(Integration::RenderStatus& status, bool forceClear)
638 DALI_PRINT_RENDER_START(mImpl->renderBufferIndex);
639 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "\n\nNewFrame %d\n", mImpl->frameCount);
641 // Increment the frame count at the beginning of each frame
644 // Process messages queued during previous update
645 mImpl->renderQueue.ProcessMessages(mImpl->renderBufferIndex);
647 uint32_t totalInstructionCount = 0u;
648 for(auto& i : mImpl->sceneContainer)
650 totalInstructionCount += i->GetRenderInstructions().Count(mImpl->renderBufferIndex);
653 const bool haveInstructions = totalInstructionCount > 0u;
655 DALI_LOG_INFO(gLogFilter, Debug::General, "Render: haveInstructions(%s) || mImpl->lastFrameWasRendered(%s) || forceClear(%s)\n", haveInstructions ? "true" : "false", mImpl->lastFrameWasRendered ? "true" : "false", forceClear ? "true" : "false");
657 // Only render if we have instructions to render, or the last frame was rendered (and therefore a clear is required).
658 if(haveInstructions || mImpl->lastFrameWasRendered || forceClear)
660 DALI_LOG_INFO(gLogFilter, Debug::General, "Render: Processing\n");
662 // Upload the geometries
663 for(auto&& geom : mImpl->geometryContainer)
665 geom->Upload(mImpl->graphicsController);
669 // Reset pipeline cache before rendering
670 mImpl->pipelineCache->PreRender();
672 // Check we need to clean up program cache
673 mImpl->RequestProgramCacheCleanIfNeed();
675 mImpl->commandBufferSubmitted = false;
678 void RenderManager::PreRender(Integration::Scene& scene, std::vector<Rect<int>>& damagedRects)
680 if(mImpl->partialUpdateAvailable != Integration::PartialUpdateAvailable::TRUE)
685 Internal::Scene& sceneInternal = GetImplementation(scene);
686 SceneGraph::Scene* sceneObject = sceneInternal.GetSceneObject();
688 if(!sceneObject || sceneObject->IsRenderingSkipped())
690 // We don't need to calculate dirty rects
693 DALI_LOG_ERROR("Scene was empty handle. Skip pre-rendering\n");
697 DALI_LOG_RELEASE_INFO("RenderingSkipped was set true. Skip pre-rendering\n");
702 class DamagedRectsCleaner
705 explicit DamagedRectsCleaner(std::vector<Rect<int>>& damagedRects, Rect<int>& surfaceRect)
706 : mDamagedRects(damagedRects),
707 mSurfaceRect(surfaceRect),
712 void SetCleanOnReturn(bool cleanOnReturn)
714 mCleanOnReturn = cleanOnReturn;
717 ~DamagedRectsCleaner()
721 mDamagedRects.clear();
722 mDamagedRects.push_back(mSurfaceRect);
727 std::vector<Rect<int>>& mDamagedRects;
728 Rect<int> mSurfaceRect;
732 Rect<int32_t> surfaceRect = sceneObject->GetSurfaceRect();
734 // Clean collected dirty/damaged rects on exit if 3d layer or 3d node or other conditions.
735 DamagedRectsCleaner damagedRectCleaner(damagedRects, surfaceRect);
736 bool cleanDamagedRect = false;
738 Scene::ItemsDirtyRectsContainer& itemsDirtyRects = sceneObject->GetItemsDirtyRects();
740 if(!sceneObject->IsPartialUpdateEnabled())
742 // Clear all dirty rects
743 // The rects will be added when partial updated is enabled again
744 itemsDirtyRects.clear();
748 // Mark previous dirty rects in the std::unordered_map.
749 for(auto& dirtyRectPair : itemsDirtyRects)
751 dirtyRectPair.second.visited = false;
754 uint32_t instructionCount = sceneObject->GetRenderInstructions().Count(mImpl->renderBufferIndex);
755 for(uint32_t i = 0; i < instructionCount; ++i)
757 RenderInstruction& instruction = sceneObject->GetRenderInstructions().At(mImpl->renderBufferIndex, i);
759 if(instruction.mFrameBuffer)
761 cleanDamagedRect = true;
762 continue; // TODO: reset, we don't deal with render tasks with framebuffers (for now)
765 const Camera* camera = instruction.GetCamera();
766 if(camera && camera->mType == Camera::DEFAULT_TYPE && camera->mTargetPosition == Camera::DEFAULT_TARGET_POSITION)
770 Quaternion orientation;
771 camera->GetWorldMatrix(mImpl->renderBufferIndex).GetTransformComponents(position, orientation, scale);
773 Vector3 orientationAxis;
774 Radian orientationAngle;
775 orientation.ToAxisAngle(orientationAxis, orientationAngle);
777 if(position.x > Math::MACHINE_EPSILON_10000 ||
778 position.y > Math::MACHINE_EPSILON_10000 ||
779 orientationAxis != Vector3(0.0f, 1.0f, 0.0f) ||
780 orientationAngle != ANGLE_180 ||
781 scale != Vector3(1.0f, 1.0f, 1.0f))
783 cleanDamagedRect = true;
789 cleanDamagedRect = true;
793 Rect<int32_t> viewportRect;
794 if(instruction.mIsViewportSet)
796 const int32_t y = (surfaceRect.height - instruction.mViewport.height) - instruction.mViewport.y;
797 viewportRect.Set(instruction.mViewport.x, y, instruction.mViewport.width, instruction.mViewport.height);
798 if(viewportRect.IsEmpty() || !viewportRect.IsValid())
800 cleanDamagedRect = true;
801 continue; // just skip funny use cases for now, empty viewport means it is set somewhere else
806 viewportRect = surfaceRect;
809 const Matrix* viewMatrix = instruction.GetViewMatrix(mImpl->renderBufferIndex);
810 const Matrix* projectionMatrix = instruction.GetProjectionMatrix(mImpl->renderBufferIndex);
811 if(viewMatrix && projectionMatrix)
813 const RenderListContainer::SizeType count = instruction.RenderListCount();
814 for(RenderListContainer::SizeType index = 0u; index < count; ++index)
816 const RenderList* renderList = instruction.GetRenderList(index);
819 if(!renderList->IsEmpty())
821 const std::size_t listCount = renderList->Count();
822 for(uint32_t listIndex = 0u; listIndex < listCount; ++listIndex)
824 RenderItem& item = renderList->GetItem(listIndex);
825 // If the item does 3D transformation, make full update
826 if(item.mUpdateArea == Vector4::ZERO)
828 cleanDamagedRect = true;
830 // Save the full rect in the damaged list. We need it when this item is removed
831 DirtyRectKey dirtyRectKey(item.mNode, item.mRenderer);
832 auto dirtyRectPos = itemsDirtyRects.find(dirtyRectKey);
833 if(dirtyRectPos != itemsDirtyRects.end())
836 dirtyRectPos->second.visited = true;
837 dirtyRectPos->second.rect = surfaceRect;
841 // Else, just insert the new dirtyrect
842 itemsDirtyRects.insert({dirtyRectKey, surfaceRect});
848 DirtyRectKey dirtyRectKey(item.mNode, item.mRenderer);
849 // If the item refers to updated node or renderer.
850 if(item.mIsUpdated ||
852 (item.mNode->Updated() || (item.mRenderer && item.mRenderer->Updated()))))
854 item.mIsUpdated = false;
856 rect = CalculateUpdateArea(item, mImpl->renderBufferIndex, viewportRect);
857 if(rect.IsValid() && rect.Intersect(viewportRect) && !rect.IsEmpty())
859 AlignDamagedRect(rect);
861 // Found valid dirty rect.
862 auto dirtyRectPos = itemsDirtyRects.find(dirtyRectKey);
863 if(dirtyRectPos != itemsDirtyRects.end())
865 Rect<int> currentRect = rect;
867 // Same item, merge it with the previous rect
868 rect.Merge(dirtyRectPos->second.rect);
870 // Replace the rect as current
871 dirtyRectPos->second.visited = true;
872 dirtyRectPos->second.rect = currentRect;
876 // Else, just insert the new dirtyrect
877 itemsDirtyRects.insert({dirtyRectKey, rect});
880 damagedRects.push_back(rect);
885 // 1. The item is not dirty, the node and renderer referenced by the item are still exist.
886 // 2. Mark the related dirty rects as visited so they will not be removed below.
887 auto dirtyRectPos = itemsDirtyRects.find(dirtyRectKey);
888 if(dirtyRectPos != itemsDirtyRects.end())
890 dirtyRectPos->second.visited = true;
894 // The item is not in the list for some reason. Add the current rect!
895 rect = CalculateUpdateArea(item, mImpl->renderBufferIndex, viewportRect);
896 if(rect.IsValid() && rect.Intersect(viewportRect) && !rect.IsEmpty())
898 AlignDamagedRect(rect);
900 itemsDirtyRects.insert({dirtyRectKey, rect});
902 cleanDamagedRect = true; // And make full update at this frame
912 // Check removed nodes or removed renderers dirty rects
913 #if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
914 for(auto iter = itemsDirtyRects.begin(); iter != itemsDirtyRects.end();)
916 // Note, std::unordered_map end iterator is validate if we call erase.
917 for(auto iter = itemsDirtyRects.cbegin(), iterEnd = itemsDirtyRects.cend(); iter != iterEnd;)
920 if(!iter->second.visited)
922 damagedRects.push_back(iter->second.rect);
923 iter = itemsDirtyRects.erase(iter);
931 if(sceneObject->IsNeededFullUpdate())
933 cleanDamagedRect = true; // And make full update at this frame
936 if(!cleanDamagedRect)
938 damagedRectCleaner.SetCleanOnReturn(false);
942 void RenderManager::RenderScene(Integration::RenderStatus& status, Integration::Scene& scene, bool renderToFbo)
944 SceneGraph::Scene* sceneObject = GetImplementation(scene).GetSceneObject();
947 DALI_LOG_ERROR("Scene was empty handle. Skip rendering\n");
951 Rect<int> clippingRect = sceneObject->GetSurfaceRect();
952 RenderScene(status, scene, renderToFbo, clippingRect);
955 void RenderManager::RenderScene(Integration::RenderStatus& status, Integration::Scene& scene, bool renderToFbo, Rect<int>& clippingRect)
957 if(mImpl->partialUpdateAvailable == Integration::PartialUpdateAvailable::TRUE && !renderToFbo && clippingRect.IsEmpty())
959 DALI_LOG_DEBUG_INFO("ClippingRect was empty. Skip rendering\n");
963 Internal::Scene& sceneInternal = GetImplementation(scene);
964 SceneGraph::Scene* sceneObject = sceneInternal.GetSceneObject();
967 DALI_LOG_ERROR("Scene was empty handle. Skip rendering\n");
971 uint32_t instructionCount = sceneObject->GetRenderInstructions().Count(mImpl->renderBufferIndex);
973 std::vector<Graphics::RenderTarget*> targetsToPresent;
975 Rect<int32_t> surfaceRect = sceneObject->GetSurfaceRect();
976 if(clippingRect == surfaceRect)
978 // Full rendering case
979 // Make clippingRect empty because we're doing full rendering now if the clippingRect is empty.
980 // To reduce side effects, keep this logic now.
981 clippingRect = Rect<int>();
984 // Prefetch programs before we start rendering so reflection is
985 // ready, and we can pull exact size of UBO needed (no need to resize during drawing)
986 auto totalSizeCPU = 0u;
987 auto totalSizeGPU = 0u;
989 std::unordered_map<Graphics::Program*, Graphics::ProgramResourceBindingInfo> programUsageCount;
991 for(uint32_t i = 0; i < instructionCount; ++i)
993 RenderInstruction& instruction = sceneObject->GetRenderInstructions().At(mImpl->renderBufferIndex, i);
995 if((instruction.mFrameBuffer != nullptr && renderToFbo) ||
996 (instruction.mFrameBuffer == nullptr && !renderToFbo))
998 for(auto j = 0u; j < instruction.RenderListCount(); ++j)
1000 const auto& renderList = instruction.GetRenderList(j);
1001 for(auto k = 0u; k < renderList->Count(); ++k)
1003 auto& item = renderList->GetItem(k);
1004 if(item.mRenderer && item.mRenderer->NeedsProgram())
1006 // Prepare and store used programs for further processing
1007 auto program = item.mRenderer->PrepareProgram(instruction);
1010 const auto& memoryRequirements = program->GetUniformBlocksMemoryRequirements();
1012 // collect how many programs we use in this frame
1013 auto key = &program->GetGraphicsProgram();
1014 auto it = programUsageCount.find(key);
1015 if(it == programUsageCount.end())
1017 programUsageCount[key] = Graphics::ProgramResourceBindingInfo{.program = key, .count = 1};
1021 (*it).second.count++;
1024 totalSizeCPU += memoryRequirements.totalCpuSizeRequired;
1025 totalSizeGPU += memoryRequirements.totalGpuSizeRequired;
1033 // Fill resource binding for the command buffer
1034 std::vector<Graphics::CommandBufferResourceBinding> commandBufferResourceBindings;
1035 if(!programUsageCount.empty())
1037 commandBufferResourceBindings.resize(programUsageCount.size());
1038 auto iter = commandBufferResourceBindings.begin();
1039 for(auto& item : programUsageCount)
1041 iter->type = Graphics::ResourceType::PROGRAM;
1042 iter->programBinding = &item.second;
1047 // Reset main algorithms command buffer
1048 mImpl->renderAlgorithms.ResetCommandBuffer(commandBufferResourceBindings.empty() ? nullptr : &commandBufferResourceBindings);
1050 auto mainCommandBuffer = mImpl->renderAlgorithms.GetMainCommandBuffer();
1052 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Render scene (%s), CPU:%d GPU:%d\n", renderToFbo ? "Offscreen" : "Onscreen", totalSizeCPU, totalSizeGPU);
1054 auto& uboManager = mImpl->uniformBufferManager;
1056 uboManager->SetCurrentSceneRenderInfo(sceneObject, renderToFbo);
1057 uboManager->Rollback(sceneObject, renderToFbo);
1059 // Respec UBOs for this frame (orphan buffers or double buffer in the GPU)
1060 if(instructionCount)
1062 uboManager->GetUniformBufferForScene(sceneObject, renderToFbo, true)->ReSpecify(totalSizeCPU);
1063 uboManager->GetUniformBufferForScene(sceneObject, renderToFbo, false)->ReSpecify(totalSizeGPU);
1066 #if defined(DEBUG_ENABLED)
1067 auto uniformBuffer1 = uboManager->GetUniformBufferForScene(sceneObject, renderToFbo, true);
1068 auto uniformBuffer2 = uboManager->GetUniformBufferForScene(sceneObject, renderToFbo, false);
1071 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "CPU buffer: Offset(%d), Cap(%d)\n", uniformBuffer1->GetCurrentOffset(), uniformBuffer1->GetCurrentCapacity());
1075 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "CPU buffer: nil\n");
1079 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GPU buffer: Offset(%d), Cap(%d)\n", uniformBuffer2->GetCurrentOffset(), uniformBuffer2->GetCurrentCapacity());
1083 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GPU buffer: nil\n");
1087 for(uint32_t i = 0; i < instructionCount; ++i)
1089 RenderInstruction& instruction = sceneObject->GetRenderInstructions().At(mImpl->renderBufferIndex, i);
1091 if((renderToFbo && !instruction.mFrameBuffer) || (!renderToFbo && instruction.mFrameBuffer))
1096 // Mark that we will require a post-render step to be performed (includes swap-buffers).
1097 status.SetNeedsPostRender(true);
1099 Rect<int32_t> viewportRect;
1101 int32_t surfaceOrientation = sceneObject->GetSurfaceOrientation() + sceneObject->GetScreenOrientation();
1102 if(surfaceOrientation >= 360)
1104 surfaceOrientation -= 360;
1107 // @todo Should these be part of scene?
1108 Integration::DepthBufferAvailable depthBufferAvailable = mImpl->depthBufferAvailable;
1109 Integration::StencilBufferAvailable stencilBufferAvailable = mImpl->stencilBufferAvailable;
1111 Graphics::RenderTarget* currentRenderTarget = nullptr;
1112 Graphics::RenderPass* currentRenderPass = nullptr;
1113 std::vector<Graphics::ClearValue> currentClearValues{};
1115 if(instruction.mFrameBuffer)
1117 // Ensure graphics framebuffer is created, bind attachments and create render passes
1118 // Only happens once per framebuffer. If the create fails, e.g. no attachments yet,
1119 // then don't render to this framebuffer.
1120 if(!instruction.mFrameBuffer->GetGraphicsObject())
1122 const bool created = instruction.mFrameBuffer->CreateGraphicsObjects();
1129 auto& clearValues = instruction.mFrameBuffer->GetGraphicsRenderPassClearValues();
1131 // Set the clear color for first color attachment
1132 if(instruction.mIsClearColorSet && !clearValues.empty())
1134 clearValues[0].color = {
1135 instruction.mClearColor.r,
1136 instruction.mClearColor.g,
1137 instruction.mClearColor.b,
1138 instruction.mClearColor.a};
1141 currentClearValues = clearValues;
1143 auto loadOp = instruction.mIsClearColorSet ? Graphics::AttachmentLoadOp::CLEAR : Graphics::AttachmentLoadOp::LOAD;
1146 currentRenderTarget = instruction.mFrameBuffer->GetGraphicsRenderTarget();
1147 currentRenderPass = instruction.mFrameBuffer->GetGraphicsRenderPass(loadOp, Graphics::AttachmentStoreOp::STORE);
1149 else // no framebuffer
1152 auto& clearValues = sceneObject->GetGraphicsRenderPassClearValues();
1154 if(instruction.mIsClearColorSet)
1156 clearValues[0].color = {
1157 instruction.mClearColor.r,
1158 instruction.mClearColor.g,
1159 instruction.mClearColor.b,
1160 instruction.mClearColor.a};
1163 currentClearValues = clearValues;
1165 // @todo SceneObject should already have the depth clear / stencil clear in the clearValues array.
1166 // if the window has a depth/stencil buffer.
1167 if((depthBufferAvailable == Integration::DepthBufferAvailable::TRUE ||
1168 stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE) &&
1169 (currentClearValues.size() <= 1))
1171 currentClearValues.emplace_back();
1172 currentClearValues.back().depthStencil.depth = 0;
1173 currentClearValues.back().depthStencil.stencil = 0;
1176 auto loadOp = instruction.mIsClearColorSet ? Graphics::AttachmentLoadOp::CLEAR : Graphics::AttachmentLoadOp::LOAD;
1178 currentRenderTarget = sceneObject->GetSurfaceRenderTarget();
1179 currentRenderPass = sceneObject->GetGraphicsRenderPass(loadOp, Graphics::AttachmentStoreOp::STORE);
1182 targetsToPresent.emplace_back(currentRenderTarget);
1184 if(!instruction.mIgnoreRenderToFbo && (instruction.mFrameBuffer != nullptr))
1186 // Offscreen buffer rendering
1187 if(instruction.mIsViewportSet)
1189 // For Viewport the lower-left corner is (0,0)
1190 const int32_t y = (instruction.mFrameBuffer->GetHeight() - instruction.mViewport.height) - instruction.mViewport.y;
1191 viewportRect.Set(instruction.mViewport.x, y, instruction.mViewport.width, instruction.mViewport.height);
1195 viewportRect.Set(0, 0, instruction.mFrameBuffer->GetWidth(), instruction.mFrameBuffer->GetHeight());
1197 surfaceOrientation = 0;
1199 else // No Offscreen frame buffer rendering
1201 // Check whether a viewport is specified, otherwise the full surface size is used
1202 if(instruction.mIsViewportSet)
1204 // For Viewport the lower-left corner is (0,0)
1205 const int32_t y = (surfaceRect.height - instruction.mViewport.height) - instruction.mViewport.y;
1206 viewportRect.Set(instruction.mViewport.x, y, instruction.mViewport.width, instruction.mViewport.height);
1210 viewportRect = surfaceRect;
1214 // Set surface orientation
1215 // @todo Inform graphics impl by another route.
1216 // was: mImpl->currentContext->SetSurfaceOrientation(surfaceOrientation);
1218 /*** Clear region of framebuffer or surface before drawing ***/
1219 bool clearFullFrameRect = (surfaceRect == viewportRect);
1220 if(instruction.mFrameBuffer != nullptr)
1222 Viewport frameRect(0, 0, instruction.mFrameBuffer->GetWidth(), instruction.mFrameBuffer->GetHeight());
1223 clearFullFrameRect = (frameRect == viewportRect);
1226 if(!clippingRect.IsEmpty())
1228 if(!clippingRect.Intersect(viewportRect))
1230 DALI_LOG_ERROR("Invalid clipping rect %d %d %d %d\n", clippingRect.x, clippingRect.y, clippingRect.width, clippingRect.height);
1231 clippingRect = Rect<int>();
1233 clearFullFrameRect = false;
1236 Graphics::Rect2D scissorArea{viewportRect.x, viewportRect.y, uint32_t(viewportRect.width), uint32_t(viewportRect.height)};
1237 if(instruction.mIsClearColorSet)
1239 if(!clearFullFrameRect)
1241 if(!clippingRect.IsEmpty())
1243 scissorArea = {clippingRect.x, clippingRect.y, uint32_t(clippingRect.width), uint32_t(clippingRect.height)};
1248 // Scissor's value should be set based on the default system coordinates.
1249 // When the surface is rotated, the input values already were set with the rotated angle.
1250 // So, re-calculation is needed.
1251 scissorArea = RecalculateScissorArea(scissorArea, surfaceOrientation, surfaceRect);
1253 // Begin render pass
1254 mainCommandBuffer->BeginRenderPass(
1256 currentRenderTarget,
1258 currentClearValues);
1260 // Note, don't set the viewport/scissor on the primary command buffer.
1262 mImpl->renderAlgorithms.ProcessRenderInstruction(
1264 mImpl->renderBufferIndex,
1265 depthBufferAvailable,
1266 stencilBufferAvailable,
1270 Uint16Pair(surfaceRect.width, surfaceRect.height),
1272 currentRenderTarget);
1274 Graphics::SyncObject* syncObject{nullptr};
1275 // If the render instruction has an associated render tracker (owned separately)
1276 // and framebuffer, create a one shot sync object, and use it to determine when
1277 // the render pass has finished executing on GPU.
1278 if(instruction.mRenderTracker && instruction.mFrameBuffer)
1280 syncObject = instruction.mRenderTracker->CreateSyncObject(mImpl->graphicsController);
1281 instruction.mRenderTracker = nullptr;
1283 mainCommandBuffer->EndRenderPass(syncObject);
1286 if(targetsToPresent.size() > 0u)
1288 DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_RENDER_FINISHED", [&](std::ostringstream& oss)
1289 { oss << "[" << targetsToPresent.size() << "]"; });
1293 mImpl->uniformBufferManager->Flush(sceneObject, renderToFbo);
1294 mImpl->renderAlgorithms.SubmitCommandBuffer();
1295 mImpl->commandBufferSubmitted = true;
1297 if(targetsToPresent.size() > 0u)
1299 std::sort(targetsToPresent.begin(), targetsToPresent.end());
1301 Graphics::RenderTarget* rt = nullptr;
1302 for(auto& target : targetsToPresent)
1306 mImpl->graphicsController.PresentRenderTarget(target);
1311 DALI_TRACE_END(gTraceFilter, "DALI_RENDER_FINISHED");
1315 void RenderManager::PostRender()
1317 if(!mImpl->commandBufferSubmitted)
1319 // Rendering is skipped but there may be pending tasks. Flush them.
1320 Graphics::SubmitInfo submitInfo;
1321 submitInfo.cmdBuffer.clear(); // Only flush
1322 submitInfo.flags = 0 | Graphics::SubmitFlagBits::FLUSH;
1323 mImpl->graphicsController.SubmitCommandBuffers(submitInfo);
1325 mImpl->commandBufferSubmitted = true;
1328 // Notify RenderGeometries that rendering has finished
1329 for(auto&& iter : mImpl->geometryContainer)
1331 iter->OnRenderFinished();
1334 // Notify updated RenderTexture that rendering has finished
1335 for(auto&& iter : mImpl->updatedTextures)
1337 iter->OnRenderFinished();
1339 mImpl->updatedTextures.Clear();
1341 // Remove discarded textures after OnRenderFinished called
1342 mImpl->textureDiscardQueue.Clear();
1344 mImpl->UpdateTrackers();
1346 uint32_t count = 0u;
1347 for(auto& scene : mImpl->sceneContainer)
1349 count += scene->GetRenderInstructions().Count(mImpl->renderBufferIndex);
1352 mImpl->ClearUnusedProgramCacheIfNeed();
1354 #if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
1355 // Shrink relevant containers if required.
1356 if(mImpl->containerRemovedFlags & ContainerRemovedFlagBits::RENDERER)
1358 mImpl->rendererContainer.ShrinkToFitIfNeeded();
1360 if(mImpl->containerRemovedFlags & ContainerRemovedFlagBits::TEXTURE)
1362 mImpl->textureContainer.ShrinkToFitIfNeeded();
1365 // Both containers always have empty slots, so we can shrink them.
1366 // Note that we don't need to release it every frames. So just check every specific frames
1367 if((mImpl->frameCount & (SHRINK_TO_FIT_FRAME_COUNT - 1)) == 0)
1369 mImpl->updatedTextures.ShrinkToFit();
1370 mImpl->textureDiscardQueue.ShrinkToFit();
1374 mImpl->containerRemovedFlags = ContainerRemovedFlagBits::NOTHING;
1377 const bool haveInstructions = count > 0u;
1379 // If this frame was rendered due to instructions existing, we mark this so we know to clear the next frame.
1380 mImpl->lastFrameWasRendered = haveInstructions;
1383 * The rendering has finished; swap to the next buffer.
1384 * Ideally the update has just finished using this buffer; otherwise the render thread
1385 * should block until the update has finished.
1387 mImpl->renderBufferIndex = (0 != mImpl->renderBufferIndex) ? 0 : 1;
1389 DALI_PRINT_RENDER_END();
1392 } // namespace SceneGraph
1394 } // namespace Internal