// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #pragma once #include #include #include #include #include #include #include namespace anki { /// @addtogroup scene /// @{ static const U32 MAX_SPATIALS_PER_VIS_TEST = 48; ///< Num of spatials to test in a single ThreadHive task. static const U32 SW_RASTERIZER_WIDTH = 80; static const U32 SW_RASTERIZER_HEIGHT = 50; /// Sort objects on distance template class DistanceSortFunctor { public: Bool operator()(const T& a, const T& b) { return a.m_distanceFromCamera < b.m_distanceFromCamera; } }; template class RevDistanceSortFunctor { public: Bool operator()(const T& a, const T& b) { return a.m_distanceFromCamera > b.m_distanceFromCamera; } }; /// Sorts first by LOD and then by material (merge key). class MaterialDistanceSortFunctor { public: Bool operator()(const RenderableQueueElement& a, const RenderableQueueElement& b) { if(a.m_lod == b.m_lod) { return a.m_mergeKey < b.m_mergeKey; } else { return a.m_lod < b.m_lod; } } }; /// Storage for a single element type. template class TRenderQueueElementStorage { public: T* m_elements = nullptr; U32 m_elementCount = 0; U32 m_elementStorage = 0; T* newElement(SceneFrameAllocator alloc) { if(ANKI_UNLIKELY(m_elementCount + 1 > m_elementStorage)) { m_elementStorage = max(INITIAL_STORAGE_SIZE, m_elementStorage * STORAGE_GROW_RATE); const T* oldElements = m_elements; m_elements = alloc.allocate(m_elementStorage); if(oldElements) { memcpy(m_elements, oldElements, sizeof(T) * m_elementCount); } } return &m_elements[m_elementCount++]; } }; class RenderQueueView { public: TRenderQueueElementStorage m_renderables; ///< Deferred shading or shadow renderables. TRenderQueueElementStorage m_forwardShadingRenderables; TRenderQueueElementStorage m_earlyZRenderables; TRenderQueueElementStorage m_pointLights; TRenderQueueElementStorage m_spotLights; DirectionalLightQueueElement m_directionalLight; TRenderQueueElementStorage m_reflectionProbes; TRenderQueueElementStorage m_lensFlares; TRenderQueueElementStorage m_decals; TRenderQueueElementStorage m_fogDensityVolumes; TRenderQueueElementStorage m_giProbes; TRenderQueueElementStorage m_genericGpuComputeJobs; TRenderQueueElementStorage m_rayTracingInstances; TRenderQueueElementStorage m_uis; Timestamp m_timestamp = 0; RenderQueueView() { zeroMemory(m_directionalLight); } }; static_assert(std::is_trivially_destructible::value == true, "Should be trivially destructible"); /// Data common for all tasks. class VisibilityContext { public: SceneGraph* m_scene = nullptr; Atomic m_testsCount = {0}; F32 m_earlyZDist = -1.0f; ///< Cache this. List m_testedFrcs; Mutex m_mtx; void submitNewWork(const FrustumComponent& frc, const FrustumComponent& primaryFrustum, RenderQueue& result, ThreadHive& hive); }; /// A context for a specific test of a frustum component. /// @note Should be trivially destructible. class FrustumVisibilityContext { public: VisibilityContext* m_visCtx = nullptr; const FrustumComponent* m_frc = nullptr; ///< This is the frustum to be tested. const FrustumComponent* m_primaryFrustum = nullptr; ///< This is the primary camera frustum. // S/W rasterizer members SoftwareRasterizer* m_r = nullptr; DynamicArray m_verts; Atomic m_rasterizedVertCount = {0}; ///< That will be used by the RasterizeTrianglesTask. // Visibility test members DynamicArray m_queueViews; ///< Sub result. Will be combined later. ThreadHiveSemaphore* m_visTestsSignalSem = nullptr; // Gather results members RenderQueue* m_renderQueue = nullptr; }; /// ThreadHive task to set the depth map of the S/W rasterizer. class FillRasterizerWithCoverageTask { public: FrustumVisibilityContext* m_frcCtx = nullptr; FillRasterizerWithCoverageTask(FrustumVisibilityContext* frcCtx) : m_frcCtx(frcCtx) { ANKI_ASSERT(m_frcCtx); } void fill(); }; static_assert(std::is_trivially_destructible::value == true, "Should be trivially destructible"); /// ThreadHive task to get visible nodes from the octree. class GatherVisiblesFromOctreeTask { public: FrustumVisibilityContext* m_frcCtx = nullptr; GatherVisiblesFromOctreeTask(FrustumVisibilityContext* frcCtx) : m_frcCtx(frcCtx) { ANKI_ASSERT(m_frcCtx); } void gather(ThreadHive& hive); private: Array m_spatials; U32 m_spatialCount = 0; /// Submit tasks to test the m_spatials. void flush(ThreadHive& hive); }; static_assert(std::is_trivially_destructible::value == true, "Should be trivially destructible"); /// ThreadHive task that does the actual visibility tests. class VisibilityTestTask { public: FrustumVisibilityContext* m_frcCtx = nullptr; Array m_spatialsToTest; U32 m_spatialToTestCount = 0; VisibilityTestTask(FrustumVisibilityContext* frcCtx) : m_frcCtx(frcCtx) { ANKI_ASSERT(m_frcCtx); } void test(ThreadHive& hive, U32 taskId); private: ANKI_USE_RESULT Bool testAgainstRasterizer(const Aabb& aabb) const { return (m_frcCtx->m_r) ? m_frcCtx->m_r->visibilityTest(aabb) : true; } }; static_assert(std::is_trivially_destructible::value == true, "Should be trivially destructible"); /// Task that combines and sorts the results. class CombineResultsTask { public: FrustumVisibilityContext* m_frcCtx = nullptr; CombineResultsTask(FrustumVisibilityContext* frcCtx) : m_frcCtx(frcCtx) { ANKI_ASSERT(m_frcCtx); } void combine(); private: template static void combineQueueElements(SceneFrameAllocator& alloc, WeakArray> subStorages, WeakArray>* ptrSubStorage, WeakArray& combined, WeakArray* ptrCombined); }; static_assert(std::is_trivially_destructible::value == true, "Should be trivially destructible"); /// @} } // end namespace anki