From e04c6d406f35146ab35b65c30fcb292f43fc8a62 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 11 Jun 2024 17:05:22 -0700 Subject: [PATCH 01/11] inject the missing packing/unpacking function in ESSL 3.0 FIXES=[343784713] --- .../src/opengl/ShaderCompilerService.cpp | 100 ++++++++++++++++-- .../src/opengl/ShaderCompilerService.h | 2 +- 2 files changed, 91 insertions(+), 11 deletions(-) diff --git a/filament/backend/src/opengl/ShaderCompilerService.cpp b/filament/backend/src/opengl/ShaderCompilerService.cpp index dd64a1b794..6935868b14 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.cpp +++ b/filament/backend/src/opengl/ShaderCompilerService.cpp @@ -572,16 +572,23 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context, // split shader source, so we can insert the specialization constants and the packing // functions - auto const [prolog, body] = splitShaderSource({ shader_src, shader_len }); + auto [version, prolog, body] = splitShaderSource({ shader_src, shader_len }); - const std::array sources = { + // enable ESSL 3.10 if available + if (context.isAtLeastGLES<3, 1>()) { + version = "#version 310 es\n"; + } + + const std::array sources = { + version.data(), prolog.data(), specializationConstantString.c_str(), packingFunctions.data(), body.data() }; - const std::array lengths = { + const std::array lengths = { + (GLint)version.length(), (GLint)prolog.length(), (GLint)specializationConstantString.length(), (GLint)packingFunctions.length(), @@ -661,6 +668,7 @@ void ShaderCompilerService::process_OVR_multiview2(OpenGLContext& context, // Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and // macOS doesn't support GL_ARB_shading_language_packing +// Also GLES3.0 didn't have the full set of packing/unpacking functions std::string_view ShaderCompilerService::process_ARB_shading_language_packing(OpenGLContext& context) noexcept { using namespace std::literals; #ifdef BACKEND_OPENGL_VERSION_GL @@ -700,21 +708,92 @@ highp uint packHalf2x16(vec2 v) { highp uint y = fp32tou16(v.y); return (y << 16u) | x; } +highp uint packUnorm4x8(mediump vec4 v) { + v = round(clamp(v, 0.0, 1.0) * 255.0); + highp uint a = uint(v.x); + highp uint b = uint(v.y) << 8; + highp uint c = uint(v.z) << 16; + highp uint d = uint(v.w) << 24; + return (a|b|c|d); +} +highp uint packSnorm4x8(mediump vec4 v) { + v = round(clamp(v, -1.0, 1.0) * 127.0); + highp uint a = uint((int(v.x) & 0xff)); + highp uint b = uint((int(v.y) & 0xff)) << 8; + highp uint c = uint((int(v.z) & 0xff)) << 16; + highp uint d = uint((int(v.w) & 0xff)) << 24; + return (a|b|c|d); +} +mediump vec4 unpackUnorm4x8(highp uint v) { + return vec4(float((v & 0x000000ffu) ), + float((v & 0x0000ff00u) >> 8), + float((v & 0x00ff0000u) >> 16), + float((v & 0xff000000u) >> 24)) / 255.0; +} +mediump vec4 unpackSnorm4x8(highp uint v) { + int a = int(((v ) & 0xffu) << 24u) >> 24 ; + int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ; + int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ; + int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ; + return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0); +} )"sv; } #endif // BACKEND_OPENGL_VERSION_GL + +#ifdef BACKEND_OPENGL_VERSION_GLES + if (!context.isES2() && !context.isAtLeastGLES<3, 1>()) { + return R"( + +highp uint packUnorm4x8(mediump vec4 v) { + v = round(clamp(v, 0.0, 1.0) * 255.0); + highp uint a = uint(v.x); + highp uint b = uint(v.y) << 8; + highp uint c = uint(v.z) << 16; + highp uint d = uint(v.w) << 24; + return (a|b|c|d); +} +highp uint packSnorm4x8(mediump vec4 v) { + v = round(clamp(v, -1.0, 1.0) * 127.0); + highp uint a = uint((int(v.x) & 0xff)); + highp uint b = uint((int(v.y) & 0xff)) << 8; + highp uint c = uint((int(v.z) & 0xff)) << 16; + highp uint d = uint((int(v.w) & 0xff)) << 24; + return (a|b|c|d); +} +mediump vec4 unpackUnorm4x8(highp uint v) { + return vec4(float((v & 0x000000ffu) ), + float((v & 0x0000ff00u) >> 8), + float((v & 0x00ff0000u) >> 16), + float((v & 0xff000000u) >> 24)) / 255.0; +} +mediump vec4 unpackSnorm4x8(highp uint v) { + int a = int(((v ) & 0xffu) << 24u) >> 24 ; + int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ; + int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ; + int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ; + return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0); +} +)"sv; + } +#endif // BACKEND_OPENGL_VERSION_GLES return ""sv; } -// split shader source code in two, the first section goes from the start to the line after the -// last #extension, and the 2nd part goes from there to the end. -std::array ShaderCompilerService::splitShaderSource(std::string_view source) noexcept { +// split shader source code in three: +// - the version line +// - extensions +// - everything else +std::array ShaderCompilerService::splitShaderSource(std::string_view source) noexcept { auto start = source.find("#version"); assert_invariant(start != std::string_view::npos); + auto version_eol = source.find('\n', start) + 1; + assert_invariant(version_eol != std::string_view::npos); + auto pos = source.rfind("\n#extension"); if (pos == std::string_view::npos) { - pos = start; + pos = version_eol; } else { ++pos; } @@ -722,9 +801,10 @@ std::array ShaderCompilerService::splitShaderSource(std::st auto eol = source.find('\n', pos) + 1; assert_invariant(eol != std::string_view::npos); - std::string_view const version = source.substr(start, eol - start); - std::string_view const body = source.substr(version.length(), source.length() - version.length()); - return { version, body }; + std::string_view const version = source.substr(start, version_eol - start); + std::string_view const prolog = source.substr(version_eol, eol - version_eol); + std::string_view const body = source.substr(eol, source.length() - eol); + return { version, prolog, body }; } /* diff --git a/filament/backend/src/opengl/ShaderCompilerService.h b/filament/backend/src/opengl/ShaderCompilerService.h index 27255ea000..ef4059fc0b 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.h +++ b/filament/backend/src/opengl/ShaderCompilerService.h @@ -146,7 +146,7 @@ private: static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept; - static std::array splitShaderSource(std::string_view source) noexcept; + static std::array splitShaderSource(std::string_view source) noexcept; static GLuint linkProgram(OpenGLContext& context, std::array shaders, From 213eb6af9edfc890c228b35bfeef8249f772ddd2 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 18 Jun 2024 17:06:35 -0700 Subject: [PATCH 02/11] Lit Materials can now specify the quality of SH computations The number of SH bands used for the indirect light irradiance computations can be set to 1, 2 or 3 (default) in Material::Builder. For e.g. in lower-end devices w/ non HDR content, it might be beneficial to set this value to 2. BUGS=[341971013] --- .../src/main/cpp/Material.cpp | 9 ++++- .../com/google/android/filament/Material.java | 22 ++++++++++- filament/include/filament/IndirectLight.h | 2 + filament/include/filament/Material.h | 12 ++++++ filament/src/details/Material.cpp | 8 ++++ .../include/private/filament/EngineEnums.h | 1 + libs/filamat/src/shaders/CodeGenerator.cpp | 3 ++ shaders/src/light_indirect.fs | 37 ++++++++++--------- 8 files changed, 72 insertions(+), 22 deletions(-) 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/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/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/src/details/Material.cpp b/filament/src/details/Material.cpp index b8fa8d2999..30f240c315 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -119,6 +119,7 @@ struct Material::BuilderDetails { const void* mPayload = nullptr; size_t mSize = 0; bool mDefaultMaterial = false; + bool mShBandsCount = 3; std::unordered_map< utils::CString, std::variant, @@ -143,6 +144,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"; @@ -897,6 +903,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/libs/filabridge/include/private/filament/EngineEnums.h b/libs/filabridge/include/private/filament/EngineEnums.h index 4a8c042e75..9491f2e118 100644 --- a/libs/filabridge/include/private/filament/EngineEnums.h +++ b/libs/filabridge/include/private/filament/EngineEnums.h @@ -70,6 +70,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 75d2b940d8..6d631e0582 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -332,6 +332,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 5084b10b64..74bcaaff20 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) { From cc8a3ed96b311c265395a0eecb262637e8c5829e Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 20 Jun 2024 15:48:17 -0700 Subject: [PATCH 03/11] fix typo that broke glsl compilation on device --- filament/src/details/Material.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 30f240c315..6d73f96027 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -119,7 +119,7 @@ struct Material::BuilderDetails { const void* mPayload = nullptr; size_t mSize = 0; bool mDefaultMaterial = false; - bool mShBandsCount = 3; + int32_t mShBandsCount = 3; std::unordered_map< utils::CString, std::variant, From d15c53e530cc5a4d75a1fc1d99309a8fe8bc0776 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 20 Jun 2024 11:16:44 -0700 Subject: [PATCH 04/11] an easier way to pipe vsync time to beginFrame BUGS=[343764098] --- android/filament-android/src/main/cpp/Engine.cpp | 6 ++++++ .../filament-android/src/main/cpp/Renderer.cpp | 7 +++++++ .../java/com/google/android/filament/Engine.java | 8 +++++++- .../com/google/android/filament/Renderer.java | 14 ++++++++++++++ filament/include/filament/Engine.h | 15 +++++++++++++++ filament/include/filament/Renderer.h | 11 +++++++++++ filament/src/Engine.cpp | 8 +++++++- filament/src/Renderer.cpp | 4 ++++ filament/src/details/Renderer.cpp | 9 +++++++++ filament/src/details/Renderer.h | 16 +++++++++++++--- web/filament-js/jsbindings.cpp | 4 ++++ 11 files changed, 97 insertions(+), 5 deletions(-) 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/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/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/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/Renderer.cpp b/filament/src/details/Renderer.cpp index 15094a0759..b27486542f 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/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()) From e4a0bb8fa0042452c72b2d4a2ba7ed2297f86a61 Mon Sep 17 00:00:00 2001 From: toddZ_CG Date: Fri, 21 Jun 2024 10:06:17 -0700 Subject: [PATCH 05/11] Add support for KHR_materials_Specular (#7564) Co-authored-by: Todd Zhang Co-authored-by: Romain Guy Co-authored-by: Mathias Agopian --- README.md | 1 + .../include/filament/MaterialEnums.h | 4 +- libs/filamat/src/Enums.cpp | 54 +++--- libs/filamat/src/shaders/CodeGenerator.cpp | 2 + libs/gltfio/CMakeLists.txt | 3 + libs/gltfio/include/gltfio/MaterialProvider.h | 11 +- libs/gltfio/materials/base.spec.in | 5 + libs/gltfio/materials/sheen.mat.in | 20 ++- libs/gltfio/materials/sheen.spec.in | 5 + libs/gltfio/materials/specular.mat.in | 168 ++++++++++++++++++ libs/gltfio/materials/specular.spec.in | 35 ++++ libs/gltfio/materials/transmission.mat.in | 31 +++- libs/gltfio/materials/transmission.spec.in | 5 + libs/gltfio/materials/volume.mat.in | 18 ++ libs/gltfio/materials/volume.spec.in | 5 + libs/gltfio/src/AssetLoader.cpp | 35 +++- libs/gltfio/src/JitShaderProvider.cpp | 49 +++++ libs/gltfio/src/MaterialProvider.cpp | 19 ++ libs/gltfio/src/UbershaderProvider.cpp | 55 ++++++ shaders/src/brdf.fs | 10 ++ shaders/src/common_lighting.fs | 5 + shaders/src/light_indirect.fs | 5 +- shaders/src/material_inputs.fs | 18 ++ shaders/src/shading_lit.fs | 25 +++ shaders/src/shading_model_standard.fs | 6 +- 25 files changed, 559 insertions(+), 35 deletions(-) create mode 100644 libs/gltfio/materials/specular.mat.in create mode 100644 libs/gltfio/materials/specular.spec.in diff --git a/README.md b/README.md index b04cd6fa55..01144d1219 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,7 @@ steps: - [x] KHR_materials_unlit - [x] KHR_materials_variants - [x] KHR_materials_volume + - [x] KHR_materials_specular - [x] KHR_mesh_quantization - [x] KHR_texture_basisu - [x] KHR_texture_transform diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index c15c6c457c..cad7057fb1 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -203,7 +203,7 @@ enum class ReflectionMode : uint8_t { // can't really use std::underlying_type::type because the driver takes a uint32_t using AttributeBitset = utils::bitset32; -static constexpr size_t MATERIAL_PROPERTIES_COUNT = 27; +static constexpr size_t MATERIAL_PROPERTIES_COUNT = 29; enum class Property : uint8_t { BASE_COLOR, //!< float4, all shading models ROUGHNESS, //!< float, lit shading models only @@ -232,6 +232,8 @@ enum class Property : uint8_t { IOR, //!< float, material's index of refraction MICRO_THICKNESS, //!< float, thickness of the thin layer BENT_NORMAL, //!< float3, all shading models only, except unlit + SPECULAR_FACTOR, //!< float, lit shading models only, except subsurface and cloth + SPECULAR_COLOR_FACTOR, //!< float3, lit shading models only, except subsurface and cloth // when adding new Properties, make sure to update MATERIAL_PROPERTIES_COUNT }; diff --git a/libs/filamat/src/Enums.cpp b/libs/filamat/src/Enums.cpp index 583d376f95..bb163425da 100644 --- a/libs/filamat/src/Enums.cpp +++ b/libs/filamat/src/Enums.cpp @@ -21,33 +21,35 @@ namespace filamat { std::unordered_map Enums::mStringToProperty = { - { "baseColor", Property::BASE_COLOR }, - { "roughness", Property::ROUGHNESS }, - { "metallic", Property::METALLIC }, - { "reflectance", Property::REFLECTANCE }, - { "ambientOcclusion", Property::AMBIENT_OCCLUSION }, - { "clearCoat", Property::CLEAR_COAT }, - { "clearCoatRoughness", Property::CLEAR_COAT_ROUGHNESS }, - { "clearCoatNormal", Property::CLEAR_COAT_NORMAL }, - { "anisotropy", Property::ANISOTROPY }, - { "anisotropyDirection", Property::ANISOTROPY_DIRECTION }, - { "thickness", Property::THICKNESS }, - { "subsurfacePower", Property::SUBSURFACE_POWER }, - { "subsurfaceColor", Property::SUBSURFACE_COLOR }, - { "sheenColor", Property::SHEEN_COLOR }, - { "sheenRoughness", Property::SHEEN_ROUGHNESS }, - { "glossiness", Property::GLOSSINESS }, - { "specularColor", Property::SPECULAR_COLOR }, - { "emissive", Property::EMISSIVE }, - { "normal", Property::NORMAL }, - { "postLightingColor", Property::POST_LIGHTING_COLOR }, + { "baseColor", Property::BASE_COLOR }, + { "roughness", Property::ROUGHNESS }, + { "metallic", Property::METALLIC }, + { "reflectance", Property::REFLECTANCE }, + { "ambientOcclusion", Property::AMBIENT_OCCLUSION }, + { "clearCoat", Property::CLEAR_COAT }, + { "clearCoatRoughness", Property::CLEAR_COAT_ROUGHNESS }, + { "clearCoatNormal", Property::CLEAR_COAT_NORMAL }, + { "anisotropy", Property::ANISOTROPY }, + { "anisotropyDirection", Property::ANISOTROPY_DIRECTION }, + { "thickness", Property::THICKNESS }, + { "subsurfacePower", Property::SUBSURFACE_POWER }, + { "subsurfaceColor", Property::SUBSURFACE_COLOR }, + { "sheenColor", Property::SHEEN_COLOR }, + { "sheenRoughness", Property::SHEEN_ROUGHNESS }, + { "glossiness", Property::GLOSSINESS }, + { "specularColor", Property::SPECULAR_COLOR }, + { "emissive", Property::EMISSIVE }, + { "normal", Property::NORMAL }, + { "postLightingColor", Property::POST_LIGHTING_COLOR }, { "postLightingMixFactor", Property::POST_LIGHTING_MIX_FACTOR }, - { "clipSpaceTransform", Property::CLIP_SPACE_TRANSFORM }, - { "absorption", Property::ABSORPTION }, - { "transmission", Property::TRANSMISSION }, - { "ior", Property::IOR }, - { "microThickness", Property::MICRO_THICKNESS }, - { "bentNormal", Property::BENT_NORMAL }, + { "clipSpaceTransform", Property::CLIP_SPACE_TRANSFORM }, + { "absorption", Property::ABSORPTION }, + { "transmission", Property::TRANSMISSION }, + { "ior", Property::IOR }, + { "microThickness", Property::MICRO_THICKNESS }, + { "bentNormal", Property::BENT_NORMAL }, + { "specularFactor", Property::SPECULAR_FACTOR }, + { "specularColorFactor", Property::SPECULAR_COLOR_FACTOR } }; template <> diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index 6d631e0582..1c74cbd779 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -1192,6 +1192,8 @@ char const* CodeGenerator::getConstantName(MaterialBuilder::Property property) n case Property::IOR: return "IOR"; case Property::MICRO_THICKNESS: return "MICRO_THICKNESS"; case Property::BENT_NORMAL: return "BENT_NORMAL"; + case Property::SPECULAR_FACTOR: return "SPECULAR_FACTOR"; + case Property::SPECULAR_COLOR_FACTOR: return "SPECULAR_COLOR_FACTOR"; } } diff --git a/libs/gltfio/CMakeLists.txt b/libs/gltfio/CMakeLists.txt index c066cc2297..c7c7d6a63c 100644 --- a/libs/gltfio/CMakeLists.txt +++ b/libs/gltfio/CMakeLists.txt @@ -147,6 +147,9 @@ build_ubershader(volume_opaque volume lit opaque) build_ubershader(volume_fade volume lit fade) build_ubershader(volume_masked volume lit masked) build_ubershader(sheen sheen _ _) +build_ubershader(specular_opaque specular lit opaque) +build_ubershader(specular_fade specular lit fade) +build_ubershader(specular_masked specular lit masked) add_custom_target(uberz_file DEPENDS ${UBERZ_OUTPUT_PATH}) set_target_properties(uberz_file PROPERTIES FOLDER Libs) diff --git a/libs/gltfio/include/gltfio/MaterialProvider.h b/libs/gltfio/include/gltfio/MaterialProvider.h index f62d667b35..042f303bb6 100644 --- a/libs/gltfio/include/gltfio/MaterialProvider.h +++ b/libs/gltfio/include/gltfio/MaterialProvider.h @@ -93,10 +93,17 @@ struct alignas(4) MaterialKey { bool hasSheen : 1; bool hasIOR : 1; bool hasVolume : 1; - uint8_t padding : 5; + bool hasSpecular : 1; + bool hasSpecularTexture : 1; + bool hasSpecularColorTexture : 1; + bool padding : 2; + // -- 32 bit boundary -- + uint8_t specularTextureUV; + uint8_t specularColorTextureUV; + uint16_t padding2; }; -static_assert(sizeof(MaterialKey) == 16, "MaterialKey has unexpected size."); +static_assert(sizeof(MaterialKey) == 20, "MaterialKey has unexpected size."); UTILS_WARNING_POP diff --git a/libs/gltfio/materials/base.spec.in b/libs/gltfio/materials/base.spec.in index 6e6e9375f5..f80236f5a1 100644 --- a/libs/gltfio/materials/base.spec.in +++ b/libs/gltfio/materials/base.spec.in @@ -30,3 +30,8 @@ TransmissionTexture = unsupported # Volume Features Volume = unsupported VolumeThicknessTexture = unsupported + +# Specular Features +Specular = unsupported +SpecularTexture = unsupported +SpecularColorTexture = unsupported diff --git a/libs/gltfio/materials/sheen.mat.in b/libs/gltfio/materials/sheen.mat.in index b8682ca34f..6ac496c96d 100644 --- a/libs/gltfio/materials/sheen.mat.in +++ b/libs/gltfio/materials/sheen.mat.in @@ -55,7 +55,16 @@ material { { type : mat3, name : sheenRoughnessUvMatrix, precision: high }, // Reflectance - { type : float, name : reflectance } + { type : float, name : reflectance }, + + // KHR_materials_specular + { type : float, name : specularStrength }, + { type : float3, name : specularColorFactor }, + { type : int, name : specularIndex }, + { type : sampler2d, name : specularMap }, + { type : mat3, name : specularUvMatrix, precision: high }, + { type : int, name : specularColorIndex }, + { type : mat3, name : specularColorUvMatrix, precision: high } ${CUSTOM_PARAMS} ], @@ -136,6 +145,15 @@ fragment { material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb; } + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + + if (materialParams.specularIndex > -1) { + highp float2 uv = uvs[materialParams.specularIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularUvMatrix).xy; + material.specularFactor *= texture(materialParams_specularMap, uv).a; + } + ${CUSTOM_FRAGMENT} } } diff --git a/libs/gltfio/materials/sheen.spec.in b/libs/gltfio/materials/sheen.spec.in index dcd41bf25c..6c72f0b3f9 100644 --- a/libs/gltfio/materials/sheen.spec.in +++ b/libs/gltfio/materials/sheen.spec.in @@ -17,3 +17,8 @@ TextureTransforms=optional Sheen=optional SheenColorTexture=optional SheenRoughnessTexture=optional + +# KHR_material_specular Features +Specular=optional +SpecularTexture=optional +SpecularColorTexture=unsupported diff --git a/libs/gltfio/materials/specular.mat.in b/libs/gltfio/materials/specular.mat.in new file mode 100644 index 0000000000..acb4f2dbd7 --- /dev/null +++ b/libs/gltfio/materials/specular.mat.in @@ -0,0 +1,168 @@ +material { + name : specular_ubershader, + requires : [ uv0, uv1, color ], + shadingModel : lit, + blending : ${BLENDING}, + doubleSided : ${DOUBLESIDED}, + transparency : ${TRANSPARENCY}, + flipUV : false, + specularAmbientOcclusion : simple, + specularAntiAliasing : true, + clearCoatIorChange : false, + reflections: screenspace, + parameters : [ + + // Base Color + { type : int, name : baseColorIndex }, + { type : float4, name : baseColorFactor }, + { type : sampler2d, name : baseColorMap }, + { type : mat3, name : baseColorUvMatrix, precision: high }, + + // Metallic-Roughness Map + { type : int, name : metallicRoughnessIndex }, + { type : float, name : metallicFactor }, + { type : float, name : roughnessFactor }, + { type : sampler2d, name : metallicRoughnessMap }, + { type : mat3, name : metallicRoughnessUvMatrix, precision: high }, + + // Normal Map + { type : int, name : normalIndex }, + { type : float, name : normalScale }, + { type : sampler2d, name : normalMap }, + { type : mat3, name : normalUvMatrix, precision: high }, + + // Ambient Occlusion + { type : int, name : aoIndex }, + { type : float, name : aoStrength }, + { type : sampler2d, name : occlusionMap }, + { type : mat3, name : occlusionUvMatrix, precision: high }, + + // Emissive Map + { type : int, name : emissiveIndex }, + { type : float3, name : emissiveFactor }, + { type : float, name : emissiveStrength }, + { type : sampler2d, name : emissiveMap }, + { type : mat3, name : emissiveUvMatrix, precision: high }, + + // Clear coat, we drop texture support for clear coat due to 8 texture limits. + { type : float, name : clearCoatFactor }, + { type : float, name : clearCoatRoughnessFactor }, + { type : int, name : clearCoatIndex }, + { type : sampler2d, name : clearCoatMap }, + { type : mat3, name : clearCoatUvMatrix, precision: high }, + { type : int, name : clearCoatRoughnessIndex }, + { type : mat3, name : clearCoatRoughnessUvMatrix, precision: high }, + { type : int, name : clearCoatNormalIndex }, + { type : mat3, name : clearCoatNormalUvMatrix, precision: high }, + { type : float, name : clearCoatNormalScale }, + + // KHR_materials_specular + { type : float, name : specularStrength }, + { type : float3, name : specularColorFactor }, + { type : int, name : specularIndex }, + { type : sampler2d, name : specularMap }, + { type : mat3, name : specularUvMatrix, precision: high }, + { type : int, name : specularColorIndex }, + { type : sampler2d, name : specularColorMap }, + { type : mat3, name : specularColorUvMatrix, precision: high }, + + // IOR + { type : float, name : ior } + + ${CUSTOM_PARAMS} + ], +} + +vertex { +void materialVertex(inout MaterialVertexInputs material) { + ${CUSTOM_VERTEX} +} +} + +fragment { +void material(inout MaterialInputs material) { + highp float2 uvs[2]; + uvs[0] = getUV0(); + uvs[1] = getUV1(); + + if (materialParams.normalIndex > -1) { + highp float2 uv = uvs[materialParams.normalIndex]; + uv = (vec3(uv, 1.0) * materialParams.normalUvMatrix).xy; + material.normal = texture(materialParams_normalMap, uv).xyz * 2.0 - 1.0; + material.normal.xy *= materialParams.normalScale; + } + + prepareMaterial(material); + material.baseColor = materialParams.baseColorFactor; + + if (materialParams.baseColorIndex > -1) { + highp float2 uv = uvs[materialParams.baseColorIndex]; + uv = (vec3(uv, 1.0) * materialParams.baseColorUvMatrix).xy; + material.baseColor *= texture(materialParams_baseColorMap, uv); + } + +#if defined(BLEND_MODE_TRANSPARENT) + material.baseColor.rgb *= material.baseColor.a; +#endif + + material.baseColor *= getColor(); + + material.roughness = materialParams.roughnessFactor; + material.metallic = materialParams.metallicFactor; + material.ior = materialParams.ior; + + material.emissive = vec4(materialParams.emissiveStrength * + materialParams.emissiveFactor.rgb, 0.0); + + // KHR_materials_clearcoat forbids clear coat from + // being applied in the specular/glossiness model + material.clearCoat = materialParams.clearCoatFactor; + material.clearCoatRoughness = materialParams.clearCoatRoughnessFactor; + + if (materialParams.clearCoatIndex > -1) { + highp float2 uv = uvs[materialParams.clearCoatIndex]; + uv = (vec3(uv, 1.0) * materialParams.clearCoatUvMatrix).xy; + material.clearCoat *= texture(materialParams_clearCoatMap, uv).r; + } + + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + + if (materialParams.specularIndex > -1) { + highp float2 uv = uvs[materialParams.specularIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularUvMatrix).xy; + material.specularFactor *= texture(materialParams_specularMap, uv).a; + } + + if (materialParams.specularColorIndex > -1) { + highp float2 uv = uvs[materialParams.specularColorIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularColorUvMatrix).xy; + vec4 sc = texture(materialParams_specularColorMap, uv); + material.specularColorFactor.r *= sc.r; + material.specularColorFactor.g *= sc.g; + material.specularColorFactor.b *= sc.b; + } + + if (materialParams.metallicRoughnessIndex > -1) { + highp float2 uv = uvs[materialParams.metallicRoughnessIndex]; + uv = (vec3(uv, 1.0) * materialParams.metallicRoughnessUvMatrix).xy; + vec4 mr = texture(materialParams_metallicRoughnessMap, uv); + material.roughness *= mr.g; + material.metallic *= mr.b; + } + + if (materialParams.aoIndex > -1) { + highp float2 uv = uvs[materialParams.aoIndex]; + uv = (vec3(uv, 1.0) * materialParams.occlusionUvMatrix).xy; + float occlusion = texture(materialParams_occlusionMap, uv).r; + material.ambientOcclusion = 1.0 + materialParams.aoStrength * (occlusion - 1.0); + } + if (materialParams.emissiveIndex > -1) { + highp float2 uv = uvs[materialParams.emissiveIndex]; + uv = (vec3(uv, 1.0) * materialParams.emissiveUvMatrix).xy; + material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb; + } + + ${CUSTOM_FRAGMENT} +} +} diff --git a/libs/gltfio/materials/specular.spec.in b/libs/gltfio/materials/specular.spec.in new file mode 100644 index 0000000000..1b381ee8b5 --- /dev/null +++ b/libs/gltfio/materials/specular.spec.in @@ -0,0 +1,35 @@ +ShadingModel=lit + +# Required Aspects +BlendingMode = ${BLENDING} + +VertexColors = optional +BaseColorTexture = optional +NormalTexture = optional +OcclusionTexture = optional +EmissiveTexture = optional +MetallicRoughnessTexture = optional +TextureTransforms = optional + +# KHR_materials_specular Features +Specular = optional +SpecularTexture = optional +SpecularColorTexture = optional + +# ClearCoat Features +ClearCoat = optional +ClearCoatTexture = optional + +# Sheen Features +Sheen = unsupported +SheenColorTexture = unsupported +SheenRoughnessTexture = unsupported + +# Transmission and IOR Features +Transmission = unsupported +Ior = optional +TransmissionTexture = unsupported + +# Volume Features +Volume = unsupported +VolumeThicknessTexture = unsupported diff --git a/libs/gltfio/materials/transmission.mat.in b/libs/gltfio/materials/transmission.mat.in index 631ec89763..608b8576f2 100644 --- a/libs/gltfio/materials/transmission.mat.in +++ b/libs/gltfio/materials/transmission.mat.in @@ -52,7 +52,18 @@ material { { type : mat3, name : transmissionUvMatrix, precision: high }, // IOR - { type : float, name : ior } + { type : float, name : ior }, + + // KHR_materials_specular + { type : float, name : specularStrength }, + { type : float3, name : specularColorFactor }, + { type : int, name : specularIndex }, + { type : sampler2d, name : specularMap }, + { type : mat3, name : specularUvMatrix, precision: high }, + { type : int, name : specularColorIndex }, + { type : sampler2d, name : specularColorMap }, + { type : mat3, name : specularColorUvMatrix, precision: high } + ${CUSTOM_PARAMS} ], @@ -126,6 +137,24 @@ fragment { material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb; } + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + + if (materialParams.specularIndex > -1) { + highp float2 uv = uvs[materialParams.specularIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularUvMatrix).xy; + material.specularFactor *= texture(materialParams_specularMap, uv).a; + } + + if (materialParams.specularColorIndex > -1) { + highp float2 uv = uvs[materialParams.specularColorIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularColorUvMatrix).xy; + vec4 sc = texture(materialParams_specularColorMap, uv); + material.specularColorFactor.r *= sc.r; + material.specularColorFactor.g *= sc.g; + material.specularColorFactor.b *= sc.b; + } + ${CUSTOM_FRAGMENT} } } diff --git a/libs/gltfio/materials/transmission.spec.in b/libs/gltfio/materials/transmission.spec.in index ac76ae5072..ff0c364813 100644 --- a/libs/gltfio/materials/transmission.spec.in +++ b/libs/gltfio/materials/transmission.spec.in @@ -15,6 +15,11 @@ EmissiveTexture=optional MetallicRoughnessTexture=optional TextureTransforms=optional +# KHR_materials_specular Features +Specular=optional +SpecularTexture=optional +SpecularColorTexture=optional + Transmission=optional Ior=optional TransmissionTexture=optional diff --git a/libs/gltfio/materials/volume.mat.in b/libs/gltfio/materials/volume.mat.in index 18998f5877..d7470bb257 100644 --- a/libs/gltfio/materials/volume.mat.in +++ b/libs/gltfio/materials/volume.mat.in @@ -58,6 +58,15 @@ material { { type : sampler2d, name : volumeThicknessMap }, { type : mat3, name : volumeThicknessUvMatrix, precision: high }, + // KHR_materials_specular + { type : float, name : specularStrength }, + { type : float3, name : specularColorFactor }, + { type : int, name : specularIndex }, + { type : sampler2d, name : specularMap }, + { type : mat3, name : specularUvMatrix, precision: high }, + { type : int, name : specularColorIndex }, + { type : mat3, name : specularColorUvMatrix, precision: high }, + // IOR { type : float, name : ior } @@ -142,6 +151,15 @@ fragment { material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb; } + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + + if (materialParams.specularIndex > -1) { + highp float2 uv = uvs[materialParams.specularIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularUvMatrix).xy; + material.specularFactor *= texture(materialParams_specularMap, uv).a; + } + ${CUSTOM_FRAGMENT} } } diff --git a/libs/gltfio/materials/volume.spec.in b/libs/gltfio/materials/volume.spec.in index fe290ab7f1..5f65676765 100644 --- a/libs/gltfio/materials/volume.spec.in +++ b/libs/gltfio/materials/volume.spec.in @@ -26,3 +26,8 @@ TransmissionTexture=optional # Volume Features Volume=optional VolumeThicknessTexture=optional + +# KHR_materials_specular Features +Specular=optional +SpecularTexture=optional +SpecularColorTexture=unsupported diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index 31bbc169f7..19aa85e3a6 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -1222,6 +1222,7 @@ MaterialKey FAssetLoader::getMaterialKey(const cgltf_data* srcAsset, auto trConfig = inputMat->transmission; auto shConfig = inputMat->sheen; auto vlConfig = inputMat->volume; + auto spConfig = inputMat->specular; *baseColorTexture = mrConfig.base_color_texture; *metallicRoughnessTexture = mrConfig.metallic_roughness_texture; @@ -1238,7 +1239,9 @@ MaterialKey FAssetLoader::getMaterialKey(const cgltf_data* srcAsset, ccConfig.clearcoat_normal_texture.has_transform || shConfig.sheen_color_texture.has_transform || shConfig.sheen_roughness_texture.has_transform || - trConfig.transmission_texture.has_transform; + trConfig.transmission_texture.has_transform || + spConfig.specular_color_texture.has_transform || + spConfig.specular_texture.has_transform; MaterialKey matkey { .doubleSided = !!inputMat->double_sided, @@ -1273,6 +1276,11 @@ MaterialKey FAssetLoader::getMaterialKey(const cgltf_data* srcAsset, .hasSheen = !!inputMat->has_sheen, .hasIOR = !!inputMat->has_ior, .hasVolume = !!inputMat->has_volume, + .hasSpecular = !!inputMat->has_specular, + .hasSpecularTexture = spConfig.specular_texture.texture != nullptr, + .hasSpecularColorTexture = spConfig.specular_color_texture.texture != nullptr, + .specularTextureUV = (uint8_t) spConfig.specular_texture.texcoord, + .specularColorTextureUV = (uint8_t) spConfig.specular_color_texture.texcoord, }; if (inputMat->has_pbr_specular_glossiness) { @@ -1361,6 +1369,7 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_material* inp auto trConfig = inputMat->transmission; auto shConfig = inputMat->sheen; auto vlConfig = inputMat->volume; + auto spConfig = inputMat->specular; // Check the material blending mode, not the cgltf blending mode, because the provider // might have selected an alternative blend mode (e.g. to support transmission). @@ -1560,6 +1569,30 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_material* inp inputMat->emissive_strength.emissive_strength : 1.0f); } + if (matkey.hasSpecular) { + const float* s = spConfig.specular_color_factor; + mi->setParameter("specularColorFactor", float3{s[0], s[1], s[2]}); + mi->setParameter("specularStrength", spConfig.specular_factor); + + if (matkey.hasSpecularColorTexture) { + fAsset->addTextureBinding(mi, "specularColorMap", spConfig.specular_color_texture.texture, sRGB); + if (matkey.hasTextureTransforms) { + const cgltf_texture_transform uvt = spConfig.specular_color_texture.transform; + auto uvmat = matrixFromUvTransform(uvt.offset, uvt.rotation, uvt.scale); + mi->setParameter("specularColorUvMatrix", uvmat); + } + } + if (matkey.hasSpecularTexture) { + bool sameTexture = spConfig.specular_color_texture.texture == spConfig.specular_texture.texture; + fAsset->addTextureBinding(mi, "specularMap", spConfig.specular_texture.texture, sameTexture ? sRGB : LINEAR); + if (matkey.hasTextureTransforms) { + const cgltf_texture_transform uvt = spConfig.specular_texture.transform; + auto uvmat = matrixFromUvTransform(uvt.offset, uvt.rotation, uvt.scale); + mi->setParameter("specularUvMatrix", uvmat); + } + } + } + *cacheEntry = { mi, *uvmap }; return mi; } diff --git a/libs/gltfio/src/JitShaderProvider.cpp b/libs/gltfio/src/JitShaderProvider.cpp index b42acda520..44651176a5 100644 --- a/libs/gltfio/src/JitShaderProvider.cpp +++ b/libs/gltfio/src/JitShaderProvider.cpp @@ -308,6 +308,35 @@ std::string shaderFromKey(const MaterialKey& config) { material.ior = materialParams.ior; )SHADER"; } + + if (config.hasSpecular) { + shader += R"SHADER( + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + )SHADER"; + + if (config.hasSpecularTexture) { + shader += "highp float2 specularUV = ${specular};\n"; + if (config.hasTextureTransforms) { + shader += "specularUV = (vec3(specularUV, 1.0) * " + "materialParams.specularUvMatrix).xy;\n"; + } + shader += R"SHADER( + material.specularFactor *= texture(materialParams_specularMap, specularUV).a; + )SHADER"; + } + + if (config.hasSpecularColorTexture) { + shader += "highp float2 specularColorUV = ${specularColor};\n"; + if (config.hasTextureTransforms) { + shader += "specularColorUV = (vec3(specularColorUV, 1.0) * " + "materialParams.specularColorUvMatrix).xy;\n"; + } + shader += R"SHADER( + material.specularColorFactor *= texture(materialParams_specularColorMap, specularColorUV).rgb; + )SHADER"; + } + } } shader += "}\n"; @@ -463,6 +492,26 @@ Material* createMaterial(Engine* engine, const MaterialKey& config, const UvMap& } } + // SPECULAR + if (config.hasSpecular) { + builder.parameter("specularStrength", MaterialBuilder::UniformType::FLOAT); + builder.parameter("specularColorFactor", MaterialBuilder::UniformType::FLOAT3); + if (config.hasSpecularTexture) { + builder.parameter("specularMap", MaterialBuilder::SamplerType::SAMPLER_2D); + if (config.hasTextureTransforms) { + builder.parameter("specularUvMatrix", MaterialBuilder::UniformType::MAT3, + MaterialBuilder::ParameterPrecision::HIGH); + } + } + if (config.hasSpecularColorTexture) { + builder.parameter("specularColorMap", MaterialBuilder::SamplerType::SAMPLER_2D); + if (config.hasTextureTransforms) { + builder.parameter("specularColorUvMatrix", MaterialBuilder::UniformType::MAT3, + MaterialBuilder::ParameterPrecision::HIGH); + } + } + } + // BLENDING switch (config.alphaMode) { case AlphaMode::OPAQUE: diff --git a/libs/gltfio/src/MaterialProvider.cpp b/libs/gltfio/src/MaterialProvider.cpp index b42bd9a930..7e7fa6d947 100644 --- a/libs/gltfio/src/MaterialProvider.cpp +++ b/libs/gltfio/src/MaterialProvider.cpp @@ -143,6 +143,21 @@ void constrainMaterial(MaterialKey* key, UvMap* uvmap) { retval[key->volumeThicknessUV] = (UvSet) index++; } } + if (key->hasSpecularTexture && retval[key->specularTextureUV] == UNUSED) { + if (index > MAX_INDEX) { + key->hasSpecularTexture = false; + } else { + retval[key->specularTextureUV] = (UvSet) index++; + } + } + if (key->hasSpecularColorTexture && retval[key->specularColorTextureUV] == UNUSED) { + if (index > MAX_INDEX) { + key->hasSpecularColorTexture = false; + } else { + retval[key->specularColorTextureUV] = (UvSet) index++; + } + } + // NOTE: KHR_materials_clearcoat does not provide separate UVs, we'll assume UV0 *uvmap = retval; } @@ -167,6 +182,8 @@ void processShaderString(std::string* shader, const UvMap& uvmap, const Material const auto& sheenColorUV = uvstrings[uvmap[config.sheenColorUV]]; const auto& sheenRoughnessUV = uvstrings[uvmap[config.sheenRoughnessUV]]; const auto& volumeThicknessUV = uvstrings[uvmap[config.volumeThicknessUV]]; + const auto& specularUV = uvstrings[uvmap[config.specularTextureUV]]; + const auto& specularColorUV = uvstrings[uvmap[config.specularColorTextureUV]]; replaceAll("${normal}", normalUV); replaceAll("${color}", baseColorUV); @@ -180,6 +197,8 @@ void processShaderString(std::string* shader, const UvMap& uvmap, const Material replaceAll("${sheenColor}", sheenColorUV); replaceAll("${sheenRoughness}", sheenRoughnessUV); replaceAll("${volumeThickness}", volumeThicknessUV); + replaceAll("${specular}", specularUV); + replaceAll("${specularColor}", specularColorUV); } } // namespace filament::gltfio diff --git a/libs/gltfio/src/UbershaderProvider.cpp b/libs/gltfio/src/UbershaderProvider.cpp index 7e184f648d..64451aa363 100644 --- a/libs/gltfio/src/UbershaderProvider.cpp +++ b/libs/gltfio/src/UbershaderProvider.cpp @@ -63,6 +63,42 @@ static void prepareConfig(MaterialKey* config, const char* label) { config->hasSheen = false; config->hasIOR = false; } + + if (config->useSpecularGlossiness && config->hasSpecular) { + slog.w << "SpecularGlossiness and specular are not supported together in ubershader mode," + " removing specularGlossiness (" << label << ")." << io::endl; + + config->useSpecularGlossiness = false; + } + + if (config->unlit && config->hasSpecular) { + slog.w << "Unlit and specular are not supported together in ubershader mode," + " removing unlit (" << label << ")." << io::endl; + + config->unlit = false; + } + + // Also due to sampler overload, clearcoat with specular, we can only support with specular map. + if ((config->hasClearCoatNormalTexture || config->hasClearCoatRoughnessTexture) && config->hasSpecular) { + slog.w << "Specular can only work with clearcoat that does not use clear coat normal or clear coat roughness map," + " removing clear coat normal and roughness (" << label << ")." << io::endl; + config->hasClearCoatNormalTexture = false; + config->hasClearCoatRoughnessTexture = false; + } + + // Also due to sampler overload, sheen with specular, we can only support without specular color texture. + if (config->hasSpecularColorTexture && config->hasSheen) { + slog.w << "Sheen can only work with specular that does not use specular color map," + " removing specular color (" << label << ")." << io::endl; + config->hasSpecularColorTexture = false; + } + + // Also due to sampler overload, volume with specular, we can only support without specular color texture. + if (config->hasSpecularColorTexture && config->hasVolume) { + slog.w << "Volume can only work with specular that does not use specular color map," + "removing specular color (" << label << ")." << io::endl; + config->hasSpecularColorTexture = false; + } } class UbershaderProvider : public MaterialProvider { @@ -155,6 +191,9 @@ Material* UbershaderProvider::getMaterial(const MaterialKey& config) const { requirements.features["SheenColorTexture"] = config.hasSheenColorTexture; requirements.features["SheenRoughnessTexture"] = config.hasSheenRoughnessTexture; requirements.features["VolumeThicknessTexture"] = config.hasVolumeThicknessTexture; + requirements.features["Specular"] = config.hasSpecular; + requirements.features["SpecularTexture"] = config.hasSpecularTexture; + requirements.features["SpecularColorTexture"] = config.hasSpecularColorTexture; if (Material* mat = mMaterials.getMaterial(requirements); mat != nullptr) { return mat; @@ -250,6 +289,14 @@ MaterialInstance* UbershaderProvider::createMaterialInstance(MaterialKey* config mi->setParameter("transmissionIndex", getUvIndex(config->transmissionUV, config->hasTransmissionTexture)); } + if (config->hasSpecular) { + mi->setParameter("specularUvMatrix", identity); + mi->setParameter("specularIndex", + getUvIndex(config->specularTextureUV, config->hasSpecularTexture)); + mi->setParameter("specularColorUvMatrix", identity); + mi->setParameter("specularColorIndex", + getUvIndex(config->specularColorTextureUV, config->hasSpecularColorTexture)); + } } TextureSampler sampler; @@ -300,6 +347,14 @@ MaterialInstance* UbershaderProvider::createMaterialInstance(MaterialKey* config mi->setParameter("reflectance", 0.5f); } + if (needsTexture("SpecularTexture")) { + mi->setParameter("specularMap", mDummyTexture, sampler); + } + + if (needsTexture("SpecularColorTexture")) { + mi->setParameter("specularColorMap", mDummyTexture, sampler); + } + return mi; } diff --git a/shaders/src/brdf.fs b/shaders/src/brdf.fs index f4204cf44d..2f4d152255 100644 --- a/shaders/src/brdf.fs +++ b/shaders/src/brdf.fs @@ -181,6 +181,16 @@ vec3 fresnel(const vec3 f0, float LoH) { #endif } +vec3 fresnel(const vec3 f0, const float f90, float LoH) { +#if BRDF_SPECULAR_F == SPECULAR_F_SCHLICK +#if FILAMENT_QUALITY == FILAMENT_QUALITY_LOW + return F_Schlick(f0, LoH); // f90 = 1.0 +#else + return F_Schlick(f0, f90, LoH); +#endif +#endif +} + float distributionAnisotropic(float at, float ab, float ToH, float BoH, float NoH) { #if BRDF_ANISOTROPIC_D == SPECULAR_D_GGX_ANISOTROPIC return D_GGX_Anisotropic(at, ab, ToH, BoH, NoH); diff --git a/shaders/src/common_lighting.fs b/shaders/src/common_lighting.fs index 2f822460de..43df890838 100644 --- a/shaders/src/common_lighting.fs +++ b/shaders/src/common_lighting.fs @@ -18,6 +18,11 @@ struct PixelParams { float perceptualRoughness; float perceptualRoughnessUnclamped; vec3 f0; +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) || defined(MATERIAL_HAS_SPECULAR_FACTOR) + float f90; + float specular; + vec3 specularColor; +#endif float roughness; vec3 dfg; vec3 energyCompensation; diff --git a/shaders/src/light_indirect.fs b/shaders/src/light_indirect.fs index 74bcaaff20..5dc3cad35d 100644 --- a/shaders/src/light_indirect.fs +++ b/shaders/src/light_indirect.fs @@ -136,9 +136,13 @@ vec3 getSpecularDominantDirection(const vec3 n, const vec3 r, float roughness) { vec3 specularDFG(const PixelParams pixel) { #if defined(SHADING_MODEL_CLOTH) return pixel.f0 * pixel.dfg.z; +#else +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) || defined(MATERIAL_HAS_SPECULAR_FACTOR) + return mix(pixel.dfg.xxx, pixel.dfg.yyy, pixel.f0) * pixel.specular; #else return mix(pixel.dfg.xxx, pixel.dfg.yyy, pixel.f0); #endif +#endif } vec3 getReflectedVector(const PixelParams pixel, const vec3 n) { @@ -663,7 +667,6 @@ void evaluateIBL(const MaterialInputs material, const PixelParams pixel, inout v // Combine all terms // Note: iblLuminance is already premultiplied by the exposure - color.rgb += Fr + Fd; #if defined(MATERIAL_HAS_REFRACTION) color.rgb += Ft; diff --git a/shaders/src/material_inputs.fs b/shaders/src/material_inputs.fs index 53d4ade1ec..32923dc90a 100644 --- a/shaders/src/material_inputs.fs +++ b/shaders/src/material_inputs.fs @@ -89,6 +89,15 @@ struct MaterialInputs { #endif #endif #endif + +#if defined(MATERIAL_HAS_SPECULAR_FACTOR) + float specularFactor; +#endif + +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) + vec3 specularColorFactor; +#endif + }; void initMaterial(out MaterialInputs material) { @@ -177,6 +186,15 @@ void initMaterial(out MaterialInputs material) { #endif #endif #endif + +#if defined(MATERIAL_HAS_SPECULAR_FACTOR) + material.specularFactor = 1.0; +#endif + +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) + material.specularColorFactor = vec3(1.0); +#endif + } #if defined(MATERIAL_HAS_CUSTOM_SURFACE_SHADING) diff --git a/shaders/src/shading_lit.fs b/shaders/src/shading_lit.fs index bf80ea57cd..103005845c 100644 --- a/shaders/src/shading_lit.fs +++ b/shaders/src/shading_lit.fs @@ -75,7 +75,21 @@ void getCommonPixelParams(const MaterialInputs material, inout PixelParams pixel // Assumes an interface from air to an IOR of 1.5 for dielectrics float reflectance = computeDielectricF0(material.reflectance); #endif +#if !defined(MATERIAL_HAS_SPECULAR_FACTOR) && !defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) pixel.f0 = computeF0(baseColor, material.metallic, reflectance); +#else + vec3 dielectricSpecularF0 = vec3(0.0); + float dielectricSpecularF90 = 0.0; +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) + dielectricSpecularF0 = min(reflectance * material.specularColorFactor, vec3(1.0)); +#endif +#if defined(MATERIAL_HAS_SPECULAR_FACTOR) + dielectricSpecularF0 *= material.specularFactor; + dielectricSpecularF90 = material.specularFactor; +#endif + pixel.f0 = baseColor.rgb * material.metallic + dielectricSpecularF0 * (1.0 - material.metallic); + pixel.f90 = material.metallic + dielectricSpecularF90 * (1.0 - material.metallic); +#endif #else pixel.diffuseColor = baseColor.rgb; pixel.f0 = material.sheenColor; @@ -123,6 +137,16 @@ void getCommonPixelParams(const MaterialInputs material, inout PixelParams pixel #endif } +void getSpecularPixelParams(const MaterialInputs material, inout PixelParams pixel) { +#if defined(MATERIAL_HAS_SPECULAR_FACTOR) + pixel.specular = material.specularFactor; +#endif + +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) + pixel.specularColor = material.specularColorFactor; +#endif +} + void getSheenPixelParams(const MaterialInputs material, inout PixelParams pixel) { #if defined(MATERIAL_HAS_SHEEN_COLOR) && !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE) pixel.sheenColor = material.sheenColor; @@ -232,6 +256,7 @@ void getEnergyCompensationPixelParams(inout PixelParams pixel) { * testing fails. */ void getPixelParams(const MaterialInputs material, out PixelParams pixel) { + getSpecularPixelParams(material, pixel); getCommonPixelParams(material, pixel); getSheenPixelParams(material, pixel); getClearCoatPixelParams(material, pixel); diff --git a/shaders/src/shading_model_standard.fs b/shaders/src/shading_model_standard.fs index 025295ba27..ca71d1de0f 100644 --- a/shaders/src/shading_model_standard.fs +++ b/shaders/src/shading_model_standard.fs @@ -63,8 +63,11 @@ vec3 isotropicLobe(const PixelParams pixel, const Light light, const vec3 h, float D = distribution(pixel.roughness, NoH, h); float V = visibility(pixel.roughness, NoV, NoL); +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) || defined(MATERIAL_HAS_SPECULAR_FACTOR) + vec3 F = fresnel(pixel.f0, pixel.f90, LoH); +#else vec3 F = fresnel(pixel.f0, LoH); - +#endif return (D * V) * F; } @@ -144,7 +147,6 @@ vec3 surfaceShading(const PixelParams pixel, const Light light, float occlusion) color += clearCoat; #endif #endif - return (color * light.colorIntensity.rgb) * (light.colorIntensity.w * light.attenuation * NoL * occlusion); } From db9183a105ad985188b4a48bd2587dc4948421cb Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Fri, 21 Jun 2024 17:37:01 +0000 Subject: [PATCH 06/11] Fix memory leak of asset loading for gltf_viewer (#7933) When a user drags and drops a gltf file to the gltf_viewer window, it loads the new asset. While this happens, the function `checkAsset` tries parsing the gltf file, but it doesn't free it. Fix this. --- samples/gltf_viewer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index 6b83316f1b..340391ec26 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -547,10 +547,11 @@ static bool checkAsset(const utils::Path& filename) { return false; } - // Parse the glTF file and create Filament entities. + // Try parsing the glTF file to check the validity of the file format. cgltf_options options{}; - cgltf_data* sourceAsset; + cgltf_data* sourceAsset = nullptr; cgltf_result result = cgltf_parse(&options, buffer.data(), contentSize, &sourceAsset); + cgltf_free(sourceAsset); if (result != cgltf_result_success) { slog.e << "Unable to parse glTF file." << io::endl; return false; From 2c1044e8127ae5f0eabc5adad3d855822b66ff9c Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 24 Jun 2024 11:00:58 -0700 Subject: [PATCH 07/11] Fix use-after-free of a std::mutex in PresentCallable. (#7934) If a user is using `setFrameScheduledCallback()`, managing the provided PresentCallable during engine shutdown is tricky -- we'll likely get a final frame scheduled when we flush the engine's work queue, but the PresentCallable will schedule the final CAMetalDrawable to be released on main thread afterwards, even if we call `present(false)` to skip it. If the swap chain is destroyed before that main queue block gets executed, the mutex presenting that drawable will no longer exist, causing a crash. To make things easier, store the std::mutex in a shared_ptr, so that a PresentCallable can safely outlive the FSwapChain instance that created it and clean itself up afterwards. Alternatives considered, all of which seem unfortunate: * Require users to clear out the callback before shutting down the engine, so that any final drawables are immediately discarded instead of using PresentCallable * Require users to split up the engine teardown across two main queue blocks, ensuring that the PresentCallable cleanup executes before swapchains are destroyed * Drop the PresentCallable on the ground and leak the memory --- filament/backend/src/metal/MetalHandles.h | 2 +- filament/backend/src/metal/MetalHandles.mm | 23 ++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 4d6f864639..4fba4724ea 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -109,7 +109,7 @@ private: NSUInteger headlessWidth = 0; NSUInteger headlessHeight = 0; CAMetalLayer* layer = nullptr; - std::mutex layerDrawableMutex; + std::shared_ptr layerDrawableMutex; MetalExternalImage externalImage; SwapChainType type; diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 3d56956387..c54b978217 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -73,6 +73,7 @@ MetalSwapChain::MetalSwapChain(MetalContext& context, CAMetalLayer* nativeWindow : context(context), depthStencilFormat(decideDepthStencilFormat(flags)), layer(nativeWindow), + layerDrawableMutex(std::make_shared()), externalImage(context), type(SwapChainType::CAMETALLAYER) { @@ -179,7 +180,7 @@ id MetalSwapChain::acquireDrawable() { // calling -nextDrawable, or when releasing the last known reference // to any CAMetalDrawable returned from a previous -nextDrawable. { - std::lock_guard lock(layerDrawableMutex); + std::lock_guard lock(*layerDrawableMutex); drawable = [layer nextDrawable]; } @@ -188,8 +189,10 @@ id MetalSwapChain::acquireDrawable() { } void MetalSwapChain::releaseDrawable() { - std::lock_guard lock(layerDrawableMutex); - drawable = nil; + if (drawable) { + std::lock_guard lock(*layerDrawableMutex); + drawable = nil; + } } id MetalSwapChain::acquireDepthTexture() { @@ -265,7 +268,7 @@ public: PresentDrawableData& operator=(const PresentDrawableData&) = delete; static PresentDrawableData* create(id drawable, - std::mutex* drawableMutex, MetalDriver* driver) { + std::shared_ptr drawableMutex, MetalDriver* driver) { assert_invariant(drawableMutex); assert_invariant(driver); return new PresentDrawableData(drawable, drawableMutex, driver); @@ -287,22 +290,22 @@ public: } private: - PresentDrawableData(id drawable, std::mutex* drawableMutex, + PresentDrawableData(id drawable, std::shared_ptr drawableMutex, MetalDriver* driver) : mDrawable(drawable), mDrawableMutex(drawableMutex), mDriver(driver) {} static void cleanupAndDestroy(PresentDrawableData *that) { - { + if (that->mDrawable) { std::lock_guard lock(*(that->mDrawableMutex)); that->mDrawable = nil; } - that->mDrawableMutex = nullptr; + that->mDrawableMutex.reset(); that->mDriver = nullptr; delete that; } id mDrawable; - std::mutex* mDrawableMutex = nullptr; + std::shared_ptr mDrawableMutex; MetalDriver* mDriver = nullptr; }; @@ -320,7 +323,7 @@ void MetalSwapChain::scheduleFrameScheduledCallback() { struct Callback { Callback(std::shared_ptr callback, id drawable, - std::mutex* drawableMutex, MetalDriver* driver) + std::shared_ptr drawableMutex, MetalDriver* driver) : f(callback), data(PresentDrawableData::create(drawable, drawableMutex, driver)) {} std::shared_ptr f; // PresentDrawableData* is destroyed by maybePresentAndDestroyAsync() later. @@ -337,7 +340,7 @@ void MetalSwapChain::scheduleFrameScheduledCallback() { // This callback pointer will be captured by the block. Even if the scheduled handler is never // called, the unique_ptr will still ensure we don't leak memory. __block auto callback = std::make_unique( - frameScheduled.callback, drawable, &layerDrawableMutex, context.driver); + frameScheduled.callback, drawable, layerDrawableMutex, context.driver); backend::CallbackHandler* handler = frameScheduled.handler; MetalDriver* driver = context.driver; From fe743523bf9ce15010f7cbf60d8ede152c8cf217 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 21 Jun 2024 22:04:46 -0700 Subject: [PATCH 08/11] fix use-after-free dereference risk Texture handles were resolved to pointers when updating a SamplerGroup, as that point the handle was checked for use-after-free. However, the texture could be destroyed later while still active in the SamplerGroup, this would result in using the pointer which now contains garbage. We now keep the handle and resolve the texture when binding samplers to the program; which will also perform the use-after-free check. --- .../include/private/backend/HandleAllocator.h | 12 ++++++++++++ filament/backend/src/opengl/OpenGLDriver.cpp | 8 ++++---- filament/backend/src/opengl/OpenGLDriver.h | 7 ++++++- filament/backend/src/opengl/OpenGLProgram.cpp | 14 +++++++++----- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/filament/backend/include/private/backend/HandleAllocator.h b/filament/backend/include/private/backend/HandleAllocator.h index afc6dc924e..d083fe3da9 100644 --- a/filament/backend/include/private/backend/HandleAllocator.h +++ b/filament/backend/include/private/backend/HandleAllocator.h @@ -181,6 +181,18 @@ public: return static_cast(p); } + template + bool is_valid(Handle& handle) { + if (handle && isPoolHandle(handle.getId())) { + auto [p, tag] = handleToPointer(handle.getId()); + uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT; + auto const pNode = static_cast(p); + uint8_t const expectedAge = pNode[-1].age; + return expectedAge == age; + } + return true; + } + template inline typename std::enable_if_t< std::is_pointer_v && diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 4f88c9717f..5b011ee625 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -2336,9 +2336,9 @@ void OpenGLDriver::updateSamplerGroup(Handle sbh, auto const* const pSamplers = (SamplerDescriptor const*)data.buffer; for (size_t i = 0, c = sb->textureUnitEntries.size(); i < c; i++) { GLuint samplerId = 0u; - const GLTexture* t = nullptr; - if (UTILS_LIKELY(pSamplers[i].t)) { - t = handle_cast(pSamplers[i].t); + Handle th = pSamplers[i].t; + if (UTILS_LIKELY(th)) { + GLTexture const* const t = handle_cast(th); assert_invariant(t); SamplerParams params = pSamplers[i].s; @@ -2394,7 +2394,7 @@ void OpenGLDriver::updateSamplerGroup(Handle sbh, // which is not an error. } - sb->textureUnitEntries[i] = { t, samplerId }; + sb->textureUnitEntries[i] = { th, samplerId }; } scheduleDestroy(std::move(data)); } diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h index d5e1b797b6..5345d474bf 100644 --- a/filament/backend/src/opengl/OpenGLDriver.h +++ b/filament/backend/src/opengl/OpenGLDriver.h @@ -126,7 +126,7 @@ public: struct GLSamplerGroup : public HwSamplerGroup { using HwSamplerGroup::HwSamplerGroup; struct Entry { - GLTexture const* texture = nullptr; + Handle th; GLuint sampler = 0u; }; utils::FixedCapacityVector textureUnitEntries; @@ -256,6 +256,11 @@ private: return mHandleAllocator.handle_cast(handle); } + template + bool is_valid(Handle& handle) { + return mHandleAllocator.is_valid(handle); + } + template inline typename std::enable_if_t< std::is_pointer_v && diff --git a/filament/backend/src/opengl/OpenGLProgram.cpp b/filament/backend/src/opengl/OpenGLProgram.cpp index 6c5eab9a44..7a85e3c63c 100644 --- a/filament/backend/src/opengl/OpenGLProgram.cpp +++ b/filament/backend/src/opengl/OpenGLProgram.cpp @@ -20,21 +20,24 @@ #include "OpenGLDriver.h" #include "ShaderCompilerService.h" +#include #include +#include -#include - -#include #include +#include +#include #include #include +#include #include #include #include #include #include +#include namespace filament::backend { @@ -241,8 +244,9 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept { assert_invariant(sb); if (!sb) continue; // should never happen, this would be a user error. for (uint8_t j = 0, m = sb->textureUnitEntries.size(); j < m; ++j, ++tmu) { // "<=" on purpose here - const GLTexture* const t = sb->textureUnitEntries[j].texture; - if (t) { // program may not use all samplers of sampler group + Handle th = sb->textureUnitEntries[j].th; + if (th) { // program may not use all samplers of sampler group + GLTexture const* const t = gld->handle_cast(th); gld->bindTexture(tmu, t); #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 if (UTILS_LIKELY(!es2)) { From c73f4e64d51cce2e75d23b951826163569aed826 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Tue, 25 Jun 2024 11:45:45 -0700 Subject: [PATCH 09/11] Metal: use a bump allocator for vertex and index uploads (#7926) --- filament/backend/include/backend/Platform.h | 2 + filament/backend/src/metal/MetalBuffer.h | 9 +++ filament/backend/src/metal/MetalBuffer.mm | 81 +++++++++++++------ filament/backend/src/metal/MetalBufferPool.h | 22 +++++ filament/backend/src/metal/MetalBufferPool.mm | 33 ++++++++ filament/backend/src/metal/MetalContext.h | 2 + filament/backend/src/metal/MetalDriver.mm | 3 + filament/include/filament/Engine.h | 18 +++++ filament/src/details/Engine.cpp | 2 + 9 files changed, 149 insertions(+), 23 deletions(-) diff --git a/filament/backend/include/backend/Platform.h b/filament/backend/include/backend/Platform.h index 4f73c4375c..b8befb4cee 100644 --- a/filament/backend/include/backend/Platform.h +++ b/filament/backend/include/backend/Platform.h @@ -75,6 +75,8 @@ public: */ size_t textureUseAfterFreePoolSize = 0; + size_t metalUploadBufferSizeBytes = 512 * 1024; + /** * Set to `true` to forcibly disable parallel shader compilation in the backend. * Currently only honored by the GL and Metal backends. diff --git a/filament/backend/src/metal/MetalBuffer.h b/filament/backend/src/metal/MetalBuffer.h index 081ca6c3fd..bb3de6d27c 100644 --- a/filament/backend/src/metal/MetalBuffer.h +++ b/filament/backend/src/metal/MetalBuffer.h @@ -204,6 +204,15 @@ public: private: + enum class UploadStrategy { + POOL, + BUMP_ALLOCATOR, + }; + + void uploadWithPoolBuffer(void* src, size_t size, size_t byteOffset) const; + void uploadWithBumpAllocator(void* src, size_t size, size_t byteOffset) const; + + UploadStrategy mUploadStrategy; TrackedMetalBuffer mBuffer; size_t mBufferSize = 0; void* mCpuBuffer = nullptr; diff --git a/filament/backend/src/metal/MetalBuffer.mm b/filament/backend/src/metal/MetalBuffer.mm index 83fe25d598..6070cf47fb 100644 --- a/filament/backend/src/metal/MetalBuffer.mm +++ b/filament/backend/src/metal/MetalBuffer.mm @@ -27,7 +27,16 @@ MetalPlatform* TrackedMetalBuffer::platform = nullptr; MetalPlatform* ScopedAllocationTimer::platform = nullptr; MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage, - size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) { + size_t size, bool forceGpuBuffer) + : mBufferSize(size), mContext(context) { + const MetalBumpAllocator& allocator = *mContext.bumpAllocator; + // VERTEX is also used for index buffers + if (allocator.getCapacity() > 0 && bindingType == BufferObjectBinding::VERTEX) { + mUploadStrategy = UploadStrategy::BUMP_ALLOCATOR; + } else { + mUploadStrategy = UploadStrategy::POOL; + } + // If the buffer is less than 4K in size and is updated frequently, we don't use an explicit // buffer. Instead, we use immediate command encoder methods like setVertexBytes:length:atIndex:. // This won't work for SSBOs, since they are read/write. @@ -61,34 +70,23 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) { FILAMENT_CHECK_PRECONDITION(size + byteOffset <= mBufferSize) << "Attempting to copy " << size << " bytes into a buffer of size " << mBufferSize << " at offset " << byteOffset; + // The copy blit requires that byteOffset be a multiple of 4. + FILAMENT_CHECK_PRECONDITION(!(byteOffset & 0x3)) << "byteOffset must be a multiple of 4"; - // Either copy into the Metal buffer or into our cpu buffer. + // If we have a cpu buffer, we can directly copy into it. if (mCpuBuffer) { memcpy(static_cast(mCpuBuffer) + byteOffset, src, size); return; } - // Acquire a staging buffer to hold the contents of this update. - MetalBufferPool* bufferPool = mContext.bufferPool; - const MetalBufferPoolEntry* const staging = bufferPool->acquireBuffer(size); - memcpy(staging->buffer.get().contents, src, size); - - // The blit below requires that byteOffset be a multiple of 4. - FILAMENT_CHECK_PRECONDITION(!(byteOffset & 0x3u)) << "byteOffset must be a multiple of 4"; - - // Encode a blit from the staging buffer into the private GPU buffer. - id cmdBuffer = getPendingCommandBuffer(&mContext); - id blitEncoder = [cmdBuffer blitCommandEncoder]; - blitEncoder.label = @"Buffer upload blit"; - [blitEncoder copyFromBuffer:staging->buffer.get() - sourceOffset:0 - toBuffer:mBuffer.get() - destinationOffset:byteOffset - size:size]; - [blitEncoder endEncoding]; - [cmdBuffer addCompletedHandler:^(id cb) { - bufferPool->releaseBuffer(staging); - }]; + switch (mUploadStrategy) { + case UploadStrategy::BUMP_ALLOCATOR: + uploadWithBumpAllocator(src, size, byteOffset); + break; + case UploadStrategy::POOL: + uploadWithPoolBuffer(src, size, byteOffset); + break; + } } void MetalBuffer::copyIntoBufferUnsynchronized(void* src, size_t size, size_t byteOffset) { @@ -199,5 +197,42 @@ void MetalBuffer::bindBuffers(id cmdBuffer, idacquireBuffer(size); + memcpy(staging->buffer.get().contents, src, size); + + // Encode a blit from the staging buffer into the private GPU buffer. + id cmdBuffer = getPendingCommandBuffer(&mContext); + id blitEncoder = [cmdBuffer blitCommandEncoder]; + blitEncoder.label = @"Buffer upload blit - pool buffer"; + [blitEncoder copyFromBuffer:staging->buffer.get() + sourceOffset:0 + toBuffer:mBuffer.get() + destinationOffset:byteOffset + size:size]; + [blitEncoder endEncoding]; + [cmdBuffer addCompletedHandler:^(id cb) { + bufferPool->releaseBuffer(staging); + }]; +} + +void MetalBuffer::uploadWithBumpAllocator(void* src, size_t size, size_t byteOffset) const { + MetalBumpAllocator& allocator = *mContext.bumpAllocator; + auto [buffer, offset] = allocator.allocateStagingArea(size); + memcpy(static_cast(buffer.contents) + offset, src, size); + + // Encode a blit from the staging buffer into the private GPU buffer. + id cmdBuffer = getPendingCommandBuffer(&mContext); + id blitEncoder = [cmdBuffer blitCommandEncoder]; + blitEncoder.label = @"Buffer upload blit - bump allocator"; + [blitEncoder copyFromBuffer:buffer + sourceOffset:offset + toBuffer:mBuffer.get() + destinationOffset:byteOffset + size:size]; + [blitEncoder endEncoding]; +} + } // namespace backend } // namespace filament diff --git a/filament/backend/src/metal/MetalBufferPool.h b/filament/backend/src/metal/MetalBufferPool.h index 03688ab3c4..770527e092 100644 --- a/filament/backend/src/metal/MetalBufferPool.h +++ b/filament/backend/src/metal/MetalBufferPool.h @@ -38,6 +38,28 @@ struct MetalBufferPoolEntry { mutable uint32_t referenceCount; }; +class MetalBumpAllocator { +public: + MetalBumpAllocator(id device, size_t capacity); + + /** + * Allocates a staging area of the given size. Returns a pair of the buffer and the offset + * within the buffer. The buffer is guaranteed to be at least the given size, but may be larger. + * Clients must not write to the buffer beyond the returned offset + size. + * Clients are responsible for holding a reference to the returned buffer. + * Allocations are guaranteed to be aligned to 4 bytes. + */ + std::pair, size_t> allocateStagingArea(size_t size); + + size_t getCapacity() const noexcept { return mCapacity; } + +private: + id mDevice; + TrackedMetalBuffer mCurrentUploadBuffer = nil; + size_t mHead = 0; + size_t mCapacity; +}; + // Manages a pool of Metal buffers, periodically releasing ones that have been unused for awhile. class MetalBufferPool { public: diff --git a/filament/backend/src/metal/MetalBufferPool.mm b/filament/backend/src/metal/MetalBufferPool.mm index c9a80e8352..a601a85052 100644 --- a/filament/backend/src/metal/MetalBufferPool.mm +++ b/filament/backend/src/metal/MetalBufferPool.mm @@ -116,5 +116,38 @@ void MetalBufferPool::reset() noexcept { mFreeStages.clear(); } +MetalBumpAllocator::MetalBumpAllocator(id device, size_t capacity) + : mDevice(device), mCapacity(capacity) { + if (mCapacity > 0) { + mCurrentUploadBuffer = { [device newBufferWithLength:capacity options:MTLStorageModeShared], + TrackedMetalBuffer::Type::STAGING }; + } +} + +std::pair, size_t> MetalBumpAllocator::allocateStagingArea(size_t size) { + if (size == 0) { + return { nil, 0 }; + } + if (size > mCapacity) { + return { [mDevice newBufferWithLength:size options:MTLStorageModeShared], 0 }; + } + assert_invariant(mCurrentUploadBuffer); + + // Align the head to a 4-byte boundary. + mHead = (mHead + 3) & ~3; + + if (UTILS_LIKELY(mHead + size <= mCapacity)) { + const size_t oldHead = mHead; + mHead += size; + return { mCurrentUploadBuffer.get(), oldHead }; + } + + // We're finished with the current allocation. + mCurrentUploadBuffer = { [mDevice newBufferWithLength:mCapacity options:MTLStorageModeShared], + TrackedMetalBuffer::Type::STAGING }; + + return { mCurrentUploadBuffer.get(), 0 }; +} + } // namespace backend } // namespace filament diff --git a/filament/backend/src/metal/MetalContext.h b/filament/backend/src/metal/MetalContext.h index 8a14d9a36c..0f920466e9 100644 --- a/filament/backend/src/metal/MetalContext.h +++ b/filament/backend/src/metal/MetalContext.h @@ -44,6 +44,7 @@ namespace backend { class MetalDriver; class MetalBlitter; class MetalBufferPool; +class MetalBumpAllocator; class MetalRenderTarget; class MetalSamplerGroup; class MetalSwapChain; @@ -141,6 +142,7 @@ struct MetalContext { utils::FixedCircularBuffer> texturesToDestroy; MetalBufferPool* bufferPool; + MetalBumpAllocator* bumpAllocator; MetalSwapChain* currentDrawSwapChain = nil; MetalSwapChain* currentReadSwapChain = nil; diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 58852a6714..92b1b22f73 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -171,6 +171,8 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig& mContext->samplerStateCache.setDevice(mContext->device); mContext->argumentEncoderCache.setDevice(mContext->device); mContext->bufferPool = new MetalBufferPool(*mContext); + mContext->bumpAllocator = + new MetalBumpAllocator(mContext->device, driverConfig.metalUploadBufferSizeBytes); mContext->blitter = new MetalBlitter(*mContext); if (@available(iOS 12, *)) { @@ -209,6 +211,7 @@ MetalDriver::~MetalDriver() noexcept { mContext->emptyTexture = nil; CFRelease(mContext->textureCache); delete mContext->bufferPool; + delete mContext->bumpAllocator; delete mContext->blitter; delete mContext->timerQueryImpl; delete mContext->shaderCompiler; diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 1c44fe9ab9..6c3d87c29c 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -298,6 +298,24 @@ public: */ size_t textureUseAfterFreePoolSize = 0; + /** + * When uploading vertex or index data, the Filament Metal backend copies data + * into a shared staging area before transferring it to the GPU. This setting controls + * the total size of the buffer used to perform these allocations. + * + * Higher values can improve performance when performing many uploads across a small + * number of frames. + * + * This buffer remains alive throughout the lifetime of the Engine, so this size adds to the + * memory footprint of the app and should be set as conservative as possible. + * + * A value of 0 disables the shared staging buffer entirely; uploads will acquire an + * individual buffer from a pool of shared buffers. + * + * Only respected by the Metal backend. + */ + size_t metalUploadBufferSizeBytes = 512 * 1024; + /** * Set to `true` to forcibly disable parallel shader compilation in the backend. * Currently only honored by the GL and Metal backends. diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index 2547b40103..72b5b599c3 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -102,6 +102,7 @@ Engine* FEngine::create(Engine::Builder const& builder) { DriverConfig const driverConfig{ .handleArenaSize = instance->getRequestedDriverHandleArenaSize(), .textureUseAfterFreePoolSize = instance->getConfig().textureUseAfterFreePoolSize, + .metalUploadBufferSizeBytes = instance->getConfig().metalUploadBufferSizeBytes, .disableParallelShaderCompile = instance->getConfig().disableParallelShaderCompile, .disableHandleUseAfterFreeCheck = instance->getConfig().disableHandleUseAfterFreeCheck, .forceGLES2Context = instance->getConfig().forceGLES2Context, @@ -678,6 +679,7 @@ int FEngine::loop() { DriverConfig const driverConfig { .handleArenaSize = getRequestedDriverHandleArenaSize(), .textureUseAfterFreePoolSize = mConfig.textureUseAfterFreePoolSize, + .metalUploadBufferSizeBytes = mConfig.metalUploadBufferSizeBytes, .disableParallelShaderCompile = mConfig.disableParallelShaderCompile, .disableHandleUseAfterFreeCheck = mConfig.disableHandleUseAfterFreeCheck, .forceGLES2Context = mConfig.forceGLES2Context, From 659b8b6e035fd25178d8310c3f0ffa7d40319318 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Tue, 25 Jun 2024 11:46:45 -0700 Subject: [PATCH 10/11] Release Filament 1.53.1 --- README.md | 4 ++-- RELEASE_NOTES.md | 3 +++ android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 01144d1219..b368b37c95 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.53.0' + implementation 'com.google.android.filament:filament-android:1.53.1' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.53.0' +pod 'Filament', '~> 1.53.1' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 2a4ed29bcd..3591acf61e 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,9 @@ A new header is inserted each time a *tag* is created. Instead, if you are authoring a PR for the main branch, add your release note to [NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md). +## v1.53.2 + + ## v1.53.1 diff --git a/android/gradle.properties b/android/gradle.properties index 3e27e61499..cca037f8f7 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.53.0 +VERSION_NAME=1.53.1 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 45fce39806..fc42e6ee98 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.53.0" + spec.version = "1.53.1" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.53.0/filament-v1.53.0-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.53.1/filament-v1.53.1-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/web/filament-js/package.json b/web/filament-js/package.json index 56b18b1cf4..7cea353927 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.53.0", + "version": "1.53.1", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From a2fe9f745cd23739ec0a915ba4599f172981b441 Mon Sep 17 00:00:00 2001 From: Tibor Hencz <39774620+mentholyspirit@users.noreply.github.com> Date: Thu, 27 Jun 2024 03:05:14 +0300 Subject: [PATCH 11/11] Add ability to disable panning in the OrbitManipulator (#7928) --- .../src/main/cpp/Manipulator.cpp | 5 +++++ .../google/android/filament/utils/Manipulator.java | 12 ++++++++++++ libs/camutils/include/camutils/Manipulator.h | 3 +++ libs/camutils/src/Manipulator.cpp | 8 ++++++++ libs/camutils/src/OrbitManipulator.h | 2 +- 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/android/filament-utils-android/src/main/cpp/Manipulator.cpp b/android/filament-utils-android/src/main/cpp/Manipulator.cpp index 22d471def2..84ac111e75 100644 --- a/android/filament-utils-android/src/main/cpp/Manipulator.cpp +++ b/android/filament-utils-android/src/main/cpp/Manipulator.cpp @@ -125,6 +125,11 @@ extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBu builder->groundPlane(a, b, c, d); } +extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderPanning(JNIEnv*, jclass, jlong nativeBuilder, jboolean enabled) { + Builder* builder = (Builder*) nativeBuilder; + builder->panning(enabled); +} + extern "C" JNIEXPORT long Java_com_google_android_filament_utils_Manipulator_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder, jint mode) { Builder* builder = (Builder*) nativeBuilder; return (jlong) builder->build((Mode) mode); diff --git a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/Manipulator.java b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/Manipulator.java index 2c0ab85a14..548a6e6f8c 100644 --- a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/Manipulator.java +++ b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/Manipulator.java @@ -274,6 +274,17 @@ public class Manipulator { return this; } + /** + * Sets whether panning is enabled in the manipulator. + * + * @return this Builder object for chaining calls + */ + @NonNull + public Builder panning(Boolean enabled) { + nBuilderPanning(mNativeBuilder, enabled); + return this; + } + /** * Creates and returns the Manipulator object. * @@ -483,6 +494,7 @@ public class Manipulator { private static native void nBuilderFlightPanSpeed(long nativeBuilder, float x, float y); private static native void nBuilderFlightMoveDamping(long nativeBuilder, float damping); private static native void nBuilderGroundPlane(long nativeBuilder, float a, float b, float c, float d); + private static native void nBuilderPanning(long nativeBuilder, Boolean enabled); private static native long nBuilderBuild(long nativeBuilder, int mode); private static native void nDestroyManipulator(long nativeManip); diff --git a/libs/camutils/include/camutils/Manipulator.h b/libs/camutils/include/camutils/Manipulator.h index 2cc8a4da03..5ea49eaf5a 100644 --- a/libs/camutils/include/camutils/Manipulator.h +++ b/libs/camutils/include/camutils/Manipulator.h @@ -109,6 +109,7 @@ public: vec4 groundPlane; RayCallback raycastCallback; void* raycastUserdata; + bool panning = true; }; struct Builder { @@ -143,6 +144,8 @@ public: Builder& groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d); //! Plane equation used as a raycast fallback Builder& raycastCallback(RayCallback cb, void* userdata); //! Raycast function for accurate grab-and-pan + Builder& panning(bool enabled); //! Sets whether panning is enabled + /** * Creates a new camera manipulator, either ORBIT, MAP, or FREE_FLIGHT. * diff --git a/libs/camutils/src/Manipulator.cpp b/libs/camutils/src/Manipulator.cpp index d44528cd50..897d75c6d1 100644 --- a/libs/camutils/src/Manipulator.cpp +++ b/libs/camutils/src/Manipulator.cpp @@ -144,6 +144,14 @@ Manipulator::Builder& Manipulator::Builder::raycastCallback(RayCal return *this; } + + +template typename +Manipulator::Builder& Manipulator::Builder::panning(bool enabled) { + details.panning = enabled; + return *this; +} + template Manipulator* Manipulator::Builder::build(Mode mode) { switch (mode) { diff --git a/libs/camutils/src/OrbitManipulator.h b/libs/camutils/src/OrbitManipulator.h index 54e325cc3b..f89b7ada03 100644 --- a/libs/camutils/src/OrbitManipulator.h +++ b/libs/camutils/src/OrbitManipulator.h @@ -69,7 +69,7 @@ public: } void grabBegin(int x, int y, bool strafe) override { - mGrabState = strafe ? PANNING : ORBITING; + mGrabState = strafe && Base::mProps.panning ? PANNING : ORBITING; mGrabPivot = mPivot; mGrabEye = Base::mEye; mGrabTarget = Base::mTarget;