diff --git a/filament/backend/include/private/backend/CommandStream.h b/filament/backend/include/private/backend/CommandStream.h index 8e0432955a..a2475f6b1d 100644 --- a/filament/backend/include/private/backend/CommandStream.h +++ b/filament/backend/include/private/backend/CommandStream.h @@ -64,8 +64,10 @@ public: // executes this command and returns the next one inline CommandBase* execute(Driver& driver) { - // it is important to return the next command offset by output parameter instead - // of return value -- it allows the compiler to perform the tail call optimization. + // returning the next command by output parameter allows the compiler to perform the + // tail-call optimization in the function called by mExecute, however that comes at + // a cost here (writing and reading the stack at each iteration), in the end it's + // probably better to pay the cost at just one location. intptr_t next; mExecute(driver, this, &next); return reinterpret_cast(reinterpret_cast(this) + next); @@ -184,8 +186,8 @@ public: #if !defined(NDEBUG) || (FILAMENT_DEBUG_COMMANDS >= FILAMENT_DEBUG_COMMANDS_ENABLE) // For now, simply pass the method name down as a string and throw away the parameters. // This is good enough for certain debugging needs and we can improve this later. - #define DEBUG_COMMAND_BEGIN(methodName, sync, ...) mDriver->debugCommandBegin(this, sync, #methodName) - #define DEBUG_COMMAND_END(methodName, sync) mDriver->debugCommandEnd(this, sync, #methodName) + #define DEBUG_COMMAND_BEGIN(methodName, sync, ...) mDriver.debugCommandBegin(this, sync, #methodName) + #define DEBUG_COMMAND_END(methodName, sync) mDriver.debugCommandEnd(this, sync, #methodName) #else #define DEBUG_COMMAND_BEGIN(methodName, sync, ...) #define DEBUG_COMMAND_END(methodName, sync) @@ -200,9 +202,11 @@ class CommandStream { }; public: - CommandStream() noexcept = default; CommandStream(Driver& driver, CircularBuffer& buffer) noexcept; + CommandStream(CommandStream const& rhs) noexcept = delete; + CommandStream& operator=(CommandStream const& rhs) noexcept = delete; + public: #define DECL_DRIVER_API(methodName, paramsDecl, params) \ inline void methodName(paramsDecl) { \ @@ -219,13 +223,13 @@ public: AutoExecute callOnExit([=](){ \ DEBUG_COMMAND_END(methodName, true); \ }); \ - return apply(&Driver::methodName, *mDriver, std::forward_as_tuple(params)); \ + return apply(&Driver::methodName, mDriver, std::forward_as_tuple(params)); \ } #define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \ inline RetType methodName(paramsDecl) { \ DEBUG_COMMAND_BEGIN(methodName, false, params); \ - RetType result = mDriver->methodName##S(); \ + RetType result = mDriver.methodName##S(); \ using Cmd = COMMAND_TYPE(methodName##R); \ void* const p = allocateCommand(CommandBase::align(sizeof(Cmd))); \ new(p) Cmd(mDispatcher.methodName##_, RetType(result), APPLY(std::move, params)); \ @@ -271,14 +275,14 @@ public: private: inline void* allocateCommand(size_t size) { assert_invariant(utils::ThreadUtils::isThisThread(mThreadId)); - return mCurrentBuffer->allocate(size); + return mCurrentBuffer.allocate(size); } // We use a copy of Dispatcher (instead of a pointer) because this removes one dereference // when executing driver commands. + Driver& UTILS_RESTRICT mDriver; + CircularBuffer& UTILS_RESTRICT mCurrentBuffer; Dispatcher mDispatcher; - Driver* UTILS_RESTRICT mDriver = nullptr; - CircularBuffer* UTILS_RESTRICT mCurrentBuffer = nullptr; #ifndef NDEBUG // just for debugging... diff --git a/filament/backend/src/CircularBuffer.cpp b/filament/backend/src/CircularBuffer.cpp index 820b57d973..20135fff3f 100644 --- a/filament/backend/src/CircularBuffer.cpp +++ b/filament/backend/src/CircularBuffer.cpp @@ -33,8 +33,7 @@ using namespace utils; -namespace filament { -namespace backend { +namespace filament::backend { CircularBuffer::CircularBuffer(size_t size) { mData = alloc(size); @@ -56,6 +55,7 @@ CircularBuffer::~CircularBuffer() noexcept { // If the system does not support mmap, emulate soft circular buffer with two buffers next // to each others and a special case in circularize() +UTILS_NOINLINE void* CircularBuffer::alloc(size_t size) noexcept { #if HAS_MMAP void* data = nullptr; @@ -127,6 +127,7 @@ void* CircularBuffer::alloc(size_t size) noexcept { #endif } +UTILS_NOINLINE void CircularBuffer::dealloc() noexcept { #if HAS_MMAP if (mData) { @@ -162,5 +163,4 @@ void CircularBuffer::circularize() noexcept { mTail = mHead; } -} // namespace backend -} // namespace filament +} // namespace filament::backend diff --git a/filament/backend/src/CommandStream.cpp b/filament/backend/src/CommandStream.cpp index ffc3d5f489..5f5c75dfb0 100644 --- a/filament/backend/src/CommandStream.cpp +++ b/filament/backend/src/CommandStream.cpp @@ -56,9 +56,9 @@ static UTILS_NOINLINE UTILS_UNUSED std::string extractMethodName(std::string& co // ------------------------------------------------------------------------------------------------ CommandStream::CommandStream(Driver& driver, CircularBuffer& buffer) noexcept - : mDispatcher(driver.getDispatcher()), - mDriver(&driver), - mCurrentBuffer(&buffer) + : mDriver(driver), + mCurrentBuffer(buffer), + mDispatcher(driver.getDispatcher()) #ifndef NDEBUG , mThreadId(ThreadUtils::getThreadId()) #endif @@ -83,8 +83,8 @@ void CommandStream::execute(void* buffer) { } } - mDriver->execute([this, buffer]() { - Driver& UTILS_RESTRICT driver = *mDriver; + mDriver.execute([this, buffer]() { + Driver& UTILS_RESTRICT driver = mDriver; CommandBase* UTILS_RESTRICT base = static_cast(buffer); while (UTILS_LIKELY(base)) { base = base->execute(driver); diff --git a/filament/backend/test/BackendTest.cpp b/filament/backend/test/BackendTest.cpp index 3a9a1e6375..69647381e5 100644 --- a/filament/backend/test/BackendTest.cpp +++ b/filament/backend/test/BackendTest.cpp @@ -59,7 +59,7 @@ void BackendTest::initializeDriver() { DefaultPlatform* platform = DefaultPlatform::create(&backend); assert_invariant(static_cast(backend) == static_cast(sBackend)); driver = platform->createDriver(nullptr); - commandStream = CommandStream(*driver, commandBufferQueue.getCircularBuffer()); + commandStream = std::make_unique(*driver, commandBufferQueue.getCircularBuffer()); } void BackendTest::executeCommands() { @@ -67,7 +67,7 @@ void BackendTest::executeCommands() { auto buffers = commandBufferQueue.waitForCommands(); for (auto& item : buffers) { if (UTILS_LIKELY(item.begin)) { - commandStream.execute(item.begin); + getDriverApi().execute(item.begin); commandBufferQueue.releaseBuffer(item); } } @@ -84,7 +84,7 @@ void BackendTest::flushAndWait(uint64_t timeout) { Handle BackendTest::createSwapChain() { const NativeView& view = getNativeView(); - return commandStream.createSwapChain(view.ptr, 0); + return getDriverApi().createSwapChain(view.ptr, 0); } void BackendTest::fullViewport(RenderPassParams& params) { diff --git a/filament/backend/test/BackendTest.h b/filament/backend/test/BackendTest.h index 11587c8425..266f4abc9b 100644 --- a/filament/backend/test/BackendTest.h +++ b/filament/backend/test/BackendTest.h @@ -58,14 +58,14 @@ protected: void readPixelsAndAssertHash(const char* testName, size_t width, size_t height, filament::backend::Handle rt, uint32_t expectedHash); - filament::backend::DriverApi& getDriverApi() { return commandStream; } + filament::backend::DriverApi& getDriverApi() { return *commandStream; } filament::backend::Driver& getDriver() { return *driver; } private: filament::backend::Driver* driver = nullptr; filament::backend::CommandBufferQueue commandBufferQueue; - filament::backend::DriverApi commandStream; + std::unique_ptr commandStream; filament::backend::Handle uniform; }; diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index 5dcf453f41..c42e34a81d 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -208,7 +208,9 @@ void FEngine::init() { SYSTRACE_CALL(); // this must be first. - mCommandStream = CommandStream(*mDriver, mCommandBufferQueue.getCircularBuffer()); + assert_invariant( intptr_t(&mDriverApiStorage) % alignof(DriverApi) == 0 ); + ::new(&mDriverApiStorage) DriverApi(*mDriver, mCommandBufferQueue.getCircularBuffer()); + DriverApi& driverApi = getDriverApi(); mResourceAllocator = new ResourceAllocator(driverApi); @@ -338,6 +340,12 @@ FEngine::~FEngine() noexcept { SYSTRACE_CALL(); ASSERT_DESTRUCTOR(mTerminated, "Engine destroyed but not terminated!"); + + // we use mResourceAllocator as a proxy for knowing if init() was called + if (mResourceAllocator) { + std::destroy_at(std::launder(reinterpret_cast(&mDriverApiStorage))); + } + delete mResourceAllocator; delete mDriver; if (mOwnPlatform) { @@ -1000,7 +1008,7 @@ bool FEngine::execute() { // execute all command buffers for (auto& item : buffers) { if (UTILS_LIKELY(item.begin)) { - mCommandStream.execute(item.begin); + getDriverApi().execute(item.begin); mCommandBufferQueue.releaseBuffer(item); } } diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index 13fa6e0796..2a4f38445b 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -76,6 +76,7 @@ using MaterialKey = uint32_t; #include #include +#include #include #include @@ -147,7 +148,11 @@ public: ~FEngine() noexcept; backend::Driver& getDriver() const noexcept { return *mDriver; } - DriverApi& getDriverApi() noexcept { return mCommandStream; } + + DriverApi& getDriverApi() noexcept { + return *std::launder(reinterpret_cast(&mDriverApiStorage)); + } + DFG* getDFG() const noexcept { return mDFG.get(); } // the per-frame Area is used by all Renderer, so they must run in sequence and @@ -423,7 +428,9 @@ private: std::thread mDriverThread; backend::CommandBufferQueue mCommandBufferQueue; - DriverApi mCommandStream; + std::aligned_storage::type mDriverApiStorage; + static_assert( sizeof(mDriverApiStorage) >= sizeof(DriverApi) ); + uint32_t mFlushCounter = 0; LinearAllocatorArena mPerRenderPassAllocator;