From 76b15e895a48e89c65ef1aeecaf5848f403bb0a6 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 7 Dec 2018 16:03:16 -0800 Subject: [PATCH] [MATERIALS BREAK] higher period clock THIS CHANGE BREAKS MATERIALS. This adds getUserTime() in shaders/materials, which returns the time in second since Renderer::resetUserTime() was called. Two values are provided, the time in second encoded as a float and the difference between that and the double value, which together allows to perform high precision time computation when needed. This change allows longer running animations in materials. Using only the float value, give millisecond resolution for more than 4h. --- .../src/main/cpp/Renderer.cpp | 12 ++++ .../com/google/android/filament/Renderer.java | 10 ++++ docs/Materials.md.html | 1 + filament/include/filament/Renderer.h | 57 +++++++++++++++++++ filament/src/Engine.cpp | 2 +- filament/src/PostProcessManager.cpp | 2 +- filament/src/Renderer.cpp | 23 +++++++- filament/src/View.cpp | 5 +- filament/src/details/Engine.h | 12 ++-- filament/src/details/Renderer.h | 16 ++++++ filament/src/details/View.h | 2 +- .../include/private/filament/UibGenerator.h | 2 + libs/filabridge/src/UibGenerator.cpp | 2 + shaders/src/common_getters.fs | 5 ++ 14 files changed, 139 insertions(+), 12 deletions(-) diff --git a/android/filament-android/src/main/cpp/Renderer.cpp b/android/filament-android/src/main/cpp/Renderer.cpp index dee60182f7..cf39d8cc70 100644 --- a/android/filament-android/src/main/cpp/Renderer.cpp +++ b/android/filament-android/src/main/cpp/Renderer.cpp @@ -96,3 +96,15 @@ Java_com_google_android_filament_Renderer_nReadPixels(JNIEnv *env, jclass, return 0; } + +extern "C" JNIEXPORT jdouble JNICALL +Java_com_google_android_filament_Renderer_nGetUserTime(JNIEnv*, jclass, jlong nativeRenderer) { + Renderer *renderer = (Renderer *) nativeRenderer; + return renderer->getUserTime(); +} + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_Renderer_nResetUserTime(JNIEnv*, jclass, jlong nativeRenderer) { + Renderer *renderer = (Renderer *) nativeRenderer; + renderer->resetUserTime(); +} diff --git a/android/filament-android/src/main/java/com/google/android/filament/Renderer.java b/android/filament-android/src/main/java/com/google/android/filament/Renderer.java index 682650ca34..0031590c36 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Renderer.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Renderer.java @@ -89,6 +89,14 @@ public class Renderer { } } + double getUserTime() { + return nGetUserTime(getNativeObject()); + } + + void resetUserTime() { + nResetUserTime(getNativeObject()); + } + long getNativeObject() { if (mNativeObject == 0) { throw new IllegalStateException("Calling method on destroyed Renderer"); @@ -112,4 +120,6 @@ public class Renderer { Buffer storage, int remaining, int left, int top, int type, int alignment, int stride, int format, Object handler, Runnable callback); + private static native double nGetUserTime(long nativeRenderer); + private static native void nResetUserTime(long nativeRenderer); } diff --git a/docs/Materials.md.html b/docs/Materials.md.html index 9ee59bae51..3a7cf52f72 100644 --- a/docs/Materials.md.html +++ b/docs/Materials.md.html @@ -1330,6 +1330,7 @@ type aliases: **getResolution()** | float4 | Resolution of the view in pixels: `width`, `height`, `1 / width`, `1 / height` **getWorldCameraPosition()** | float3 | Position of the camera/eye in world space **getTime()** | float | Current time in seconds, may be reset regularly to avoid precision loss +**getUserTime()** | float4 | Current time in seconds: `time`, `(double)time - time`, `0`, `0` **getExposure()** | float | Photometric exposure of the camera **getEV100()** | float | [Exposure value at ISO 100](https://en.wikipedia.org/wiki/Exposure_value) of the camera diff --git a/filament/include/filament/Renderer.h b/filament/include/filament/Renderer.h index ab7846507d..180de93f9c 100644 --- a/filament/include/filament/Renderer.h +++ b/filament/include/filament/Renderer.h @@ -288,6 +288,63 @@ public: * beginFrame() */ void endFrame(); + + /** + * Returns the time in second of the last call to beginFrame(). This value is constant for all + * views rendered during a frame. The epoch is set with resetUserTime(). + * + * In materials, this value can be queried using `vec4 getUserTime()`. The value returned + * is a highp vec4 encoded as follows: + * + * time.x = (float)Renderer.getUserTime(); + * time.y = Renderer.getUserTime() - time.x; + * + * It follows that the following invariants are true: + * + * (double)time.x + (double)time.y == Renderer.getUserTime() + * time.x == (float)Renderer.getUserTime() + * + * This encoding allows the shader code to perform high precision (i.e. double) time + * calculations when needed despite the lack of double precision in the shader, for e.g.: + * + * To compute (double)time * vertex in the material, use the following construct: + * + * vec3 result = time.x * vertex + time.y * vertex; + * + * + * Most of the time, high precision computations are not required, but be aware that the + * precision of time.x rapidly diminishes as time passes: + * + * time | precision + * --------+---------- + * 16.7s | us + * 4h39 | ms + * 77h | 1/60s + * + * + * In other words, it only possible to get microsecond accuracy for about 16s or millisecond + * accuracy for just under 5h. + * + * This problem can be mitigated by calling resetUserTime(), or using high precision time as + * described above. + * + * @return The time is seconds since resetUserTime() was last called. + * + * @see + * resetUserTime() + */ + double getUserTime() const; + + /** + * Sets the user time epoch to now, i.e. resets the user time to zero. + * + * Use this method used to keep the precision of time high in materials, in practice it should + * be called at least when the application is paused, e.g. Activity.onPause() in Android. + * + * @see + * getUserTime() + */ + void resetUserTime(); }; } // namespace filament diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp index 96fc60da23..40a9ff42f0 100644 --- a/filament/src/Engine.cpp +++ b/filament/src/Engine.cpp @@ -137,7 +137,7 @@ FEngine::FEngine(Backend backend, Platform* platform, void* sharedGLContext) : mPostProcessSib(PostProcessSib::getSib()), mCommandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE, CONFIG_COMMAND_BUFFERS_SIZE), mPerRenderPassAllocator("per-renderpass allocator", CONFIG_PER_RENDER_PASS_ARENA_SIZE), - mEpoch(std::chrono::steady_clock::now()), + mEngineEpoch(std::chrono::steady_clock::now()), mDriverBarrier(1) { SYSTRACE_ENABLE(); diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 54cbd043a5..e47044199a 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -62,7 +62,7 @@ void PostProcessManager::setSource(uint32_t viewportWidth, uint32_t viewportHeig SamplerBuffer sb(engine.getPostProcessSib()); sb.setSampler(PostProcessSib::COLOR_BUFFER, pos->texture, params); - auto duration = engine.getTime(); + auto duration = engine.getEngineTime(); float fraction = (duration.count() % 1000000000) / 1000000000.0f; UniformBuffer& ub = mPostProcessUb; diff --git a/filament/src/Renderer.cpp b/filament/src/Renderer.cpp index eb6189d60b..53b4eaf935 100644 --- a/filament/src/Renderer.cpp +++ b/filament/src/Renderer.cpp @@ -33,6 +33,8 @@ #include #include +#include + using namespace math; using namespace utils; @@ -55,6 +57,7 @@ FRenderer::FRenderer(FEngine& engine) : void FRenderer::init() noexcept { DriverApi& driver = mEngine.getDriverApi(); + mUserEpoch = mEngine.getEngineEpoch(); mRenderTarget = driver.createDefaultRenderTarget(); mIsRGB16FSupported = driver.isRenderTargetFormatSupported(driver::TextureFormat::RGB16F); mIsRGB8Supported = driver.isRenderTargetFormatSupported(driver::TextureFormat::RGB8); @@ -95,6 +98,10 @@ void FRenderer::terminate(FEngine& engine) { } } +void FRenderer::resetUserTime() { + mUserEpoch = std::chrono::steady_clock::now(); +} + driver::TextureFormat FRenderer::getHdrFormat(const View& view) const noexcept { const bool translucent = mSwapChain->isTransparent(); if (translucent) return driver::TextureFormat::RGBA16F; @@ -170,7 +177,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { return; } - view.prepare(engine, driver, arena, svp); + view.prepare(engine, driver, arena, svp, getShaderUserTime()); // TODO: froxelization could actually start now, instead of in ColorPass::renderColorPass() /* @@ -345,6 +352,12 @@ bool FRenderer::beginFrame(FSwapChain* swapChain) { return false; } + // latch the frame time + std::chrono::duration time{ getUserTime() }; + float h = (float)time.count(); + float l = float(time.count() - h); + mShaderUserTime = { h, l, 0, 0 }; + // ask the engine to do what it needs to (e.g. updates light buffer, materials...) engine.prepare(); @@ -475,4 +488,12 @@ void Renderer::endFrame() { upcast(this)->endFrame(); } +double Renderer::getUserTime() const { + return upcast(this)->getUserTime().count(); +} + +void Renderer::resetUserTime() { + upcast(this)->resetUserTime(); +} + } // namespace filament diff --git a/filament/src/View.cpp b/filament/src/View.cpp index 6c2992ac2e..d5d54fbdb1 100644 --- a/filament/src/View.cpp +++ b/filament/src/View.cpp @@ -397,7 +397,7 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc } void FView::prepare(FEngine& engine, driver::DriverApi& driver, ArenaScope& arena, - Viewport const& viewport) noexcept { + Viewport const& viewport, math::float4 const& userTime) noexcept { JobSystem& js = engine.getJobSystem(); /* @@ -537,8 +537,9 @@ void FView::prepare(FEngine& engine, driver::DriverApi& driver, ArenaScope& aren * Update driver state */ - float fraction = (engine.getTime().count() % 1000000000) / 1000000000.0f; + float fraction = (engine.getEngineTime().count() % 1000000000) / 1000000000.0f; getUb().setUniform(offsetof(PerViewUib, time), fraction); + getUb().setUniform(offsetof(PerViewUib, userTime), userTime); // upload the renderables's dirty UBOs engine.getRenderableManager().prepare(driver, diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index df162e75af..90bec5d43f 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -203,15 +203,15 @@ public: return mBackend; } - duration getTime() const noexcept { - return clock::now() - getEpoch(); - } - void* streamAlloc(size_t size, size_t alignment) noexcept; utils::JobSystem& getJobSystem() noexcept { return mJobSystem; } - Epoch getEpoch() const { return mEpoch; } + + Epoch getEngineEpoch() const { return mEngineEpoch; } + duration getEngineTime() const noexcept { + return clock::now() - getEngineEpoch(); + } void shutdown(); @@ -347,7 +347,7 @@ private: utils::JobSystem mJobSystem; - Epoch mEpoch; + Epoch mEngineEpoch; mutable FMaterial const* mDefaultMaterial = nullptr; mutable FMaterial const* mSkyboxMaterials[2] = { nullptr, nullptr }; diff --git a/filament/src/details/Renderer.h b/filament/src/details/Renderer.h index 49d1c0394c..3abd994a22 100644 --- a/filament/src/details/Renderer.h +++ b/filament/src/details/Renderer.h @@ -59,6 +59,8 @@ public: FEngine& getEngine() const noexcept { return mEngine; } + math::float4 getShaderUserTime() const { return mShaderUserTime; } + // do all the work here! void render(FView const* view); void renderJob(ArenaScope& arena, FView& view); @@ -69,6 +71,8 @@ public: bool beginFrame(FSwapChain* swapChain); void endFrame(); + void resetUserTime(); + void readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height, driver::PixelBufferDescriptor&& buffer); @@ -76,6 +80,7 @@ public: void terminate(FEngine& engine); private: + friend class Renderer; using Command = RenderPass::Command; // this class is defined in RenderPass.cpp @@ -123,6 +128,15 @@ private: driver::TextureFormat getHdrFormat(const View& view) const noexcept; driver::TextureFormat getLdrFormat() const noexcept; + using clock = std::chrono::steady_clock; + using Epoch = clock::time_point; + using duration = clock::duration; + + Epoch getUserEpoch() const { return mUserEpoch; } + duration getUserTime() const noexcept { + return clock::now() - getUserEpoch(); + } + // keep a reference to our engine FEngine& mEngine; FrameSkipper mFrameSkipper; @@ -133,6 +147,8 @@ private: FrameInfoManager mFrameInfoManager; bool mIsRGB16FSupported : 1; bool mIsRGB8Supported : 1; + Epoch mUserEpoch; + math::float4 mShaderUserTime; // per-frame arena for this Renderer LinearAllocatorArena& mPerRenderPassArena; diff --git a/filament/src/details/View.h b/filament/src/details/View.h index ece01403f4..4f80444235 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -62,7 +62,7 @@ public: void terminate(FEngine& engine); void prepare(FEngine& engine, driver::DriverApi& driver, ArenaScope& arena, - Viewport const& viewport) noexcept; + Viewport const& viewport, math::float4 const& userTime) noexcept; void setScene(FScene* scene) { mScene = scene; } FScene const* getScene() const noexcept { return mScene; } diff --git a/libs/filabridge/include/private/filament/UibGenerator.h b/libs/filabridge/include/private/filament/UibGenerator.h index 7492b0c643..ed829f6869 100644 --- a/libs/filabridge/include/private/filament/UibGenerator.h +++ b/libs/filabridge/include/private/filament/UibGenerator.h @@ -77,6 +77,8 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) float ev100; alignas(16) math::float4 iblSH[9]; // actually float3 entries (std140 requires float4 alignment) + + math::float4 userTime; // time(s), (double)time - (float)time, 0, 0 }; diff --git a/libs/filabridge/src/UibGenerator.cpp b/libs/filabridge/src/UibGenerator.cpp index d8275e7c86..0df5069894 100644 --- a/libs/filabridge/src/UibGenerator.cpp +++ b/libs/filabridge/src/UibGenerator.cpp @@ -70,6 +70,8 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept { .add("ev100", 1, UniformInterfaceBlock::Type::FLOAT) // ibl .add("iblSH", 9, UniformInterfaceBlock::Type::FLOAT3) + // user time + .add("userTime", 1, UniformInterfaceBlock::Type::FLOAT4) .build(); return uib; } diff --git a/shaders/src/common_getters.fs b/shaders/src/common_getters.fs index f115df4204..428a36544e 100644 --- a/shaders/src/common_getters.fs +++ b/shaders/src/common_getters.fs @@ -42,6 +42,11 @@ float getTime() { return frameUniforms.time; } +/** @public-api */ +HIGHP vec4 getUserTime() { + return frameUniforms.userTime; +} + /** @public-api */ float getExposure() { return frameUniforms.exposure;