Allow rendering thread to pause

This PR adds a new `pause()` option to the `Engine` `Builder` and a new function
`setPaused()` to the `Engine`. While paused, the rendering thread will pause
indefinitely for commands as if none are available. As soon as the rendering
thread is unpaused, the commands are immediately executed.
This commit is contained in:
Eliza Velasquez
2024-03-11 14:08:42 -07:00
committed by Powei Feng
parent 61155644d5
commit 1801def1ee
11 changed files with 89 additions and 7 deletions

View File

@@ -7,3 +7,5 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- engine: Add experimental APIs `Engine::builder::paused()` and `Engine::setPaused()`

View File

@@ -391,6 +391,13 @@ Java_com_google_android_filament_Engine_nFlush(JNIEnv*, jclass,
engine->flush();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nSetPaused(JNIEnv*, jclass,
jlong nativeEngine, jboolean paused) {
Engine* engine = (Engine*) nativeEngine;
engine->setPaused(paused);
}
// Managers...
extern "C" JNIEXPORT jlong JNICALL
@@ -520,6 +527,12 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
builder->sharedContext((void*) sharedContext);
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderPaused(
JNIEnv*, jclass, jlong nativeBuilder, jboolean paused) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
builder->paused((bool) paused);
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;

View File

@@ -240,6 +240,18 @@ public class Engine {
return this;
}
/**
* Sets the initial paused state of the rendering thread.
*
* @param paused Whether to start the rendering thread paused.
* @return A reference to this Builder for chaining calls.
* @warning Experimental.
*/
public Builder paused(boolean paused) {
nSetBuilderPaused(mNativeBuilder, paused);
return this;
}
/**
* Creates an instance of Engine
*
@@ -1195,6 +1207,13 @@ public class Engine {
nFlush(getNativeObject());
}
/**
* Pause or resume the rendering thread.
* @warning Experimental.
*/
public void setPaused(boolean paused) {
nSetPaused(getNativeObject(), paused);
}
@UsedByReflection("TextureHelper.java")
public long getNativeObject() {
@@ -1269,6 +1288,7 @@ public class Engine {
private static native void nDestroyEntity(long nativeEngine, int entity);
private static native void nFlushAndWait(long nativeEngine);
private static native void nFlush(long nativeEngine);
private static native void nSetPaused(long nativeEngine, boolean paused);
private static native long nGetTransformManager(long nativeEngine);
private static native long nGetLightManager(long nativeEngine);
private static native long nGetRenderableManager(long nativeEngine);
@@ -1293,5 +1313,6 @@ public class Engine {
boolean disableHandleUseAfterFreeCheck);
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
private static native void nSetBuilderPaused(long nativeBuilder, boolean paused);
private static native long nBuilderBuild(long nativeBuilder);
}

View File

@@ -50,12 +50,13 @@ class CommandBufferQueue {
size_t mFreeSpace = 0;
size_t mHighWatermark = 0;
uint32_t mExitRequested = 0;
bool mPaused = false;
static constexpr uint32_t EXIT_REQUESTED = 0x31415926;
public:
// requiredSize: guaranteed available space after flush()
CommandBufferQueue(size_t requiredSize, size_t bufferSize);
CommandBufferQueue(size_t requiredSize, size_t bufferSize, bool paused);
~CommandBufferQueue();
CircularBuffer& getCircularBuffer() noexcept { return mCircularBuffer; }
@@ -80,6 +81,9 @@ public:
// returns from waitForCommands() immediately.
void requestExit();
// suspend or unsuspend the queue.
void setPaused(bool paused);
bool isExitRequested() const;
};

View File

@@ -39,10 +39,11 @@ using namespace utils;
namespace filament::backend {
CommandBufferQueue::CommandBufferQueue(size_t requiredSize, size_t bufferSize)
CommandBufferQueue::CommandBufferQueue(size_t requiredSize, size_t bufferSize, bool paused)
: mRequiredSize((requiredSize + (CircularBuffer::getBlockSize() - 1u)) & ~(CircularBuffer::getBlockSize() -1u)),
mCircularBuffer(bufferSize),
mFreeSpace(mCircularBuffer.size()) {
mFreeSpace(mCircularBuffer.size()),
mPaused(paused) {
assert_invariant(mCircularBuffer.size() > requiredSize);
}
@@ -56,6 +57,15 @@ void CommandBufferQueue::requestExit() {
mCondition.notify_one();
}
void CommandBufferQueue::setPaused(bool paused) {
if (paused) {
mPaused = true;
} else {
mPaused = false;
mCondition.notify_one();
}
}
bool CommandBufferQueue::isExitRequested() const {
std::lock_guard<utils::Mutex> const lock(mLock);
ASSERT_PRECONDITION( mExitRequested == 0 || mExitRequested == EXIT_REQUESTED,
@@ -127,7 +137,7 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
return std::move(mCommandBuffersToExecute);
}
std::unique_lock<utils::Mutex> lock(mLock);
while (mCommandBuffersToExecute.empty() && !mExitRequested) {
while ((mCommandBuffersToExecute.empty() || mPaused) && !mExitRequested) {
mCondition.wait(lock);
}

View File

@@ -51,7 +51,7 @@ void BackendTest::init(Backend backend, bool isMobilePlatform) {
}
BackendTest::BackendTest() : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE,
CONFIG_COMMAND_BUFFERS_SIZE) {
CONFIG_COMMAND_BUFFERS_SIZE, /*mPaused=*/false) {
initializeDriver();
}

View File

@@ -46,7 +46,8 @@ void ComputeTest::init(Backend backend) {
}
ComputeTest::ComputeTest()
: commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE, CONFIG_COMMAND_BUFFERS_SIZE) {
: commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE, CONFIG_COMMAND_BUFFERS_SIZE,
/*paused=*/false) {
}
ComputeTest::~ComputeTest() = default;

View File

@@ -407,6 +407,13 @@ public:
*/
Builder& featureLevel(FeatureLevel featureLevel) noexcept;
/**
* @param paused Whether to start the rendering thread paused.
* @return A reference to this Builder for chaining calls.
* @warning Experimental.
*/
Builder& paused(bool paused) noexcept;
#if UTILS_HAS_THREADING
/**
* Creates the filament Engine asynchronously.
@@ -832,6 +839,12 @@ public:
*/
void flush();
/**
* Pause or resume rendering thread.
* @warning Experimental.
*/
void setPaused(bool paused);
/**
* Drains the user callback message queue and immediately execute all pending callbacks.
*

View File

@@ -295,6 +295,11 @@ utils::JobSystem& Engine::getJobSystem() noexcept {
return downcast(this)->getJobSystem();
}
void Engine::setPaused(bool paused) {
ASSERT_PRECONDITION(UTILS_HAS_THREADING, "Pause is meant for multi-threaded platforms.");
downcast(this)->setPaused(paused);
}
DebugRegistry& Engine::getDebugRegistry() noexcept {
return downcast(this)->getDebugRegistry();
}

View File

@@ -69,6 +69,7 @@ struct Engine::BuilderDetails {
Engine::Config mConfig;
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
void* mSharedContext = nullptr;
bool mPaused = false;
static Config validateConfig(const Config* pConfig) noexcept;
};
@@ -201,7 +202,8 @@ FEngine::FEngine(Engine::Builder const& builder) :
mCameraManager(*this),
mCommandBufferQueue(
builder->mConfig.minCommandBufferSizeMB * MiB,
builder->mConfig.commandBufferSizeMB * MiB),
builder->mConfig.commandBufferSizeMB * MiB,
builder->mPaused),
mPerRenderPassArena(
"FEngine::mPerRenderPassAllocator",
builder->mConfig.perRenderPassArenaSizeMB * MiB),
@@ -1200,6 +1202,10 @@ void FEngine::destroy(FEngine* engine) {
}
}
void FEngine::setPaused(bool paused) {
mCommandBufferQueue.setPaused(paused);
}
Engine::FeatureLevel FEngine::getSupportedFeatureLevel() const noexcept {
FEngine::DriverApi& driver = const_cast<FEngine*>(this)->getDriverApi();
return driver.getFeatureLevel();
@@ -1253,6 +1259,11 @@ Engine::Builder& Engine::Builder::sharedContext(void* sharedContext) noexcept {
return *this;
}
Engine::Builder& Engine::Builder::paused(bool paused) noexcept {
mImpl->mPaused = paused;
return *this;
}
#if UTILS_HAS_THREADING
void Engine::Builder::build(Invocable<void(void*)>&& callback) const {

View File

@@ -340,6 +340,8 @@ public:
void destroy(utils::Entity e);
void setPaused(bool paused);
void flushAndWait();
// flush the current buffer