diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 6054f53825..da63b822a0 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -335,13 +335,6 @@ public: destroy(camera->getEntity()); } - /** - * Invokes one iteration of the render loop, used only on single-threaded platforms. - * - * This should be called every time the windowing system needs to paint (e.g. at 60 Hz). - */ - void execute(); - DebugRegistry& getDebugRegistry() noexcept; protected: diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp index 261258bd43..878890a0c2 100644 --- a/filament/src/Engine.cpp +++ b/filament/src/Engine.cpp @@ -74,22 +74,11 @@ static std::mutex sEnginesLock; FEngine* FEngine::create(Backend backend, ExternalContext* externalContext, void* sharedGLContext) { FEngine* instance = new FEngine(backend, externalContext, sharedGLContext); - slog.i << "FEngine (" << sizeof(void*) * 8 << " bits) created at " << instance << " " - << "(threading is " << (UTILS_HAS_THREADING ? "enabled)" : "disabled)") << io::endl; + slog.i << "FEngine (" << sizeof(void*) * 8 << " bits) created at " << instance << io::endl; // initialize all fields that need an instance of FEngine // (this cannot be done safely in the ctor) - // Normally we launch a thread and create the context and Driver from there (see FEngine::loop). - // In the single-threaded case, we do so in the here and now. - if (!UTILS_HAS_THREADING) { - instance->mExternalContext = ExternalContext::create(&instance->mBackend); - instance->mDriver = instance->mExternalContext->createDriver(sharedGLContext); - instance->init(); - instance->execute(); - return instance; - } - // start the driver thread instance->mDriverThread = std::thread(&FEngine::loop, instance); @@ -303,18 +292,13 @@ void FEngine::shutdown() { // There might be commands added by the terminate() calls flushCommandBuffer(mCommandBufferQueue); - if (!UTILS_HAS_THREADING) { - execute(); - } /* * terminate the rendering engine */ mCommandBufferQueue.requestExit(); - if (UTILS_HAS_THREADING) { - mDriverThread.join(); - } + mDriverThread.join(); mTerminated = true; // detach this thread from the jobsystem @@ -378,18 +362,28 @@ int FEngine::loop() { JobSystem::setThreadName("FEngine::loop"); JobSystem::setThreadPriority(JobSystem::Priority::DISPLAY); - while (true) { + // FIXME: we should do this based on the CPUs we actually have + uint32_t affinityMask = (std::thread::hardware_concurrency() >= 6) ? 0xF0 : 0; - // FIXME: we should do this based on the CPUs we actually have - uint32_t affinityMask = (std::thread::hardware_concurrency() >= 6) ? 0xF0 : 0; + auto& commandBufferQueue = mCommandBufferQueue; + while (true) { + // wait until we get command buffers to be executed (or thread exit requested) + auto buffers = commandBufferQueue.waitForCommands(); + if (UTILS_UNLIKELY(!buffers.size())) { + break; + } if (affinityMask) { // looks like thread affinity needs to be reset regularly (on Android) JobSystem::setThreadAffinity(affinityMask); } - if (!execute()) { - break; + // execute all command buffers + for (auto& item : buffers) { + if (UTILS_LIKELY(item.begin)) { + mCommandStream.execute(item.begin); + mCommandBufferQueue.releaseBuffer(item); + } } } @@ -705,25 +699,6 @@ void* FEngine::streamAlloc(size_t size, size_t alignment) noexcept { return getDriverApi().allocate(size, alignment); } -bool FEngine::execute() { - - // wait until we get command buffers to be executed (or thread exit requested) - auto buffers = mCommandBufferQueue.waitForCommands(); - if (UTILS_UNLIKELY(!buffers.size())) { - return false; - } - - // execute all command buffers - for (auto& item : buffers) { - if (UTILS_LIKELY(item.begin)) { - mCommandStream.execute(item.begin); - mCommandBufferQueue.releaseBuffer(item); - } - } - - return true; -} - // --------------------------------------------------------------------------------------------- EnginePerformanceTest::~EnginePerformanceTest() noexcept = default; @@ -896,14 +871,6 @@ void* Engine::streamAlloc(size_t size, size_t alignment) noexcept { return upcast(this)->streamAlloc(size, alignment); } -// The external-facing execute does a flush, and is meant only for single-threaded environments. -// It also discards the boolean return value, which would otherwise indicate a thread exit. -void Engine::execute() { - ASSERT_PRECONDITION(!UTILS_HAS_THREADING, "Execute is meant for single-threaded platforms."); - upcast(this)->flush(); - upcast(this)->execute(); -} - DebugRegistry& Engine::getDebugRegistry() noexcept { return upcast(this)->getDebugRegistry(); } diff --git a/filament/src/Fence.cpp b/filament/src/Fence.cpp index 8be6f121bb..f3fb6d7e02 100644 --- a/filament/src/Fence.cpp +++ b/filament/src/Fence.cpp @@ -20,8 +20,6 @@ #include -#include - namespace filament { using namespace driver; @@ -68,8 +66,6 @@ FenceStatus FFence::waitAndDestroy(FFence* fence, Mode mode) noexcept { UTILS_NOINLINE FenceStatus FFence::wait(Mode mode, uint64_t timeout) noexcept { - ASSERT_PRECONDITION(UTILS_HAS_THREADING || timeout == 0, "Non-zero timeout requires threads."); - FEngine& engine = mEngine; if (mode == Mode::FLUSH) { diff --git a/filament/src/Renderer.cpp b/filament/src/Renderer.cpp index 4b5481aebf..6aecada28a 100644 --- a/filament/src/Renderer.cpp +++ b/filament/src/Renderer.cpp @@ -58,9 +58,7 @@ void FRenderer::init() noexcept { mRenderTarget = driver.createDefaultRenderTarget(); mIsRGB16FSupported = driver.isRenderTargetFormatSupported(driver::TextureFormat::RGB16F); mIsRGB8Supported = driver.isRenderTargetFormatSupported(driver::TextureFormat::RGB8); - if (UTILS_HAS_THREADING) { - mFrameInfoManager.run(); - } + mFrameInfoManager.run(); } FRenderer::~FRenderer() noexcept { @@ -85,14 +83,8 @@ void FRenderer::terminate(FEngine& engine) { // before we can destroy this Renderer's resources, we must make sure // that all pending commands have been executed (as they could reference data in this // instance, e.g. Fences, Callbacks, etc...) - if (UTILS_HAS_THREADING) { - Fence::waitAndDestroy(engine.createFence()); - mFrameInfoManager.terminate(); - } else { - // In single threaded mode, allow recently-created objects (e.g. no-op fences in Skipper) - // to initialize themselves, otherwise the engine tries to destroy invalid handles. - engine.execute(); - } + Fence::waitAndDestroy(engine.createFence()); + mFrameInfoManager.terminate(); } void FRenderer::render(FView const* view) { @@ -241,9 +233,7 @@ bool FRenderer::beginFrame(FSwapChain* swapChain) { assert(swapChain); mFrameId++; - if (UTILS_HAS_THREADING) { - mFrameInfoManager.beginFrame(mFrameId); - } + mFrameInfoManager.beginFrame(mFrameId); { // scope for frame id trace char buf[64]; @@ -283,16 +273,12 @@ void FRenderer::endFrame() { FEngine::DriverApi& driver = engine.getDriverApi(); RenderTargetPool& rtp = engine.getRenderTargetPool(); + // on debug builds this helps catching cases where we're writing to + // the buffer form another thread, which is currently not allowed. + driver.debugThreading(); + FrameInfoManager& frameInfoManager = mFrameInfoManager; - - if (UTILS_HAS_THREADING) { - - // on debug builds this helps catching cases where we're writing to - // the buffer form another thread, which is currently not allowed. - driver.debugThreading(); - - frameInfoManager.endFrame(); - } + frameInfoManager.endFrame(); mFrameSkipper.endFrame(); driver.endFrame(mFrameId); diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index 14e6a64db5..0101585ef9 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -343,8 +343,6 @@ public: return mDebugRegistry; } - bool execute(); - private: FEngine(Backend backend, ExternalContext* externalContext, void* sharedGLContext); void init(); diff --git a/filament/src/driver/CommandBufferQueue.cpp b/filament/src/driver/CommandBufferQueue.cpp index 03b78009d8..d3d92b46e6 100644 --- a/filament/src/driver/CommandBufferQueue.cpp +++ b/filament/src/driver/CommandBufferQueue.cpp @@ -102,11 +102,9 @@ void CommandBufferQueue::flush() noexcept { } std::vector CommandBufferQueue::waitForCommands() const { - if (UTILS_HAS_THREADING) { - std::unique_lock lock(mLock); - while (mCommandBuffersToExecute.empty() && !mExitRequested) { - mCondition.wait(lock); - } + std::unique_lock lock(mLock); + while (mCommandBuffersToExecute.empty() && !mExitRequested) { + mCondition.wait(lock); } return std::move(mCommandBuffersToExecute); } diff --git a/filament/src/driver/CommandBufferQueue.h b/filament/src/driver/CommandBufferQueue.h index 8894c9b392..007838d0cd 100644 --- a/filament/src/driver/CommandBufferQueue.h +++ b/filament/src/driver/CommandBufferQueue.h @@ -28,7 +28,7 @@ namespace filament { /* - * A producer-consumer command queue that uses a CircularBuffer as main storage + * A produdcer-consumer command queue that uses a CircularBuffer as main storage */ class CommandBufferQueue { struct Slice { diff --git a/libs/utils/include/utils/compiler.h b/libs/utils/include/utils/compiler.h index 26d545fff5..67290b4eef 100644 --- a/libs/utils/include/utils/compiler.h +++ b/libs/utils/include/utils/compiler.h @@ -72,11 +72,6 @@ # define UTILS_HAS_HYPER_THREADING 0 #endif -#if defined(__EMSCRIPTEN__) -# define UTILS_HAS_THREADING 0 -#else -# define UTILS_HAS_THREADING 1 -#endif #if __has_attribute(noinline) #define UTILS_NOINLINE __attribute__((noinline)) diff --git a/libs/utils/src/JobSystem.cpp b/libs/utils/src/JobSystem.cpp index 4e7aebd297..06bef35fe8 100644 --- a/libs/utils/src/JobSystem.cpp +++ b/libs/utils/src/JobSystem.cpp @@ -120,7 +120,7 @@ JobSystem::JobSystem(size_t threadCount, size_t adoptableThreadsCount) noexcept threadCount = hwThreads - 1; } } - threadCount = std::min(size_t(UTILS_HAS_THREADING ? 32 : 0), threadCount); + threadCount = std::min(size_t(32), threadCount); mThreadStates = aligned_vector(threadCount + adoptableThreadsCount); mThreadCount = uint16_t(threadCount); diff --git a/samples/app/FilamentApp.cpp b/samples/app/FilamentApp.cpp index 0b9f6e93b4..96414ea433 100644 --- a/samples/app/FilamentApp.cpp +++ b/samples/app/FilamentApp.cpp @@ -74,27 +74,29 @@ FilamentApp::~FilamentApp() { SDL_Quit(); } -void FilamentApp::run(const Config& config, SetupCallback setupCallback, +void FilamentApp::run(const Config& config,SetupCallback setupCallback, CleanupCallback cleanupCallback, ImGuiCallback imguiCallback, PreRenderCallback preRender, PostRenderCallback postRender, size_t width, size_t height) { + mEngine = Engine::create(config.backend); + + mDepthMaterial = Material::Builder() + .package((void*) DEPTH_VISUALIZER_PACKAGE, sizeof(DEPTH_VISUALIZER_PACKAGE)) + .build(*mEngine); + + mDepthMI = mDepthMaterial->createInstance(); + + mTransparentMaterial = Material::Builder() + .package((void*) TRANSPARENT_COLOR_PACKAGE, sizeof(TRANSPARENT_COLOR_PACKAGE)) + .build(*mEngine); + + mDefaultMaterial = Material::Builder() + .package((void*) AI_DEFAULT_MAT_PACKAGE, sizeof(AI_DEFAULT_MAT_PACKAGE)) + .build(*mEngine); + std::unique_ptr window( new FilamentApp::Window(this, config, config.title, width, height)); - mDepthMaterial = Material::Builder() - .package((void*) DEPTH_VISUALIZER_PACKAGE, sizeof(DEPTH_VISUALIZER_PACKAGE)) - .build(*mEngine); - - mDepthMI = mDepthMaterial->createInstance(); - - mDefaultMaterial = Material::Builder() - .package((void*) AI_DEFAULT_MAT_PACKAGE, sizeof(AI_DEFAULT_MAT_PACKAGE)) - .build(*mEngine); - - mTransparentMaterial = Material::Builder() - .package((void*) TRANSPARENT_COLOR_PACKAGE, sizeof(TRANSPARENT_COLOR_PACKAGE)) - .build(*mEngine); - std::unique_ptr cameraCube(new Cube(*mEngine, mTransparentMaterial, {1,0,0})); // we can't cull the light-frustum because it's not applied a rigid transform // and currently, filament assumes that for culling @@ -204,10 +206,6 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback, while (!mClosed) { - if (!UTILS_HAS_THREADING) { - mEngine->execute(); - } - // Allow the app to animate the scene if desired. if (mAnimation) { double now = (double) SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency(); @@ -425,14 +423,10 @@ FilamentApp::Window::Window(FilamentApp* filamentApp, : mFilamentApp(filamentApp) { const int x = SDL_WINDOWPOS_CENTERED; const int y = SDL_WINDOWPOS_CENTERED; - const uint32_t windowFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; + const uint32_t windowFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE + | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL; mWindow = SDL_CreateWindow(title.c_str(), x, y, (int) w, (int) h, windowFlags); - // Create the Engine after the window in case this happens to be a single-threaded platform. - // For single-threaded platforms, we need to ensure that Filament's OpenGL context is current, - // rather than the one created by SDL. - mFilamentApp->mEngine = Engine::create(config.backend); - // HACK: We don't use SDL's 2D rendering functionality, but by invoking it we cause // SDL to create a Metal backing layer, which allows us to run Vulkan apps via MoltenVK. #if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN) && defined(__APPLE__)