diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp index 4b3b33e530..f571422255 100644 --- a/android/filament-android/src/main/cpp/Engine.cpp +++ b/android/filament-android/src/main/cpp/Engine.cpp @@ -571,3 +571,9 @@ Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv*, jclass, jlong nat Engine::Builder* builder = (Engine::Builder*) nativeBuilder; return (jlong) builder->build(); } + +extern "C" +JNIEXPORT jlong JNICALL +Java_com_google_android_filament_Engine_getSteadyClockTimeNano(JNIEnv *env, jclass clazz) { + return (jlong)Engine::getSteadyClockTimeNano(); +} diff --git a/android/filament-android/src/main/cpp/Material.cpp b/android/filament-android/src/main/cpp/Material.cpp index 7ef3b0b5be..e923d210b0 100644 --- a/android/filament-android/src/main/cpp/Material.cpp +++ b/android/filament-android/src/main/cpp/Material.cpp @@ -25,12 +25,17 @@ using namespace filament; extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass, - jlong nativeEngine, jobject buffer_, jint size) { + jlong nativeEngine, jobject buffer_, jint size, jint shBandCount) { Engine* engine = (Engine*) nativeEngine; AutoBuffer buffer(env, buffer_, size); - Material* material = Material::Builder() + auto builder = Material::Builder(); + if (shBandCount) { + builder.sphericalHarmonicsBandCount(shBandCount); + } + Material* material = builder .package(buffer.getData(), buffer.getSize()) .build(*engine); + return (jlong) material; } diff --git a/android/filament-android/src/main/cpp/Renderer.cpp b/android/filament-android/src/main/cpp/Renderer.cpp index c48b616f56..1dc252d356 100644 --- a/android/filament-android/src/main/cpp/Renderer.cpp +++ b/android/filament-android/src/main/cpp/Renderer.cpp @@ -187,3 +187,10 @@ Java_com_google_android_filament_Renderer_nSetPresentationTime(JNIEnv *, jclass Renderer *renderer = (Renderer *) nativeRenderer; renderer->setPresentationTime(monotonicClockNanos); } + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_Renderer_nSetVsyncTime(JNIEnv *, jclass, + jlong nativeRenderer, jlong steadyClockTimeNano) { + Renderer *renderer = (Renderer *) nativeRenderer; + renderer->setVsyncTime(steadyClockTimeNano); +} diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java index 5a28d1eae8..6c4c5119e3 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java @@ -1292,7 +1292,6 @@ public class Engine { /** * Switch the command queue to unprotected mode. Protected mode can be activated via * Renderer::beginFrame() using a protected SwapChain. - * @see Renderer * @see SwapChain */ @@ -1300,6 +1299,13 @@ public class Engine { nUnprotected(getNativeObject()); } + /** + * Get the current time. This is a convenience function that simply returns the + * time in nanosecond since epoch of std::chrono::steady_clock. + * @return current time in nanosecond since epoch of std::chrono::steady_clock. + * @see Renderer#beginFrame + */ + public static native long getSteadyClockTimeNano(); @UsedByReflection("TextureHelper.java") public long getNativeObject() { diff --git a/android/filament-android/src/main/java/com/google/android/filament/Material.java b/android/filament-android/src/main/java/com/google/android/filament/Material.java index 80b143ee80..cd71bbae73 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Material.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Material.java @@ -346,6 +346,7 @@ public class Material { public static class Builder { private Buffer mBuffer; private int mSize; + private int mShBandCount = 0; /** * Specifies the material data. The material data is a binary blob produced by @@ -361,6 +362,22 @@ public class Material { return this; } + /** + * Sets the quality of the indirect lights computations. This is only taken into account + * if this material is lit and in the surface domain. This setting will affect the + * IndirectLight computation if one is specified on the Scene and Spherical Harmonics + * are used for the irradiance. + * + * @param shBandCount Number of spherical harmonic bands. Must be 1, 2 or 3 (default). + * @return Reference to this Builder for chaining calls. + * @see IndirectLight + */ + @NonNull + public Builder sphericalHarmonicsBandCount(@IntRange(from = 0) int shBandCount) { + mShBandCount = shBandCount; + return this; + } + /** * Creates and returns the Material object. * @@ -372,7 +389,8 @@ public class Material { */ @NonNull public Material build(@NonNull Engine engine) { - long nativeMaterial = nBuilderBuild(engine.getNativeObject(), mBuffer, mSize); + long nativeMaterial = nBuilderBuild(engine.getNativeObject(), + mBuffer, mSize, mShBandCount); if (nativeMaterial == 0) throw new IllegalStateException("Couldn't create Material"); return new Material(nativeMaterial); } @@ -1023,7 +1041,7 @@ public class Material { mNativeObject = 0; } - private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size); + private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size, int shBandCount); private static native long nCreateInstance(long nativeMaterial); private static native long nCreateInstanceWithName(long nativeMaterial, @NonNull String name); private static native long nGetDefaultInstance(long nativeMaterial); 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 ab70e9ed38..4fb447db2c 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 @@ -284,6 +284,19 @@ public class Renderer { nSetPresentationTime(getNativeObject(), monotonicClockNanos); } + /** + * The use of this method is optional. It sets the VSYNC time expressed as the duration in + * nanosecond since epoch of std::chrono::steady_clock. + * If called, passing 0 to frameTimeNanos in Renderer.BeginFrame will use this + * time instead. + * @param steadyClockTimeNano duration in nanosecond since epoch of std::chrono::steady_clock + * @see Engine#getSteadyClockTimeNano + * @see Renderer#beginFrame + */ + public void setVsyncTime(long steadyClockTimeNano) { + nSetVsyncTime(getNativeObject(), steadyClockTimeNano); + } + /** * Sets up a frame for this Renderer. *

beginFrame manages frame pacing, and returns whether or not a frame should be @@ -702,6 +715,7 @@ public class Renderer { } private static native void nSetPresentationTime(long nativeObject, long monotonicClockNanos); + private static native void nSetVsyncTime(long nativeObject, long steadyClockTimeNano); private static native boolean nBeginFrame(long nativeRenderer, long nativeSwapChain, long frameTimeNanos); private static native void nEndFrame(long nativeRenderer); private static native void nRender(long nativeRenderer, long nativeView); diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 2aed82736f..1c44fe9ab9 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -1021,6 +1021,21 @@ public: void resetBackendState() noexcept; #endif + /** + * Get the current time. This is a convenience function that simply returns the + * time in nanosecond since epoch of std::chrono::steady_clock. + * A possible implementation is: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * return std::chrono::steady_clock::now().time_since_epoch().count(); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @return current time in nanosecond since epoch of std::chrono::steady_clock. + * @see Renderer::beginFrame() + */ + static uint64_t getSteadyClockTimeNano() noexcept; + + DebugRegistry& getDebugRegistry() noexcept; protected: diff --git a/filament/include/filament/IndirectLight.h b/filament/include/filament/IndirectLight.h index c230dac8fe..c4cc62ecd7 100644 --- a/filament/include/filament/IndirectLight.h +++ b/filament/include/filament/IndirectLight.h @@ -158,6 +158,8 @@ public: * * @return This Builder, for chaining calls. * + * @see Material::Builder::sphericalHarmonicsBandCount() + * * @note * Because the coefficients are pre-scaled, `sh[0]` is the environment's * average irradiance. diff --git a/filament/include/filament/Material.h b/filament/include/filament/Material.h index b4b9bbed3e..11cbaef30d 100644 --- a/filament/include/filament/Material.h +++ b/filament/include/filament/Material.h @@ -140,6 +140,18 @@ public: return constant(name, strlen(name), value); } + /** + * Sets the quality of the indirect lights computations. This is only taken into account + * if this material is lit and in the surface domain. This setting will affect the + * IndirectLight computation if one is specified on the Scene and Spherical Harmonics + * are used for the irradiance. + * + * @param shBandCount Number of spherical harmonic bands. Must be 1, 2 or 3 (default). + * @return Reference to this Builder for chaining calls. + * @see IndirectLight + */ + Builder& sphericalHarmonicsBandCount(size_t shBandCount) noexcept; + /** * Creates the Material object and returns a pointer to it. * diff --git a/filament/include/filament/Renderer.h b/filament/include/filament/Renderer.h index fdc291b1fd..83b6281604 100644 --- a/filament/include/filament/Renderer.h +++ b/filament/include/filament/Renderer.h @@ -227,6 +227,17 @@ public: static constexpr CopyFrameFlag CLEAR = 0x4; + /** + * The use of this method is optional. It sets the VSYNC time expressed as the duration in + * nanosecond since epoch of std::chrono::steady_clock. + * If called, passing 0 to vsyncSteadyClockTimeNano in Renderer::BeginFrame will use this + * time instead. + * @param steadyClockTimeNano duration in nanosecond since epoch of std::chrono::steady_clock + * @see Engine::getSteadyClockTimeNano() + * @see Renderer::BeginFrame() + */ + void setVsyncTime(uint64_t steadyClockTimeNano) noexcept; + /** * Set-up a frame for this Renderer. * diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp index ad110dbc92..477a0871c8 100644 --- a/filament/src/Engine.cpp +++ b/filament/src/Engine.cpp @@ -39,6 +39,8 @@ #include #include +#include + #include #include @@ -361,7 +363,7 @@ const Engine::Config& Engine::getConfig() const noexcept { return downcast(this)->getConfig(); } -bool Engine::isStereoSupported(StereoscopicType stereoscopicType) const noexcept { +bool Engine::isStereoSupported(StereoscopicType) const noexcept { return downcast(this)->isStereoSupported(); } @@ -369,6 +371,10 @@ size_t Engine::getMaxStereoscopicEyes() noexcept { return FEngine::getMaxStereoscopicEyes(); } +uint64_t Engine::getSteadyClockTimeNano() noexcept { + return std::chrono::steady_clock::now().time_since_epoch().count(); +} + #if defined(__EMSCRIPTEN__) void Engine::resetBackendState() noexcept { downcast(this)->resetBackendState(); diff --git a/filament/src/Renderer.cpp b/filament/src/Renderer.cpp index 85d58f9ec1..00837319b2 100644 --- a/filament/src/Renderer.cpp +++ b/filament/src/Renderer.cpp @@ -89,4 +89,8 @@ void Renderer::renderStandaloneView(View const* view) { downcast(this)->renderStandaloneView(downcast(view)); } +void Renderer::setVsyncTime(uint64_t steadyClockTimeNano) noexcept { + downcast(this)->setVsyncTime(steadyClockTimeNano); +} + } // namespace filament diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 268af1ded0..0ce9189284 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -122,6 +122,7 @@ struct Material::BuilderDetails { const void* mPayload = nullptr; size_t mSize = 0; bool mDefaultMaterial = false; + int32_t mShBandsCount = 3; std::unordered_map< utils::CString, std::variant, @@ -146,6 +147,11 @@ Material::Builder& Material::Builder::package(const void* payload, size_t size) return *this; } +Material::Builder& Material::Builder::sphericalHarmonicsBandCount(size_t shBandCount) noexcept { + mImpl->mShBandsCount = math::clamp(shBandCount, size_t(1), size_t(3)); + return *this; +} + template Material::Builder& Material::Builder::constant(const char* name, size_t nameLength, T value) { FILAMENT_CHECK_PRECONDITION(name != nullptr) << "name cannot be null"; @@ -889,6 +895,8 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Material::Builde mSpecializationConstants.push_back({ +ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT, (int)engine.getConfig().stereoscopicEyeCount }); + mSpecializationConstants.push_back({ + +ReservedSpecializationConstants::CONFIG_SH_BANDS_COUNT, builder->mShBandsCount }); if (UTILS_UNLIKELY(parser->getShaderLanguage() == ShaderLanguage::ESSL1)) { // The actual value of this spec-constant is set in the OpenGLDriver backend. mSpecializationConstants.push_back({ diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp index 125f28d5af..a2afa1dc45 100644 --- a/filament/src/details/Renderer.cpp +++ b/filament/src/details/Renderer.cpp @@ -232,6 +232,10 @@ void FRenderer::setPresentationTime(int64_t monotonic_clock_ns) { driver.setPresentationTime(monotonic_clock_ns); } +void FRenderer::setVsyncTime(uint64_t steadyClockTimeNano) noexcept { + mVsyncSteadyClockTimeNano = steadyClockTimeNano; +} + bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano) { assert_invariant(swapChain); @@ -252,6 +256,11 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN } #endif + if (!vsyncSteadyClockTimeNano) { + vsyncSteadyClockTimeNano = mVsyncSteadyClockTimeNano; + mVsyncSteadyClockTimeNano = 0; + } + // get the timestamp as soon as possible using namespace std::chrono; const steady_clock::time_point now{ steady_clock::now() }; diff --git a/filament/src/details/Renderer.h b/filament/src/details/Renderer.h index 056d559977..263f899886 100644 --- a/filament/src/details/Renderer.h +++ b/filament/src/details/Renderer.h @@ -29,9 +29,6 @@ #include "backend/DriverApiForward.h" -#include -#include - #include #include @@ -41,8 +38,18 @@ #include #include +#include + #include +#include +#include +#include +#include + +#include +#include + namespace filament { namespace backend { @@ -78,6 +85,8 @@ public: void setPresentationTime(int64_t monotonic_clock_ns); + void setVsyncTime(uint64_t steadyClockTimeNano) noexcept; + // start a frame bool beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano); @@ -187,6 +196,7 @@ private: backend::TargetBufferFlags mClearFlags{}; tsl::robin_set mPreviousRenderTargets; std::function mBeginFrameInternal; + uint64_t mVsyncSteadyClockTimeNano = 0; }; FILAMENT_DOWNCAST(Renderer) diff --git a/libs/filabridge/include/private/filament/EngineEnums.h b/libs/filabridge/include/private/filament/EngineEnums.h index 7fa90c22d8..32d1d87a3d 100644 --- a/libs/filabridge/include/private/filament/EngineEnums.h +++ b/libs/filabridge/include/private/filament/EngineEnums.h @@ -81,6 +81,7 @@ enum class ReservedSpecializationConstants : uint8_t { CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP = 6, CONFIG_DEBUG_FROXEL_VISUALIZATION = 7, CONFIG_STEREO_EYE_COUNT = 8, // don't change (hardcoded in ShaderCompilerService.cpp) + CONFIG_SH_BANDS_COUNT = 9 }; enum class PushConstantIds : uint8_t { diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index e8a7b3bfcd..182e758667 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -334,6 +334,9 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade generateSpecializationConstant(out, "CONFIG_STEREO_EYE_COUNT", +ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT, material.stereoscopicEyeCount); + generateSpecializationConstant(out, "CONFIG_SH_BANDS_COUNT", + +ReservedSpecializationConstants::CONFIG_SH_BANDS_COUNT, 3); + // CONFIG_MAX_STEREOSCOPIC_EYES is used to size arrays and on Adreno GPUs + vulkan, this has to // be explicitly, statically defined (as in #define). Otherwise (using const int for // example), we'd run into a GPU crash. diff --git a/shaders/src/light_indirect.fs b/shaders/src/light_indirect.fs index 0d12391078..3777eccce0 100644 --- a/shaders/src/light_indirect.fs +++ b/shaders/src/light_indirect.fs @@ -2,9 +2,6 @@ // Image based lighting configuration //------------------------------------------------------------------------------ -// Number of spherical harmonics bands (1, 2 or 3) -#define SPHERICAL_HARMONICS_BANDS 3 - // IBL integration algorithm #define IBL_INTEGRATION_PREFILTERED_CUBEMAP 0 #define IBL_INTEGRATION_IMPORTANCE_SAMPLING 1 @@ -44,21 +41,25 @@ vec3 prefilteredDFG(float perceptualRoughness, float NoV) { //------------------------------------------------------------------------------ vec3 Irradiance_SphericalHarmonics(const vec3 n) { - return max( - frameUniforms.iblSH[0] -#if SPHERICAL_HARMONICS_BANDS >= 2 - + frameUniforms.iblSH[1] * (n.y) - + frameUniforms.iblSH[2] * (n.z) - + frameUniforms.iblSH[3] * (n.x) -#endif -#if SPHERICAL_HARMONICS_BANDS >= 3 - + frameUniforms.iblSH[4] * (n.y * n.x) - + frameUniforms.iblSH[5] * (n.y * n.z) - + frameUniforms.iblSH[6] * (3.0 * n.z * n.z - 1.0) - + frameUniforms.iblSH[7] * (n.z * n.x) - + frameUniforms.iblSH[8] * (n.x * n.x - n.y * n.y) -#endif - , 0.0); + vec3 sphericalHarmonics = frameUniforms.iblSH[0]; + + if (CONFIG_SH_BANDS_COUNT >= 2) { + sphericalHarmonics += + frameUniforms.iblSH[1] * (n.y) + + frameUniforms.iblSH[2] * (n.z) + + frameUniforms.iblSH[3] * (n.x); + } + + if (CONFIG_SH_BANDS_COUNT >= 3) { + sphericalHarmonics += + frameUniforms.iblSH[4] * (n.y * n.x) + + frameUniforms.iblSH[5] * (n.y * n.z) + + frameUniforms.iblSH[6] * (3.0 * n.z * n.z - 1.0) + + frameUniforms.iblSH[7] * (n.z * n.x) + + frameUniforms.iblSH[8] * (n.x * n.x - n.y * n.y); + } + + return max(sphericalHarmonics, 0.0); } vec3 Irradiance_RoughnessOne(const vec3 n) { diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index 735305a563..e1bc5c3755 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -388,6 +388,8 @@ class_("Engine") return Engine::create(); }, allow_raw_pointers()) + .class_function("getSteadyClockTimeNano", &Engine::getSteadyClockTimeNano) + .function("unprotected", &Engine::unprotected) .function("enableAccurateTranslations", &Engine::enableAccurateTranslations) @@ -623,6 +625,8 @@ class_("Renderer") }), allow_raw_pointers()) .function("_setClearOptions", &Renderer::setClearOptions, allow_raw_pointers()) .function("getClearOptions", &Renderer::getClearOptions) + .function("setPresentationTime", &Renderer::setPresentationTime) + .function("setVsyncTime", &Renderer::setVsyncTime) .function("beginFrame", EMBIND_LAMBDA(bool, (Renderer* self, SwapChain* swapChain), { return self->beginFrame(swapChain); }), allow_raw_pointers())