diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 61c1799ba9..1234b2b9f7 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -312,10 +312,10 @@ void OpenGLDriver::setRasterStateSlow(RasterState rs) noexcept { // -- less than or equal to 208 bytes -OpenGLDriver::HandleAllocator::HandleAllocator(const utils::HeapArea& area) +OpenGLDriver::HandleAllocator::HandleAllocator(const utils::AreaPolicy::HeapArea& area) { // TODO: we probably need a better way to set the size of these pools - const size_t unit = area.getSize() / 32; + const size_t unit = area.size() / 32; const size_t offsetPool1 = unit; const size_t offsetPool2 = 16 * unit; char* const p = (char*)area.begin(); diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h index f4b82ead8b..96e764a1b2 100644 --- a/filament/backend/src/opengl/OpenGLDriver.h +++ b/filament/backend/src/opengl/OpenGLDriver.h @@ -253,7 +253,7 @@ private: utils::PoolAllocator<208, 32> mPool2; public: static constexpr size_t MIN_ALIGNMENT_SHIFT = 4; - explicit HandleAllocator(const utils::HeapArea& area); + explicit HandleAllocator(const utils::AreaPolicy::HeapArea& area); // this is in fact always called with a constexpr size argument inline void* alloc(size_t size, size_t alignment, size_t extra) noexcept { diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 86872cce7e..4763ed8377 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -299,7 +299,8 @@ void PostProcessManager::commitAndRender(FrameGraphResources::RenderPassInfo con // ------------------------------------------------------------------------------------------------ FrameGraphId PostProcessManager::structure(FrameGraph& fg, - const RenderPass& pass, uint32_t width, uint32_t height, float scale) noexcept { + RenderPass const& pass, uint32_t width, uint32_t height, float scale) noexcept { + // structure pass -- automatically culled if not used, currently used by: // - ssao @@ -332,7 +333,8 @@ FrameGraphId PostProcessManager::structure(FrameGraph& fg, .clearFlags = TargetBufferFlags::DEPTH }); }, - [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { + [=](FrameGraphResources const& resources, + auto const& data, DriverApi& driver) mutable { auto out = resources.getRenderPassInfo(); pass.execute(resources.getPassName(), out.target, out.params); }); @@ -380,8 +382,7 @@ FrameGraphId PostProcessManager::structure(FrameGraph& fg, return depth; } -FrameGraphId PostProcessManager::screenSpaceAmbientOcclusion( - FrameGraph& fg, RenderPass& pass, +FrameGraphId PostProcessManager::screenSpaceAmbientOcclusion(FrameGraph& fg, filament::Viewport const& svp, const CameraInfo& cameraInfo, View::AmbientOcclusionOptions options) noexcept { diff --git a/filament/src/PostProcessManager.h b/filament/src/PostProcessManager.h index aec322df5c..8b3f888b1b 100644 --- a/filament/src/PostProcessManager.h +++ b/filament/src/PostProcessManager.h @@ -24,11 +24,11 @@ #include #include +#include + #include #include -#include - #include #include @@ -69,8 +69,7 @@ public: // SSAO FrameGraphId screenSpaceAmbientOcclusion(FrameGraph& fg, - RenderPass& pass, filament::Viewport const& svp, - CameraInfo const& cameraInfo, + filament::Viewport const& svp, const CameraInfo& cameraInfo, View::AmbientOcclusionOptions options) noexcept; // Used in refraction pass diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index ee0309407b..da6b9454df 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -40,16 +40,32 @@ namespace filament { using namespace backend; RenderPass::RenderPass(FEngine& engine, - GrowingSlice commands) noexcept - : mEngine(engine), mCommands(commands), + RenderPass::Arena& arena) noexcept + : mEngine(engine), mCommandArena(arena), mCustomCommands(engine.getPerRenderPassAllocator()) { - mCustomCommands.reserve(8); // preallocate allocate a reasonable number of custom commands } RenderPass::RenderPass(RenderPass const& rhs) = default; RenderPass::~RenderPass() noexcept = default; +RenderPass::Command* RenderPass::append(size_t count) noexcept { + Command* const curr = mCommandArena.alloc(count); + assert_invariant(mCommandBegin == nullptr || curr == mCommandEnd); + if (mCommandBegin == nullptr) { + mCommandBegin = mCommandEnd = curr; + } + mCommandEnd += count; + return curr; +} + +void RenderPass::resize(size_t count) noexcept { + if (mCommandBegin) { + mCommandEnd = mCommandBegin + count; + mCommandArena.rewind(mCommandEnd); + } +} + void RenderPass::setGeometry(FScene::RenderableSoa const& soa, Range vr, backend::Handle uboHandle) noexcept { mRenderableSoa = &soa; @@ -57,55 +73,42 @@ void RenderPass::setGeometry(FScene::RenderableSoa const& soa, Range v mUboHandle = uboHandle; } -void RenderPass::setCamera(const CameraInfo& camera) noexcept { - mCamera = camera; -} - -void RenderPass::setRenderFlags(RenderPass::RenderFlags flags) noexcept { - mFlags = flags; -} - void RenderPass::overridePolygonOffset(backend::PolygonOffset* polygonOffset) noexcept { if ((mPolygonOffsetOverride = (polygonOffset != nullptr))) { mPolygonOffset = *polygonOffset; } } -RenderPass::Command* RenderPass::newCommandBuffer() noexcept { - GrowingSlice& commands = mCommands; - commands = GrowingSlice(commands.end(), commands.capacity() - commands.size()); - return commands.begin(); -} - -RenderPass::Command* RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcept { +void RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcept { SYSTRACE_CONTEXT(); + assert_invariant(mRenderableSoa); + + utils::Range vr = mVisibleRenderables; + // trace the number of visible renderables + SYSTRACE_VALUE32("visibleRenderables", vr.size()); + if (UTILS_UNLIKELY(vr.empty())) { + return; + } + FEngine& engine = mEngine; JobSystem& js = engine.getJobSystem(); - GrowingSlice& commands = mCommands; const RenderFlags renderFlags = mFlags; const FScene::VisibleMaskType visibilityMask = mVisibilityMask; CameraInfo const& camera = mCamera; - utils::Range vr = mVisibleRenderables; - if (UTILS_UNLIKELY(vr.empty())) { - return commands.end(); - } - assert_invariant(mRenderableSoa); - - // trace the number of visible renderables - SYSTRACE_VALUE32("visibleRenderables", vr.size()); // up-to-date summed primitive counts needed for generateCommands() FScene::RenderableSoa const& soa = *mRenderableSoa; updateSummedPrimitiveCounts(const_cast(soa), vr); // compute how much maximum storage we need for this pass - uint32_t growBy = FScene::getPrimitiveCount(soa, vr.last); + uint32_t commandCount = FScene::getPrimitiveCount(soa, vr.last); // double the color pass for transparent objects that need to render twice const bool colorPass = bool(commandTypeFlags & CommandTypeFlags::COLOR); const bool depthPass = bool(commandTypeFlags & CommandTypeFlags::DEPTH); - growBy *= uint32_t(colorPass * 2 + depthPass); - Command* const curr = commands.grow(growBy); + commandCount *= uint32_t(colorPass * 2 + depthPass); + commandCount += 1; // for the sentinel + Command* const curr = append(commandCount); // we extract camera position/forward outside of the loop, because these are not cheap. const float3 cameraPosition(camera.getPosition()); @@ -129,14 +132,10 @@ RenderPass::Command* RenderPass::appendCommands(CommandTypeFlags const commandTy // always add an "eof" command // "eof" command. these commands are guaranteed to be sorted last in the // command buffer. - commands.grow(1)->key = uint64_t(Pass::SENTINEL); - - mCommandsHighWatermark = std::max(mCommandsHighWatermark, size_t(commands.size())); - - return commands.end(); + curr[commandCount - 1].key = uint64_t(Pass::SENTINEL); } -RenderPass::Command* RenderPass::appendCustomCommand(Pass pass, CustomCommand custom, uint32_t order, +void RenderPass::appendCustomCommand(Pass pass, CustomCommand custom, uint32_t order, std::function command) { assert((uint64_t(order) << CUSTOM_ORDER_SHIFT) <= CUSTOM_ORDER_MASK); @@ -149,103 +148,22 @@ RenderPass::Command* RenderPass::appendCustomCommand(Pass pass, CustomCommand cu cmd |= uint64_t(order) << CUSTOM_ORDER_SHIFT; cmd |= uint64_t(index); - Command* const curr = mCommands.grow(1); + Command* const curr = append(1); curr->key = cmd; - return curr + 1; } -RenderPass::Command* RenderPass::sortCommands() noexcept { +void RenderPass::sortCommands() noexcept { SYSTRACE_NAME("sort and trim commands"); - GrowingSlice& commands = mCommands; - - std::sort(commands.begin(), commands.end()); + std::sort(mCommandBegin, mCommandEnd); // find the last command - Command const* const last = std::partition_point(commands.begin(), commands.end(), + Command const* const last = std::partition_point(mCommandBegin, mCommandEnd, [](Command const& c) { return c.key != uint64_t(Pass::SENTINEL); }); - commands.resize(uint32_t(last - commands.begin())); - - return commands.end(); -} - -void RenderPass::execute(const char* name, - backend::Handle renderTarget, - backend::RenderPassParams params) const noexcept { - FEngine& engine = mEngine; - DriverApi& driver = engine.getDriverApi(); - driver.beginRenderPass(renderTarget, params); - executeCommands(name); - driver.endRenderPass(); -} - -void RenderPass::executeCommands(const char* name) const noexcept { - // this is a good time to flush the CommandStream, because we're about to potentially - // output a lot of commands. This guarantees here that we have at least - // FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB bytes (1MiB by default). - FEngine& engine = mEngine; - engine.flush(); - DriverApi& driver = engine.getDriverApi(); - RenderPass::recordDriverCommands(driver, mCommands.begin(), mCommands.end()); -} - -UTILS_NOINLINE // no need to be inlined -void RenderPass::recordDriverCommands(FEngine::DriverApi& driver, const Command* first, - const Command* last) const noexcept { - SYSTRACE_CALL(); - - if (first != last) { - SYSTRACE_VALUE32("commandCount", last - first); - - PolygonOffset dummyPolyOffset; - PipelineState pipeline{ .polygonOffset = mPolygonOffset }; - PolygonOffset* const pPipelinePolygonOffset = - mPolygonOffsetOverride ? &dummyPolyOffset : &pipeline.polygonOffset; - - Handle uboHandle = mUboHandle; - FMaterialInstance const* UTILS_RESTRICT mi = nullptr; - FMaterial const* UTILS_RESTRICT ma = nullptr; - auto const& customCommands = mCustomCommands; - - first--; - while (++first != last) { - /* - * Be careful when changing code below, this is the hot inner-loop - */ - - if (UTILS_UNLIKELY((first->key & CUSTOM_MASK) != uint64_t(CustomCommand::PASS))) { - uint32_t index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT; - customCommands[index](); - continue; - } - - // per-renderable uniform - const PrimitiveInfo info = first->primitive; - pipeline.rasterState = info.rasterState; - if (UTILS_UNLIKELY(mi != info.mi)) { - // this is always taken the first time - mi = info.mi; - ma = mi->getMaterial(); - pipeline.scissor = mi->getScissor(); - *pPipelinePolygonOffset = mi->getPolygonOffset(); - mi->use(driver); - } - - pipeline.program = ma->getProgram(info.materialVariant.key); - size_t offset = info.index * sizeof(PerRenderableUib); - driver.bindUniformBufferRange(BindingPoints::PER_RENDERABLE, - uboHandle, offset, sizeof(PerRenderableUib)); - if (UTILS_UNLIKELY(info.perRenderableBones)) { - driver.bindUniformBuffer(BindingPoints::PER_RENDERABLE_BONES, - info.perRenderableBones); - } - driver.draw(pipeline, info.primitiveHandle); - } - mCustomCommands.clear(); - } + resize(uint32_t(last - mCommandBegin)); } /* static */ @@ -582,4 +500,82 @@ void RenderPass::updateSummedPrimitiveCounts( summedPrimitiveCount[vr.last] = count; } +// ------------------------------------------------------------------------------------------------ + +void RenderPass::Executor::execute(const char* name, + backend::Handle renderTarget, + backend::RenderPassParams params) const noexcept { + FEngine& engine = mEngine; + DriverApi& driver = engine.getDriverApi(); + driver.beginRenderPass(renderTarget, params); + executeCommands(name); + driver.endRenderPass(); +} + +void RenderPass::Executor::executeCommands(const char* name) const noexcept { + // this is a good time to flush the CommandStream, because we're about to potentially + // output a lot of commands. This guarantees here that we have at least + // FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB bytes (1MiB by default). + FEngine& engine = mEngine; + engine.flush(); + DriverApi& driver = engine.getDriverApi(); + recordDriverCommands(driver, mBegin, mEnd); +} + +UTILS_NOINLINE // no need to be inlined +void RenderPass::Executor::recordDriverCommands(backend::DriverApi& driver, + const Command* first, const Command* last) const noexcept { + SYSTRACE_CALL(); + + if (first != last) { + SYSTRACE_VALUE32("commandCount", last - first); + + PolygonOffset dummyPolyOffset; + PipelineState pipeline{ .polygonOffset = mPolygonOffset }; + PolygonOffset* const pPipelinePolygonOffset = + mPolygonOffsetOverride ? &dummyPolyOffset : &pipeline.polygonOffset; + + Handle uboHandle = mUboHandle; + FMaterialInstance const* UTILS_RESTRICT mi = nullptr; + FMaterial const* UTILS_RESTRICT ma = nullptr; + auto const& customCommands = mCustomCommands; + + first--; + while (++first != last) { + /* + * Be careful when changing code below, this is the hot inner-loop + */ + + if (UTILS_UNLIKELY((first->key & CUSTOM_MASK) != uint64_t(CustomCommand::PASS))) { + uint32_t index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT; + customCommands[index](); + continue; + } + + // per-renderable uniform + const PrimitiveInfo info = first->primitive; + pipeline.rasterState = info.rasterState; + if (UTILS_UNLIKELY(mi != info.mi)) { + // this is always taken the first time + mi = info.mi; + ma = mi->getMaterial(); + pipeline.scissor = mi->getScissor(); + *pPipelinePolygonOffset = mi->getPolygonOffset(); + mi->use(driver); + } + + pipeline.program = ma->getProgram(info.materialVariant.key); + size_t offset = info.index * sizeof(PerRenderableUib); + driver.bindUniformBufferRange(BindingPoints::PER_RENDERABLE, + uboHandle, offset, sizeof(PerRenderableUib)); + if (UTILS_UNLIKELY(info.perRenderableBones)) { + driver.bindUniformBuffer(BindingPoints::PER_RENDERABLE_BONES, + info.perRenderableBones); + } + driver.draw(pipeline, info.primitiveHandle); + } + } +} + + } // namespace filament diff --git a/filament/src/RenderPass.h b/filament/src/RenderPass.h index 4d8589cb1d..a1323c68b7 100644 --- a/filament/src/RenderPass.h +++ b/filament/src/RenderPass.h @@ -17,30 +17,91 @@ #ifndef TNT_UTILS_RENDERPASS_H #define TNT_UTILS_RENDERPASS_H -#include - +#include "details/Allocators.h" #include "details/Camera.h" -#include "details/Material.h" #include "details/Scene.h" #include "private/backend/DriverApiForward.h" #include +#include +#include + +#include #include -#include #include +#include +#include +#include #include - -namespace utils { -class JobSystem; -} +#include namespace filament { +class FMaterialInstance; + class RenderPass { public: + /* + * Command key encoding + * -------------------- + * + * a = alpha masking + * ppp = priority + * t = two-pass transparency ordering + * 0 = reserved, must be zero + * + * DEPTH command + * | 6 | 2| 2|1| 3 | 2| 16 | 32 | + * +------+--+--+-+---+--+----------------+--------------------------------+ + * |000000|01|00|0|ppp|00|0000000000000000| distanceBits | + * +------+--+--+-+---+-------------------+--------------------------------+ + * | correctness | optimizations (truncation allowed) | + * + * + * COLOR command + * | 6 | 2| 2|1| 3 | 2| 6 | 10 | 32 | + * +------+--+--+-+---+--+------+----------+--------------------------------+ + * |000001|01|00|a|ppp|00|000000| Z-bucket | material-id | + * |000010|01|00|a|ppp|00|000000| Z-bucket | material-id | refraction + * +------+--+--+-+---+--+------+----------+--------------------------------+ + * | correctness | optimizations (truncation allowed) | + * + * + * BLENDED command + * | 6 | 2| 2|1| 3 | 2| 32 | 15 |1| + * +------+--+--+-+---+--+--------------------------------+---------------+-+ + * |000011|01|00|0|ppp|00| ~distanceBits | blendOrder |t| + * +------+--+--+-+---+--+--------------------------------+---------------+-+ + * | correctness | + * + * + * pre-CUSTOM command + * | 6 | 2| 2| 22 | 32 | + * +------+--+--+----------------------+--------------------------------+ + * | pass |00|00| order | custom command index | + * +------+--+--+----------------------+--------------------------------+ + * | correctness | + * + * + * post-CUSTOM command + * | 6 | 2| 2| 22 | 32 | + * +------+--+--+----------------------+--------------------------------+ + * | pass |11|00| order | custom command index | + * +------+--+--+----------------------+--------------------------------+ + * | correctness | + * + * + * SENTINEL command + * | 64 | + * +--------.--------.--------.--------.--------.--------.--------.--------+ + * |11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111| + * +-----------------------------------------------------------------------+ + */ + using CommandKey = uint64_t; + static constexpr uint64_t DISTANCE_BITS_MASK = 0xFFFFFFFFllu; static constexpr unsigned DISTANCE_BITS_SHIFT = 0; @@ -119,74 +180,16 @@ public: }; - - // Command key encoding - // -------------------- - // - // a = alpha masking - // ppp = priority - // t = two-pass transparency ordering - // 0 = reserved, must be zero - // - // DEPTH command - // | 6 | 2| 2|1| 3 | 2| 16 | 32 | - // +------+--+--+-+---+--+----------------+--------------------------------+ - // |000000|01|00|0|ppp|00|0000000000000000| distanceBits | - // +------+--+--+-+---+-------------------+--------------------------------+ - // | correctness | optimizations (truncation allowed) | - // - // - // COLOR command - // | 6 | 2| 2|1| 3 | 2| 6 | 10 | 32 | - // +------+--+--+-+---+--+------+----------+--------------------------------+ - // |000001|01|00|a|ppp|00|000000| Z-bucket | material-id | - // |000010|01|00|a|ppp|00|000000| Z-bucket | material-id | refraction - // +------+--+--+-+---+--+------+----------+--------------------------------+ - // | correctness | optimizations (truncation allowed) | - // - // - // BLENDED command - // | 6 | 2| 2|1| 3 | 2| 32 | 15 |1| - // +------+--+--+-+---+--+--------------------------------+---------------+-+ - // |000011|01|00|0|ppp|00| ~distanceBits | blendOrder |t| - // +------+--+--+-+---+--+--------------------------------+---------------+-+ - // | correctness | - // - // - // pre-CUSTOM command - // | 6 | 2| 2| 22 | 32 | - // +------+--+--+----------------------+--------------------------------+ - // | pass |00|00| order | custom command index | - // +------+--+--+----------------------+--------------------------------+ - // | correctness | - // - // - // post-CUSTOM command - // | 6 | 2| 2| 22 | 32 | - // +------+--+--+----------------------+--------------------------------+ - // | pass |11|00| order | custom command index | - // +------+--+--+----------------------+--------------------------------+ - // | correctness | - // - // - // SENTINEL command - // | 64 | - // +--------.--------.--------.--------.--------.--------.--------.--------+ - // |11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111| - // +-----------------------------------------------------------------------+ - // - using CommandKey = uint64_t; - - - // The sorting material key is 32 bits and encoded as: - // - // | 12 | 8 | 12 | - // +------------+--------+------------+ - // | material |variant | instance | - // +------------+--------+------------+ - // - // The variant is inserted while building the commands, because we don't know it before that - // + /* + * The sorting material key is 32 bits and encoded as: + * + * | 12 | 8 | 12 | + * +------------+--------+------------+ + * | material |variant | instance | + * +------------+--------+------------+ + * + * The variant is inserted while building the commands, because we don't know it before that + */ static CommandKey makeMaterialSortingKey(uint32_t materialId, uint32_t instanceId) noexcept { CommandKey key = ((materialId << MATERIAL_ID_SHIFT) & MATERIAL_ID_MASK) | ((instanceId << MATERIAL_INSTANCE_ID_SHIFT) & MATERIAL_INSTANCE_ID_MASK); @@ -199,15 +202,9 @@ public: return uint64_t(value) << shift; } - template - static CommandKey makeFieldTruncate(T value, uint64_t mask, unsigned shift) noexcept { - return (uint64_t(value) << shift) & mask; - } - - template static CommandKey select(T boolish) noexcept { - return boolish ? -1llu : 0llu; + return boolish ? std::numeric_limits::max() : uint64_t(0); } struct PrimitiveInfo { // 24 bytes @@ -219,7 +216,6 @@ public: Variant materialVariant; // 1 byte uint8_t reserved = {}; // 1 byte }; - static_assert(sizeof(PrimitiveInfo) == sizeof(void*) + 16); struct alignas(8) Command { // 32 bytes @@ -227,15 +223,13 @@ public: PrimitiveInfo primitive; // 24 bytes bool operator < (Command const& rhs) const noexcept { return key < rhs.key; } // placement new declared as "throw" to avoid the compiler's null-check - inline void* operator new (std::size_t size, void* ptr) { + inline void* operator new (std::size_t, void* ptr) { assert_invariant(ptr); return ptr; } }; - static_assert(sizeof(Command) == 32); - - static_assert(std::is_trivially_destructible::value, + static_assert(std::is_trivially_destructible_v, "Command isn't trivially destructible"); using RenderFlags = uint8_t; @@ -246,62 +240,118 @@ public: static constexpr RenderFlags HAS_FOG = 0x10; static constexpr RenderFlags HAS_VSM = 0x20; + // Arena used for commands + using Arena = utils::Arena< + utils::LinearAllocator, + utils::LockingPolicy::NoLock, + utils::TrackingPolicy::HighWatermark, + utils::AreaPolicy::StaticArea>; - RenderPass(FEngine& engine, utils::GrowingSlice commands) noexcept; + /* + * Create a RenderPass. + * The Arena is used to allocate commands which are then owned by the Arena. + */ + RenderPass(FEngine& engine, Arena& arena) noexcept; + + // Copy the RenderPass as is. This can be used to create a RenderPass from a "template" + // by copying from an "empty" RenderPass. RenderPass(RenderPass const& rhs); + + // allocated commands ARE NOT freed, they're owned by the Arena ~RenderPass() noexcept; + // if non-null, overrides the material's polygon offset void overridePolygonOffset(backend::PolygonOffset* polygonOffset) noexcept; + + // specifies the geometry to generate commands for void setGeometry(FScene::RenderableSoa const& soa, utils::Range vr, backend::Handle uboHandle) noexcept; - void setCamera(const CameraInfo& camera) noexcept; - void setRenderFlags(RenderFlags flags) noexcept; + + // specifies camera information (e.g. used for sorting commands) + void setCamera(const CameraInfo& camera) noexcept { mCamera = camera; } + + // flags controling how commands are generated + void setRenderFlags(RenderFlags flags) noexcept { mFlags = flags; } // Sets the visibility mask, which is AND-ed against each Renderable's VISIBLE_MASK to determine // if the renderable is visible for this pass. // Defaults to all 1's, which means all renderables in this render pass will be rendered. void setVisibilityMask(FScene::VisibleMaskType mask) noexcept { mVisibilityMask = mask; } - // Resets the visibility mask to the default value of all 1's. - void clearVisibilityMask() noexcept { - mVisibilityMask = std::numeric_limits::max(); - } + Command const* begin() const noexcept { return mCommandBegin; } + Command const* end() const noexcept { return mCommandEnd; } - Command* begin() noexcept { return mCommands.begin(); } - Command* end() noexcept { return mCommands.end(); } + // This is the main function of this class, this appends commands to the pass using + // the current camera, geometry and flags set. This can be called multiple times if needed. + void appendCommands(CommandTypeFlags commandTypeFlags) noexcept; - Command const* begin() const noexcept { return mCommands.begin(); } - Command const* end() const noexcept { return mCommands.end(); } - - Command* newCommandBuffer() noexcept; - - // returns mCommands.end() - Command* appendCommands(CommandTypeFlags commandTypeFlags) noexcept; - - // returns mCommands.end() - Command* appendCustomCommand(Pass pass, CustomCommand custom, uint32_t order, + // Appends a custom command. + void appendCustomCommand(Pass pass, CustomCommand custom, uint32_t order, std::function command); - // sorts commands, then trims sentinels and returns - // the new mCommands.end() - Command* sortCommands() noexcept; + // sorts commands, then trims sentinels + void sortCommands() noexcept; + // Helper to execute all the commands generated by this RenderPass void execute(const char* name, backend::Handle renderTarget, - backend::RenderPassParams params) const noexcept; + backend::RenderPassParams params) const noexcept { + getExecutor().execute(name, renderTarget, params); + } - void executeCommands(const char* name) const noexcept; + /* + * Executor holds the range of commands to execute for a given pass + */ + class Executor { + using CustomCommandFn = std::function; + using CustomCommandVector = std::vector>; - utils::GrowingSlice& getCommands() { return mCommands; } - utils::Slice const& getCommands() const { return mCommands; } + friend class RenderPass; + FEngine& mEngine; + Command const* mBegin; + Command const* mEnd; + const CustomCommandVector mCustomCommands; + const backend::Handle mUboHandle; + const backend::PolygonOffset mPolygonOffset; + const bool mPolygonOffsetOverride; - size_t getCommandsHighWatermark() const noexcept { - return mCommandsHighWatermark * sizeof(Command); + Executor(RenderPass const* pass, Command const* b, Command const* e) noexcept + : mEngine(pass->mEngine), mBegin(b), mEnd(e), + mCustomCommands(pass->mCustomCommands), mUboHandle(pass->mUboHandle), + mPolygonOffset(pass->mPolygonOffset), + mPolygonOffsetOverride(pass->mPolygonOffsetOverride) { + assert_invariant(b >= pass->begin()); + assert_invariant(e <= pass->end()); + } + + void recordDriverCommands(backend::DriverApi& driver, const Command* first, + const Command* last) const noexcept; + + public: + void execute(const char* name, + backend::Handle renderTarget, + backend::RenderPassParams params) const noexcept; + + void executeCommands(const char* name) const noexcept; + }; + + // returns a new executor for this pass + Executor getExecutor() const { + return { this, mCommandBegin, mCommandEnd }; + } + + // returns a new executor for this pass with a custom range + Executor getExecutor(Command const* b, Command const* e) const { + return { this, b, e }; } private: friend class FRenderer; + Command* append(size_t count) noexcept; + void resize(size_t count) noexcept; + // on 64-bits systems, we process batches of 256 (64 bytes) cache-lines, or 512 (32 bytes) commands // on 32-bits systems, we process batches of 512 (32 bytes) cache-lines, or 512 (32 bytes) commands static constexpr size_t JOBS_PARALLEL_FOR_COMMANDS_COUNT = 512; @@ -324,9 +374,6 @@ private: static void setupColorCommand(Command& cmdDraw, FMaterialInstance const* mi, bool inverseFrontFaces) noexcept; - void recordDriverCommands(FEngine::DriverApi& driver, const Command* first, - const Command* last) const noexcept; - static void updateSummedPrimitiveCounts( FScene::RenderableSoa& renderableData, utils::Range vr) noexcept; @@ -337,30 +384,41 @@ private: // a reference to the Engine, mostly to get to things like JobSystem FEngine& mEngine; - utils::GrowingSlice mCommands; + // Arena where all Commands are allocated. The Arena owns the commands. + Arena& mCommandArena; + + // Pointer to the first command + Command* mCommandBegin = nullptr; + + // Pointer to one past the last command + Command* mCommandEnd = nullptr; // the SOA containing the renderables we're interested in FScene::RenderableSoa const* mRenderableSoa = nullptr; - // and the range of visible renderables in the SOA above + + // The range of visible renderables in the SOA above utils::Range mVisibleRenderables{}; + // the UBO containing the data for the renderables backend::Handle mUboHandle; // info about the camera CameraInfo mCamera; + // info about the scene features (e.g.: has shadows, lighting, etc...) RenderFlags mFlags{}; + + // Additional visibility mask FScene::VisibleMaskType mVisibilityMask = std::numeric_limits::max(); + // whether to override the polygon offset setting bool mPolygonOffsetOverride = false; + // value of the override backend::PolygonOffset mPolygonOffset{}; // a vector for our custom commands mutable CustomCommandVector mCustomCommands; - - // high watermark for debugging - size_t mCommandsHighWatermark = 0; }; } // namespace filament diff --git a/filament/src/Renderer.cpp b/filament/src/Renderer.cpp index 9425662c0a..be1fea209f 100644 --- a/filament/src/Renderer.cpp +++ b/filament/src/Renderer.cpp @@ -271,12 +271,13 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { FScene& scene = *view.getScene(); - const size_t commandsSize = FEngine::CONFIG_PER_FRAME_COMMANDS_SIZE; - const size_t commandsCount = commandsSize / sizeof(Command); - GrowingSlice commands( - arena.allocate(commandsCount, CACHELINE_SIZE), commandsCount); + // Allocate some space for our commands in the per-frame Arena, and use that space as + // an Arena for commands. All this space is released when we exit this method. + void* const arenaBegin = arena.allocate(FEngine::CONFIG_PER_FRAME_COMMANDS_SIZE, CACHELINE_SIZE); + void* const arenaEnd = pointermath::add(arenaBegin, FEngine::CONFIG_PER_FRAME_COMMANDS_SIZE); + RenderPass::Arena commandArena("Command Arena", { arenaBegin, arenaEnd }); - RenderPass pass(engine, commands); + RenderPass pass(engine, commandArena); RenderPass::RenderFlags renderFlags = 0; if (view.hasShadowing()) renderFlags |= RenderPass::HAS_SHADOWING; if (view.hasDirectionalLight()) renderFlags |= RenderPass::HAS_DIRECTIONAL_LIGHT; @@ -390,10 +391,15 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { CameraInfo cameraInfo = view.getCameraInfo(); + // updatePrimitivesLod must be run before appendCommands and once for each set + // of RenderPass::setCamera / RenderPass::setGeometry calls. + view.updatePrimitivesLod(engine, cameraInfo, + scene.getRenderableData(), view.getVisibleRenderables()); + pass.setCamera(cameraInfo); pass.setGeometry(scene.getRenderableData(), view.getVisibleRenderables(), scene.getRenderableUBO()); - view.updatePrimitivesLod(engine, cameraInfo, scene.getRenderableData(), view.getVisibleRenderables()); + // view set-ups that need to happen before rendering fg.addTrivialSideEffectPass("Prepare View Uniforms", [svp, &view] (DriverApi& driver) { CameraInfo cameraInfo = view.getCameraInfo(); view.prepareCamera(cameraInfo); @@ -406,13 +412,13 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { // Currently it consists of a simple depth pass. // This is normally used by SSAO and contact-shadows - // TODO: this should be a FrameGraph pass to participate to automatic culling - pass.newCommandBuffer(); - pass.appendCommands(RenderPass::CommandTypeFlags::SSAO); - pass.sortCommands(); + // TODO: ideally this should be a FrameGraph pass to participate to automatic culling + RenderPass structurePass(pass); + structurePass.appendCommands(RenderPass::CommandTypeFlags::SSAO); + structurePass.sortCommands(); // TODO: the scaling should depends on all passes that need the structure pass - ppm.structure(fg, pass, svp.width, svp.height, aoOptions.resolution); + ppm.structure(fg, structurePass, svp.width, svp.height, aoOptions.resolution); // Apply the TAA jitter to everything after the structure pass, starting with the color pass. if (taaOptions.enabled) { @@ -435,14 +441,13 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { if (aoOptions.enabled) { // we could rely on FrameGraph culling, but this creates unnecessary CPU work - ppm.screenSpaceAmbientOcclusion(fg, pass, svp, cameraInfo, aoOptions); + ppm.screenSpaceAmbientOcclusion(fg, svp, cameraInfo, aoOptions); } // -------------------------------------------------------------------------------------------- // Color passes // TODO: ideally this should be a FrameGraph pass to participate to automatic culling - pass.newCommandBuffer(); pass.appendCommands(RenderPass::COLOR); pass.sortCommands(); @@ -478,7 +483,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { // the color pass itself + color-grading as subpass if needed FrameGraphId colorPassOutput = colorPass(fg, "Color Pass", - desc, config, colorGradingConfigForColor, pass, view); + desc, config, colorGradingConfigForColor, pass.getExecutor(), view); // the color pass + refraction + color-grading as subpass if needed // this cancels the colorPass() call above if refraction is active. @@ -584,7 +589,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { // save the current history entry and destroy the oldest entry view.commitFrameHistory(engine); - recordHighWatermark(pass.getCommandsHighWatermark()); + //recordHighWatermark(pass.getCommandsHighWatermark()); } FrameGraphId FRenderer::refractionPass(FrameGraph& fg, @@ -613,10 +618,7 @@ FrameGraphId FRenderer::refractionPass(FrameGraph& fg, blackboard.remove("color"); blackboard.remove("depth"); - RenderPass opaquePass(pass); - opaquePass.getCommands().set( - const_cast(pass.begin()), - const_cast(refraction)); + const RenderPass::Executor opaquePass{ pass.getExecutor(pass.begin(), refraction) }; FrameGraphTexture::Descriptor desc = { .width = config.svp.width, @@ -688,10 +690,7 @@ FrameGraphId FRenderer::refractionPass(FrameGraph& fg, // ^^^ the actual refraction pass ends above ^^^ // set-up the refraction pass - RenderPass translucentPass(pass); - translucentPass.getCommands().set( - const_cast(refraction), - const_cast(pass.end())); + const RenderPass::Executor translucentPass{ pass.getExecutor(refraction, pass.end()) }; config.refractionLodOffset = refractionLodOffset; config.clearFlags = TargetBufferFlags::NONE; @@ -714,7 +713,7 @@ FrameGraphId FRenderer::refractionPass(FrameGraph& fg, FrameGraphId FRenderer::colorPass(FrameGraph& fg, const char* name, FrameGraphTexture::Descriptor const& colorBufferDesc, ColorPassConfig const& config, PostProcessManager::ColorGradingConfig colorGradingConfig, - RenderPass const& pass, FView const& view) const noexcept { + RenderPass::Executor const& passExecutor, FView const& view) const noexcept { struct ColorPassData { FrameGraphId shadows; @@ -850,16 +849,15 @@ FrameGraphId FRenderer::colorPass(FrameGraph& fg, const char* if (colorGradingConfig.asSubpass) { out.params.subpassMask = 1; + // TODO: we should implement this with a RenderPass command driver.beginRenderPass(out.target, out.params); - pass.executeCommands(resources.getPassName()); + passExecutor.executeCommands(resources.getPassName()); ppm.colorGradingSubpass(driver, colorGradingConfig); + driver.endRenderPass(); } else { - driver.beginRenderPass(out.target, out.params); - pass.executeCommands(resources.getPassName()); + passExecutor.execute(resources.getPassName(), out.target, out.params); } - driver.endRenderPass(); - // color pass is typically heavy and we don't have much CPU work left after // this point, so flushing now allows us to start the GPU earlier and reduce // latency, without creating bubbles. diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index 3ffc3d586a..39f6d41396 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -69,24 +69,23 @@ ShadowMap::~ShadowMap() { engine.getEntityManager().destroy(sizeof(entities) / sizeof(Entity), entities); } -void ShadowMap::render(DriverApi& driver, FView::Range const& range, RenderPass& pass, - FView& view) noexcept { +void ShadowMap::render(DriverApi& driver, FView::Range const& range, + RenderPass* const pass, FView& view) noexcept { FEngine& engine = mEngine; + filament::CameraInfo cameraInfo(getCamera()); + FScene& scene = *view.getScene(); + FScene::RenderableSoa& renderableData = scene.getRenderableData(); - FCamera const& camera = getCamera(); - filament::CameraInfo cameraInfo(camera); - - pass.setCamera(cameraInfo); - pass.setGeometry(scene.getRenderableData(), range, scene.getRenderableUBO()); - + pass->setCamera(cameraInfo); + pass->setGeometry(renderableData, range, scene.getRenderableUBO()); // updatePrimitivesLod must be run before appendCommands. - view.updatePrimitivesLod(engine, cameraInfo, scene.getRenderableData(), range); + view.updatePrimitivesLod(engine, cameraInfo, renderableData, range); - pass.newCommandBuffer(); - pass.appendCommands(RenderPass::SHADOW); - pass.sortCommands(); + pass->overridePolygonOffset(&mPolygonOffset); + pass->appendCommands(RenderPass::SHADOW); + pass->sortCommands(); } mat4f ShadowMap::getLightViewMatrix(float3 position, float3 direction) noexcept { diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index d1ba7237b2..689e06bec0 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -77,7 +77,7 @@ void ShadowMapManager::addSpotShadowMap(size_t lightIndex) noexcept { } void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, FView& view, - backend::DriverApi& driver, RenderPass& pass) noexcept { + backend::DriverApi& driver, RenderPass const& pass) noexcept { constexpr size_t MAX_SHADOW_LAYERS = CONFIG_MAX_SHADOW_CASCADES + CONFIG_MAX_SHADOW_CASTING_SPOTS; @@ -88,37 +88,45 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, FView& view, const TextureRequirements textureRequirements = mTextureRequirements; assert_invariant(textureRequirements.layers <= MAX_SHADOW_LAYERS); - using ShadowPass = std::pair; - auto passes = utils::FixedCapacityVector::with_capacity(MAX_SHADOW_LAYERS); + struct ShadowPass { + ShadowPass(ShadowMapEntry const* shadowmap, RenderPass const& pass) noexcept + : shadowmap(shadowmap), pass(pass) { + } + ShadowMapEntry const* shadowmap; + RenderPass pass; + }; + + auto passList = utils::FixedCapacityVector::with_capacity(MAX_SHADOW_LAYERS); // These loops fill render passes with appropriate rendering commands for each shadow map. // The actual render pass execution is deferred to the frame graph. // Directional, cascaded shadowmaps for (const auto& map : mCascadeShadowMaps) { - if (!map.hasVisibleShadows()) { + auto& range = view.getVisibleDirectionalShadowCasters(); + if (!map.hasVisibleShadows() || range.empty()) { continue; } - map.getShadowMap().render(driver, view.getVisibleDirectionalShadowCasters(), pass, view); - passes.emplace_back(&map, pass); + auto& entry = passList.emplace_back(&map, pass); + map.getShadowMap().render(driver, range, &entry.pass, view); } // Spotlight shadowmaps for (size_t i = 0, c = mSpotShadowMaps.size(); i < c; i++) { const auto& map = mSpotShadowMaps[i]; - if (!map.hasVisibleShadows()) { + auto& range = view.getVisibleSpotShadowCasters(); + if (!map.hasVisibleShadows() || range.empty()) { continue; } - pass.setVisibilityMask(VISIBLE_SPOT_SHADOW_RENDERABLE_N(i)); - map.getShadowMap().render(driver, view.getVisibleSpotShadowCasters(), pass, view); - pass.clearVisibilityMask(); - passes.emplace_back(&map, pass); + auto& entry = passList.emplace_back(&map, pass); + entry.pass.setVisibilityMask(VISIBLE_SPOT_SHADOW_RENDERABLE_N(i)); + map.getShadowMap().render(driver, range, &entry.pass, view); } const float vsmMoment2 = std::numeric_limits::max(); const float vsmMoment1 = std::sqrt(vsmMoment2); const float4 vsmClearColor{ vsmMoment1, vsmMoment2, 0.0f, 0.0f }; - assert_invariant(passes.size() <= textureRequirements.layers); + assert_invariant(passList.size() <= textureRequirements.layers); // ------------------------------------------------------------------------------------------- @@ -151,13 +159,12 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, FView& view, auto& ppm = engine.getPostProcessManager(); - for (auto& entry : passes) { - auto* map = entry.first; - auto& pass = entry.second; + for (auto& entry : passList) { + if (!entry.shadowmap->hasVisibleShadows()) { + continue; + } - if (!map->hasVisibleShadows()) continue; - - ShadowLayout const& layout = map->getLayout(); + ShadowLayout const& layout = entry.shadowmap->getLayout(); const auto layer = layout.layer; const auto* options = layout.options; @@ -230,14 +237,14 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, FView& view, // finally create the shadowmap render target -- one per layer. data.shadowRt = builder.declareRenderPass("Shadow RT", renderTargetDesc); }, - [=, passes = std::move(passes), &view](FrameGraphResources const& resources, - auto const& data, DriverApi& driver) mutable { + [=, executor = entry.pass.getExecutor(), &view](FrameGraphResources const& resources, + auto const& data, DriverApi& driver) { const auto& options = layout.options; const bool blur = view.hasVsm() && options->vsm.blurWidth > 0.0f; // TODO: camera is already set inside 'pass', we could get it from there - FCamera const& camera = map->getShadowMap().getCamera(); + FCamera const& camera = entry.shadowmap->getShadowMap().getCamera(); filament::CameraInfo cameraInfo(camera); view.prepareCamera(cameraInfo); @@ -268,10 +275,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, FView& view, rt.params.viewport = viewport; - auto polygonOffset = map->getShadowMap().getPolygonOffset(); - pass.overridePolygonOffset(&polygonOffset); - - pass.execute("Shadow Pass", rt.target, rt.params); + executor.execute("Shadow Pass", rt.target, rt.params); }); diff --git a/filament/src/View.cpp b/filament/src/View.cpp index 00d34d527c..0871ddf0c7 100644 --- a/filament/src/View.cpp +++ b/filament/src/View.cpp @@ -863,7 +863,7 @@ void FView::updatePrimitivesLod(FEngine& engine, const CameraInfo&, } void FView::renderShadowMaps(FrameGraph& fg, FEngine& engine, FEngine::DriverApi& driver, - RenderPass& pass) noexcept { + RenderPass const& pass) noexcept { mShadowMapManager.render(fg, engine, *this, driver, pass); } diff --git a/filament/src/details/Renderer.h b/filament/src/details/Renderer.h index 32f7f345d3..5395f21f19 100644 --- a/filament/src/details/Renderer.h +++ b/filament/src/details/Renderer.h @@ -155,7 +155,7 @@ private: FrameGraphTexture::Descriptor const& colorBufferDesc, ColorPassConfig const& config, PostProcessManager::ColorGradingConfig colorGradingConfig, - RenderPass const& pass, FView const& view) const noexcept; + RenderPass::Executor const& passExecutor, FView const& view) const noexcept; FrameGraphId refractionPass(FrameGraph& fg, ColorPassConfig config, diff --git a/filament/src/details/ShadowMap.h b/filament/src/details/ShadowMap.h index 8cf8cdcdf6..b86329611b 100644 --- a/filament/src/details/ShadowMap.h +++ b/filament/src/details/ShadowMap.h @@ -95,8 +95,8 @@ public: filament::CameraInfo const& camera, const ShadowMapInfo& shadowMapInfo, const SceneInfo& cascadeParams) noexcept; - void render(backend::DriverApi& driver, utils::Range const& range, RenderPass& pass, - FView& view) noexcept; + void render(backend::DriverApi& driver, utils::Range const& range, + RenderPass* pass, FView& view) noexcept; // Do we have visible shadows. Valid after calling update(). bool hasVisibleShadows() const noexcept { return mHasVisibleShadows; } diff --git a/filament/src/details/ShadowMapManager.h b/filament/src/details/ShadowMapManager.h index 650299998d..152acb9520 100644 --- a/filament/src/details/ShadowMapManager.h +++ b/filament/src/details/ShadowMapManager.h @@ -75,7 +75,7 @@ public: // Renders all of the shadow maps. void render(FrameGraph& fg, FEngine& engine, FView& view, backend::DriverApi& driver, - RenderPass& pass) noexcept; + RenderPass const& pass) noexcept; const ShadowMap* getCascadeShadowMap(size_t c) const noexcept { assert_invariant(c < mCascadeShadowMapCache.size()); diff --git a/filament/src/details/View.h b/filament/src/details/View.h index 0773289549..ad5ce39e15 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -183,7 +183,7 @@ public: bool hasVsm() const noexcept { return mShadowType == ShadowType::VSM; } void renderShadowMaps(FrameGraph& fg, FEngine& engine, FEngine::DriverApi& driver, - RenderPass& pass) noexcept; + RenderPass const& pass) noexcept; void updatePrimitivesLod( FEngine& engine, const CameraInfo& camera, diff --git a/filament/src/fg2/FrameGraphPass.h b/filament/src/fg2/FrameGraphPass.h index dc2c7e3207..15f76717ff 100644 --- a/filament/src/fg2/FrameGraphPass.h +++ b/filament/src/fg2/FrameGraphPass.h @@ -58,7 +58,7 @@ class FrameGraphPass : public FrameGraphPassBase { friend class FrameGraph; // allow our allocators to instantiate us - template + template friend class utils::Arena; explicit FrameGraphPass(Execute&& execute) noexcept diff --git a/libs/utils/include/utils/Allocator.h b/libs/utils/include/utils/Allocator.h index 3226ababc0..e76ae6c2de 100644 --- a/libs/utils/include/utils/Allocator.h +++ b/libs/utils/include/utils/Allocator.h @@ -385,6 +385,39 @@ using ThreadSafeObjectPoolAllocator = PoolAllocator + typename TrackingPolicy = TrackingPolicy::Untracked, + typename AreaPolicy = AreaPolicy::HeapArea> class Arena { public: @@ -536,7 +571,15 @@ public: : mArenaName(name), mArea(size), mAllocator(mArea, std::forward(args) ... ), - mListener(name, mArea.data(), size) { + mListener(name, mArea.data(), mArea.size()) { + } + + template + Arena(const char* name, AreaPolicy&& area, ARGS&& ... args) + : mArenaName(name), + mArea(std::forward(area)), + mAllocator(mArea, std::forward(args) ... ), + mListener(name, mArea.data(), mArea.size()) { } // allocate memory from arena with given size and alignment @@ -616,8 +659,8 @@ public: TrackingPolicy& getListener() noexcept { return mListener; } TrackingPolicy const& getListener() const noexcept { return mListener; } - HeapArea& getArea() noexcept { return mArea; } - HeapArea const& getArea() const noexcept { return mArea; } + AreaPolicy& getArea() noexcept { return mArea; } + AreaPolicy const& getArea() const noexcept { return mArea; } void setListener(TrackingPolicy listener) noexcept { std::swap(mListener, listener); @@ -644,7 +687,7 @@ public: private: char const* mArenaName = nullptr; - HeapArea mArea; // We might want to make that a template parameter too eventually. + AreaPolicy mArea; // note: we should use something like compressed_pair for the members below AllocatorPolicy mAllocator; LockingPolicy mLock;