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:
committed by
Powei Feng
parent
61155644d5
commit
1801def1ee
@@ -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()`
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -340,6 +340,8 @@ public:
|
||||
|
||||
void destroy(utils::Entity e);
|
||||
|
||||
void setPaused(bool paused);
|
||||
|
||||
void flushAndWait();
|
||||
|
||||
// flush the current buffer
|
||||
|
||||
Reference in New Issue
Block a user