From 6fd5d45295e6033035f84861685f5c2c139ba612 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 1 Sep 2023 15:51:55 -0700 Subject: [PATCH 01/19] fix shadow stability when eye is far from world origin in stable mode the scale was ever so slightly varying with the camera position, because it was calculated from the camera frustum in world-space, this variation was amplified when the camera is far from the origin, which eventually caused the modulo needed for snapping the shadowmap projection to widely vary, leading to the instability. We now calculate the camera frustum sphere in view space, which is guaranteed to be constant. If "shadow caster mode" is chosen, we quantize the scale a little bit so it stays constant. The snapping code itself has been cleaned. --- filament/src/ShadowMap.cpp | 125 ++++++++++++++++++--------- filament/src/ShadowMap.h | 2 +- filament/src/ShadowMapManager.cpp | 12 +-- filament/src/ShadowMapManager.h | 16 +--- libs/math/include/math/TVecHelpers.h | 31 +++++++ 5 files changed, 120 insertions(+), 66 deletions(-) diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index 585300e097..273855e528 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -110,7 +110,7 @@ math::mat4f ShadowMap::getPointLightViewMatrix(backend::TextureCubemapFace face, } ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, - const FScene::LightSoa& lightData, size_t index, + FScene::LightSoa const& lightData, size_t index, filament::CameraInfo const& camera, ShadowMapInfo const& shadowMapInfo, SceneInfo const& sceneInfo) noexcept { @@ -216,7 +216,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, } // Now that we know the znear (-lsLightFrustumBounds.max.z), adjust the light's position such - // that znear = 0, this is only need for VSM, but doesn't hurt PCF. + // that znear = 0, this is only needed for VSM, but doesn't hurt PCF. const mat4f Mv = getDirectionalLightViewMatrix(direction, direction * -lsLightFrustumBounds.max.z); // near / far planes are specified relative to the direction the eye is looking at @@ -240,8 +240,11 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, const float4 shadowReceiverVolumeBoundingSphere = computeBoundingSphere( wsShadowReceiversVolume.getCorners().data(), 8); - // in stable mode we simply take the view volume, bounding sphere - viewVolumeBoundingSphere = computeBoundingSphere(wsViewFrustumVertices, 8); + // in stable mode we simply take the view volume bounding sphere, but we calculate it + // in view space, so that it's perfectly stable. + float3 vertices[8]; + computeFrustumCorners(vertices, inverse(cullingProjection), sceneInfo.csNearFar); + viewVolumeBoundingSphere = computeBoundingSphere(vertices, 8); if (shadowReceiverVolumeBoundingSphere.w < viewVolumeBoundingSphere.w) { @@ -320,45 +323,65 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, // // In LiPSM mode, we're using the warped space here. - Aabb bounds; - if (params.options.stable && viewVolumeBoundingSphere.w > 0) { - bounds = compute2DBounds(Mv, viewVolumeBoundingSphere); - } else { - bounds = compute2DBounds(WLMpMv, wsClippedShadowReceiverVolume.data(), vertexCount); - } - lsLightFrustumBounds.min.xy = bounds.min.xy; - lsLightFrustumBounds.max.xy = bounds.max.xy; - + float2 s, o; if (params.options.stable) { - // in stable mode we can't do anything that can change the scaling of the texture + if (viewVolumeBoundingSphere.w > 0) { + s = 1.0f / viewVolumeBoundingSphere.w; + o = mat4f::project(Mv * camera.model, viewVolumeBoundingSphere.xyz).xy; + } else { + Aabb const bounds = compute2DBounds(Mv, + wsClippedShadowReceiverVolume.data(), vertexCount); + if (UTILS_UNLIKELY((bounds.min.x >= bounds.max.x) || (bounds.min.y >= bounds.max.y))) { + // this could happen if the only thing visible is a perfectly horizontal or + // vertical thin line + mHasVisibleShadows = false; + return {}; + } + assert_invariant(bounds.min.x < bounds.max.x); + assert_invariant(bounds.min.y < bounds.max.y); + + s = 2.0f / float2(bounds.max.xy - bounds.min.xy); + o = float2(bounds.max.xy + bounds.min.xy) * 0.5f; + + // Quantize the scale in world-space units. This value can be very small because + // if it wasn't for floating-point imprecision, the scale would be a constant. + double2 const quantizer = 0.0625; + s = 1.0 / (ceil(1.0 / (s * quantizer)) * quantizer); + } } else { + Aabb const bounds = compute2DBounds(WLMpMv, + wsClippedShadowReceiverVolume.data(), vertexCount); + lsLightFrustumBounds.min.xy = bounds.min.xy; + lsLightFrustumBounds.max.xy = bounds.max.xy; // For directional lights, we further constraint the light frustum to the // intersection of the shadow casters & shadow receivers in light-space. // ** This relies on the 1-texel shadow map border ** if (engine.debug.shadowmap.focus_shadowcasters) { intersectWithShadowCasters(lsLightFrustumBounds, WLMpMv, wsShadowCastersVolume); } + if (UTILS_UNLIKELY((lsLightFrustumBounds.min.x >= lsLightFrustumBounds.max.x) || + (lsLightFrustumBounds.min.y >= lsLightFrustumBounds.max.y))) { + // this could happen if the only thing visible is a perfectly horizontal or + // vertical thin line + mHasVisibleShadows = false; + return {}; + } + assert_invariant(lsLightFrustumBounds.min.x < lsLightFrustumBounds.max.x); + assert_invariant(lsLightFrustumBounds.min.y < lsLightFrustumBounds.max.y); + + s = 2.0f / float2(bounds.max.xy - bounds.min.xy); + o = float2(bounds.max.xy + bounds.min.xy) * 0.5f; + + // TODO: we could quantize `s` here to give some stability when lispsm is disabled, + // however, the quantization paramater should probably be user settable. } - if (UTILS_UNLIKELY((lsLightFrustumBounds.min.x >= lsLightFrustumBounds.max.x) || - (lsLightFrustumBounds.min.y >= lsLightFrustumBounds.max.y))) { - // this could happen if the only thing visible is a perfectly horizontal or - // vertical thin line - mHasVisibleShadows = false; - return {}; - } + // adjust offset for scale + o = -s * o; - assert_invariant(lsLightFrustumBounds.min.x < lsLightFrustumBounds.max.x); - assert_invariant(lsLightFrustumBounds.min.y < lsLightFrustumBounds.max.y); - - // compute focus scale and offset - float2 s = 2.0f / float2(lsLightFrustumBounds.max.xy - lsLightFrustumBounds.min.xy); - float2 o = -s * float2(lsLightFrustumBounds.max.xy + lsLightFrustumBounds.min.xy) * 0.5f; - - if (params.options.stable) { - // Use the world origin as reference point, fixed w.r.t. the camera - snapLightFrustum(s, o, Mv, camera.worldOrigin[3].xyz, - 1.0f / float(shadowMapInfo.shadowDimension)); + if (!useLispsm) { + // stabilize the shadowmap in all modes, except lispsm which can never be stable + snapLightFrustum(s, o, Mv, camera.worldOrigin, shadowMapInfo.shadowDimension); } const mat4f F(mat4f::row_major_init { @@ -825,21 +848,39 @@ void ShadowMap::computeFrustumCorners(float3* UTILS_RESTRICT out, } void ShadowMap::snapLightFrustum(float2& s, float2& o, - mat4f const& Mv, float3 worldOrigin, float2 shadowMapResolution) noexcept { + mat4f const& Mv, mat4 worldOrigin, int2 resolution) noexcept { - auto fmod = [](float2 x, float2 y) -> float2 { - auto mod = [](float x, float y) -> float { return std::fmod(x, y); }; - return float2{ mod(x[0], y[0]), mod(x[1], y[1]) }; + auto proj = [](mat4 m, double4 v) -> double3 { + // for directional light p.w == 1, exactly + auto p = m * v; + assert_invariant(p.w == 1.0); + return p.xyz; }; - // This snaps the shadow map bounds to texels. - // The 2.0 comes from Mv having a NDC in the range -1,1 (so a range of 2). - const float2 r = 2.0f * shadowMapResolution; - o -= fmod(o, r); + auto fract = [](auto v) { + using namespace std; + using T = decltype(v); + return fmod(v, T{1}); + }; + + const mat4 F(mat4::row_major_init { + s.x, 0.0, 0.0, o.x, + 0.0, s.y, 0.0, o.y, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0, + }); + + // The (resolution * 0.5) comes from Mv having a NDC in the range -1,1 (so a range of 2). + + // focused light-space + mat4 const FMv{ F * Mv }; // This offsets the texture coordinates, so it has a fixed offset w.r.t the world - const float2 lsOrigin = mat4f::project(Mv, worldOrigin).xy * s; - o -= fmod(lsOrigin, r); + double2 const lsOrigin = proj(FMv, worldOrigin[3]).xy; + double2 const d = (fract(lsOrigin * resolution * 0.5) * 2.0) / resolution; + + // adjust offset + o -= d; } size_t ShadowMap::intersectFrustumWithBox( diff --git a/filament/src/ShadowMap.h b/filament/src/ShadowMap.h index de198387c6..bbb0bf744d 100644 --- a/filament/src/ShadowMap.h +++ b/filament/src/ShadowMap.h @@ -222,7 +222,7 @@ private: const math::float3& dir); static inline void snapLightFrustum(math::float2& s, math::float2& o, - math::mat4f const& Mv, math::float3 worldOrigin, math::float2 shadowMapResolution) noexcept; + math::mat4f const& Mv, math::mat4 worldOrigin, math::int2 resolution) noexcept; static inline void computeFrustumCorners(math::float3* out, const math::mat4f& projectionViewInverse, math::float2 csNearFar = { -1.0f, 1.0f }) noexcept; diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index 4504292d2e..fbb4e744e8 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -504,19 +504,13 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng splitPercentages[i] = options.cascadeSplitPositions[i - 1]; } - const CascadeSplits::Params p{ + const CascadeSplits splits({ .proj = cameraInfo.cullingProjection, .near = vsNear, .far = vsFar, .cascadeCount = cascadeCount, .splitPositions = splitPercentages - }; - if (p != mCascadeSplitParams) { - mCascadeSplits = CascadeSplits{ p }; - mCascadeSplitParams = p; - } - - const CascadeSplits& splits = mCascadeSplits; + }); // The split positions uniform is a float4. To save space, we chop off the first split position // (which is the near plane, and doesn't need to be communicated to the shaders). @@ -530,7 +524,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform; - // when computing the required bias we need a half-texel size, so we multiply by 0.5 here. + // When computing the required bias we need a half-texel size, so we multiply by 0.5 here. // note: normalBias is set to zero for VSM const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0); diff --git a/filament/src/ShadowMapManager.h b/filament/src/ShadowMapManager.h index 1fb9602369..488e108e40 100644 --- a/filament/src/ShadowMapManager.h +++ b/filament/src/ShadowMapManager.h @@ -145,17 +145,8 @@ private: float far = 0.0f; size_t cascadeCount = 1; std::array splitPositions = { 0.0f }; - - bool operator!=(const Params& rhs) const { - return proj != rhs.proj || - near != rhs.near || - far != rhs.far || - cascadeCount != rhs.cascadeCount || - splitPositions != rhs.splitPositions; - } }; - CascadeSplits() noexcept : CascadeSplits(Params{}) {} explicit CascadeSplits(Params const& params) noexcept; // Split positions in world-space. @@ -186,9 +177,6 @@ private: SoftShadowOptions mSoftShadowOptions; - CascadeSplits::Params mCascadeSplitParams; - CascadeSplits mCascadeSplits; - mutable TypedUniformBuffer mShadowUb; backend::Handle mShadowUbh; @@ -204,8 +192,8 @@ private: utils::FixedCapacityVector::with_capacity( CONFIG_MAX_SHADOWMAPS - CONFIG_MAX_SHADOW_CASCADES) }; - // inline storage for all our ShadowMap objects, we can't easily use a std::array<> directly. - // because ShadowMap doesn't have a default ctor, and we avoid out-of-line allocations. + // Inline storage for all our ShadowMap objects, we can't easily use a std::array<> directly. + // Because ShadowMap doesn't have a default ctor, and we avoid out-of-line allocations. // Each ShadowMap is currently 40 bytes (total of 2.5KB for 64 shadow maps) using ShadowMapStorage = std::aligned_storage::type; std::array mShadowMapCache; diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h index a43c2a479f..fb7d026d6f 100644 --- a/libs/math/include/math/TVecHelpers.h +++ b/libs/math/include/math/TVecHelpers.h @@ -443,6 +443,37 @@ private: return v; } + template + friend inline + VECTOR MATH_PURE fmod(VECTOR const& x, VECTOR const& y) { + VECTOR r; + for (size_t i = 0; i < r.size(); i++) { + r[i] = std::fmod(x[i], y[i]); + } + return r; + } + + template + friend inline + VECTOR MATH_PURE remainder(VECTOR const& x, VECTOR const& y) { + VECTOR r; + for (size_t i = 0; i < r.size(); i++) { + r[i] = std::remainder(x[i], y[i]); + } + return r; + } + + template + friend inline + VECTOR MATH_PURE remquo(VECTOR const& x, VECTOR const& y, + VECTOR* q) { + VECTOR r; + for (size_t i = 0; i < r.size(); i++) { + r[i] = std::remquo(x[i], y[i], &((*q)[i])); + } + return r; + } + friend inline VECTOR MATH_PURE inversesqrt(VECTOR v) { for (size_t i = 0; i < v.size(); i++) { v[i] = T(1) / std::sqrt(v[i]); From 3ab7780c381724ab1dfa8a2bc8d87bd0c39381a7 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Fri, 8 Sep 2023 13:06:52 -0700 Subject: [PATCH 02/19] Fix incorrect SamplerParams operators (#7150) --- filament/backend/include/backend/DriverEnums.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index e25f18dafd..8598114a44 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -813,22 +813,22 @@ struct SamplerParams { // NOLINT struct Hasher { size_t operator()(SamplerParams p) const noexcept { // we don't use std::hash<> here, so we don't have to include - return *reinterpret_cast(reinterpret_cast(&p)); + return *reinterpret_cast(reinterpret_cast(&p)); } }; struct EqualTo { bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept { - auto* pLhs = reinterpret_cast(reinterpret_cast(&lhs)); - auto* pRhs = reinterpret_cast(reinterpret_cast(&rhs)); + auto* pLhs = reinterpret_cast(reinterpret_cast(&lhs)); + auto* pRhs = reinterpret_cast(reinterpret_cast(&rhs)); return *pLhs == *pRhs; } }; struct LessThan { bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept { - auto* pLhs = reinterpret_cast(reinterpret_cast(&lhs)); - auto* pRhs = reinterpret_cast(reinterpret_cast(&rhs)); + auto* pLhs = reinterpret_cast(reinterpret_cast(&lhs)); + auto* pRhs = reinterpret_cast(reinterpret_cast(&rhs)); return *pLhs == *pRhs; } }; @@ -838,6 +838,7 @@ private: return SamplerParams::LessThan{}(lhs, rhs); } }; +static_assert(sizeof(SamplerParams) == 4); // The limitation to 64-bits max comes from how we store a SamplerParams in our JNI code // see android/.../TextureSampler.cpp From d328b64dce9298387685a263bd5faa6314d33891 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Mon, 11 Sep 2023 12:58:29 -0700 Subject: [PATCH 03/19] Only call .put if the tick op was canceled (#7152) --- filament/backend/src/opengl/ShaderCompilerService.cpp | 8 +++++--- filament/backend/src/opengl/ShaderCompilerService.h | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/filament/backend/src/opengl/ShaderCompilerService.cpp b/filament/backend/src/opengl/ShaderCompilerService.cpp index 84ea9d8ead..ad6914d2f6 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.cpp +++ b/filament/backend/src/opengl/ShaderCompilerService.cpp @@ -341,7 +341,7 @@ GLuint ShaderCompilerService::getProgram(ShaderCompilerService::program_token_t& token->canceled = true; - token->compiler.cancelTickOp(token); + bool canceled = token->compiler.cancelTickOp(token); if (token->compiler.mShaderCompilerThreadCount) { auto job = token->compiler.mCompilerThreadPool.dequeue(token); @@ -354,7 +354,7 @@ GLuint ShaderCompilerService::getProgram(ShaderCompilerService::program_token_t& // order for future callbacks to be successfully called. token->compiler.mCallbackManager.put(token->handle); } - } else { + } else if (canceled) { // Since the tick op was canceled, we need to .put the token here. token->compiler.mCallbackManager.put(token->handle); } @@ -683,7 +683,7 @@ void ShaderCompilerService::runAtNextTick(CompilerPriorityQueue priority, SYSTRACE_VALUE32("ShaderCompilerService Jobs", mRunAtNextTickOps.size()); } -void ShaderCompilerService::cancelTickOp(program_token_t token) noexcept { +bool ShaderCompilerService::cancelTickOp(program_token_t token) noexcept { // We do a linear search here, but this is rare, and we know the list is pretty small. auto& ops = mRunAtNextTickOps; auto pos = std::find_if(ops.begin(), ops.end(), [&](const auto& item) { @@ -691,9 +691,11 @@ void ShaderCompilerService::cancelTickOp(program_token_t token) noexcept { }); if (pos != ops.end()) { ops.erase(pos); + return true; } SYSTRACE_CONTEXT(); SYSTRACE_VALUE32("ShaderCompilerService Jobs", ops.size()); + return false; } void ShaderCompilerService::executeTickOps() noexcept { diff --git a/filament/backend/src/opengl/ShaderCompilerService.h b/filament/backend/src/opengl/ShaderCompilerService.h index 0d8cb19192..bbce6a5c23 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.h +++ b/filament/backend/src/opengl/ShaderCompilerService.h @@ -141,7 +141,7 @@ private: void runAtNextTick(CompilerPriorityQueue priority, const program_token_t& token, Job job) noexcept; void executeTickOps() noexcept; - void cancelTickOp(program_token_t token) noexcept; + bool cancelTickOp(program_token_t token) noexcept; // order of insertion is important using ContainerType = std::tuple; From 2c63a5ad7a572f1354ffaa44753fede7fd607702 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Mon, 11 Sep 2023 13:40:02 -0700 Subject: [PATCH 04/19] Fix use of -Wno-deprecated-register flag and MSVC (#7156) --- libs/gltfio/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libs/gltfio/CMakeLists.txt b/libs/gltfio/CMakeLists.txt index 0a4471f3af..2bc5da8823 100644 --- a/libs/gltfio/CMakeLists.txt +++ b/libs/gltfio/CMakeLists.txt @@ -186,8 +186,7 @@ if (NOT WEBGL AND NOT ANDROID AND NOT IOS) # ================================================================================================== # Compiler flags # ================================================================================================== - if (MSVC) - else() + if (NOT MSVC) target_compile_options(${TARGET} PRIVATE -Wno-deprecated-register) endif() @@ -225,7 +224,9 @@ if (TNT_DEV AND NOT WEBGL AND NOT ANDROID AND NOT IOS) add_dependencies(${TEST_TARGET} test_gltfio_files) target_link_libraries(${TEST_TARGET} PRIVATE ${TARGET} filament filabridge gtest uberarchive) - target_compile_options(${TEST_TARGET} PRIVATE -Wno-deprecated-register) + if (NOT MSVC) + target_compile_options(${TEST_TARGET} PRIVATE -Wno-deprecated-register) + endif() set_target_properties(${TEST_TARGET} PROPERTIES FOLDER Tests) endif() From 8d621561f3c0ab696cfc08dfea89c8ec7cc2e431 Mon Sep 17 00:00:00 2001 From: Jacob Su Date: Tue, 12 Sep 2023 07:25:06 +0800 Subject: [PATCH 05/19] fix ios samples missing functional header (#7153) (#7154) 1. ios sample: ios/samples/hello-gltf; 2. ios sample: ios/samples/hello-pbr; --- ios/samples/hello-gltf/hello-gltf/CameraManipulator.h | 2 ++ ios/samples/hello-pbr/hello-pbr/CameraManipulator.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/ios/samples/hello-gltf/hello-gltf/CameraManipulator.h b/ios/samples/hello-gltf/hello-gltf/CameraManipulator.h index 1eba71f247..95036e6b32 100644 --- a/ios/samples/hello-gltf/hello-gltf/CameraManipulator.h +++ b/ios/samples/hello-gltf/hello-gltf/CameraManipulator.h @@ -17,6 +17,8 @@ #ifndef TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H #define TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H +#include + #include #include #include diff --git a/ios/samples/hello-pbr/hello-pbr/CameraManipulator.h b/ios/samples/hello-pbr/hello-pbr/CameraManipulator.h index 1eba71f247..95036e6b32 100644 --- a/ios/samples/hello-pbr/hello-pbr/CameraManipulator.h +++ b/ios/samples/hello-pbr/hello-pbr/CameraManipulator.h @@ -17,6 +17,8 @@ #ifndef TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H #define TNT_FILAMENT_SAMPLE_CAMERA_MANIPULATOR_H +#include + #include #include #include From 9ac4fa63ed4640497f89926aa3da6194586b32fd Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Mon, 11 Sep 2023 23:21:52 -0700 Subject: [PATCH 06/19] vulkan: fix swapchain leak (#7161) --- filament/backend/src/vulkan/VulkanDriver.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 3145d5df4a..875a583736 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -526,21 +526,22 @@ void VulkanDriver::createSwapChainR(Handle sch, void* nativeWindow, << utils::io::endl; flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE); } - mResourceAllocator.construct(sch, mPlatform, mContext, mAllocator, - mCommands.get(), mStagePool, nativeWindow, flags); + auto swapChain = mResourceAllocator.construct(sch, mPlatform, mContext, + mAllocator, mCommands.get(), mStagePool, nativeWindow, flags); + mResourceManager.acquire(swapChain); } void VulkanDriver::createSwapChainHeadlessR(Handle sch, uint32_t width, uint32_t height, uint64_t flags) { - if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && - !isSRGBSwapChainSupported()) { + if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) { utils::slog.w << "sRGB swapchain requested, but Platform does not support it" << utils::io::endl; flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE); } assert_invariant(width > 0 && height > 0 && "Vulkan requires non-zero swap chain dimensions."); - mResourceAllocator.construct(sch, mPlatform, mContext, mAllocator, - mCommands.get(), mStagePool, nullptr, flags, VkExtent2D{width, height}); + auto swapChain = mResourceAllocator.construct(sch, mPlatform, mContext, + mAllocator, mCommands.get(), mStagePool, nullptr, flags, VkExtent2D{width, height}); + mResourceManager.acquire(swapChain); } void VulkanDriver::createTimerQueryR(Handle tqh, int) { From da96b45827ace85379ee1217a40abb435ff72485 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 11 Sep 2023 21:26:46 -0700 Subject: [PATCH 07/19] apply shadow settings to all lights in the scene this "fixes" point light shadows in gltf_viewer, which were never enabled --- libs/viewer/src/Settings.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libs/viewer/src/Settings.cpp b/libs/viewer/src/Settings.cpp index 880063eb33..f416fdd43a 100644 --- a/libs/viewer/src/Settings.cpp +++ b/libs/viewer/src/Settings.cpp @@ -578,11 +578,9 @@ void applySettings(Engine* engine, const LightSettings& settings, IndirectLight* ibl->setRotation(math::mat3f::rotation(settings.iblRotation, math::float3 { 0, 1, 0 })); } for (size_t i = 0; i < sceneLightCount; i++) { - light = lm->getInstance(sceneLights[i]); - if (lm->isSpotLight(light)) { - lm->setShadowCaster(light, settings.enableShadows); - } - lm->setShadowOptions(light, settings.shadowOptions); + auto const li = lm->getInstance(sceneLights[i]); + lm->setShadowCaster(li, settings.enableShadows); + lm->setShadowOptions(li, settings.shadowOptions); } view->setSoftShadowOptions(settings.softShadowOptions); } From 1ff0a2dd6d9682d20174a4a3309f996ebc5211a9 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 11 Sep 2023 09:47:15 -0700 Subject: [PATCH 08/19] Improvements to shadowing - use the geometric normal to apply the shadow bias. This affects cascades > 0 and spot/point lights. - use the scene's origin as a reference point for stabilizing the shadowmap, this is more robust. - clamp directional shadowmap correctly to the 1-texel border, which needs to be reachable, as it is a valid value. - don't snap the shadowmap to texel boundaries if stable mode is not active (before we only didn't do it based on lispsm). Stable mode can make the shadow unstable when both the camera and the scene move together, so it's better to have a more predictable API where "stable" mode means that the snapping occurs and doesn't otherwise. - add "far origin" distance slider to the debug ui FIXES=[299310624] --- NEW_RELEASE_NOTES.md | 2 ++ filament/src/ShadowMap.cpp | 71 +++++++++++++++++++++++++------------- filament/src/ShadowMap.h | 4 +-- samples/gltf_viewer.cpp | 4 ++- shaders/src/getters.fs | 4 +-- 5 files changed, 56 insertions(+), 29 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 4a1a9c7fa7..1d21895439 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,3 +7,5 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut + +- engine: Fixes "stable" shadows (see b/299310624) diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index 273855e528..db1f43cbb9 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -327,9 +327,9 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, if (params.options.stable) { if (viewVolumeBoundingSphere.w > 0) { s = 1.0f / viewVolumeBoundingSphere.w; - o = mat4f::project(Mv * camera.model, viewVolumeBoundingSphere.xyz).xy; + o = mat4f::project(LMpMv * camera.model, viewVolumeBoundingSphere.xyz).xy; } else { - Aabb const bounds = compute2DBounds(Mv, + Aabb const bounds = compute2DBounds(LMpMv, wsClippedShadowReceiverVolume.data(), vertexCount); if (UTILS_UNLIKELY((bounds.min.x >= bounds.max.x) || (bounds.min.y >= bounds.max.y))) { // this could happen if the only thing visible is a perfectly horizontal or @@ -379,9 +379,9 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, // adjust offset for scale o = -s * o; - if (!useLispsm) { - // stabilize the shadowmap in all modes, except lispsm which can never be stable - snapLightFrustum(s, o, Mv, camera.worldOrigin, shadowMapInfo.shadowDimension); + if (params.options.stable) { + snapLightFrustum(s, o, LMpMv, + sceneInfo.wsShadowCastersVolume.center(), shadowMapInfo.shadowDimension); } const mat4f F(mat4f::row_major_init { @@ -400,8 +400,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, // Computes St the transform to use in the shader to access the shadow map texture // i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap - const backend::Viewport viewport = getViewport(); - const auto [Mt, Mn] = ShadowMap::getTextureCoordsMapping(shadowMapInfo, viewport); + const auto [Mt, Mn] = ShadowMap::getTextureCoordsMapping(shadowMapInfo, getViewport()); const mat4f St = math::highPrecisionMultiply(Mt, S); ShadowMap::ShaderParameters shaderParameters; @@ -423,7 +422,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, znear, zfar); } - shaderParameters.scissorNormalized = getViewportNormalized(shadowMapInfo); + shaderParameters.scissorNormalized = getClampToEdgeCoords(shadowMapInfo); // We apply the constant bias in world space (as opposed to light-space) to account // for perspective and lispsm shadow maps. This also allows us to do this at zero-cost @@ -456,9 +455,8 @@ ShadowMap::ShaderParameters ShadowMap::updatePunctual( assert_invariant(shadowMapInfo.textureDimension == mOptions->mapSize); // Final shadow transform - const backend::Viewport viewport = getViewport(); const mat4f S = math::highPrecisionMultiply(Mp, Mv); - const auto [Mt, Mn] = ShadowMap::getTextureCoordsMapping(shadowMapInfo, viewport); + const auto [Mt, Mn] = ShadowMap::getTextureCoordsMapping(shadowMapInfo, getViewport()); const mat4f St = math::highPrecisionMultiply(Mt, S); // TODO: focus projection @@ -485,7 +483,7 @@ ShadowMap::ShaderParameters ShadowMap::updatePunctual( shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane); } - shaderParameters.scissorNormalized = getViewportNormalized(shadowMapInfo); + shaderParameters.scissorNormalized = getClampToEdgeCoords(shadowMapInfo); const float3 direction = -transpose(Mv)[2].xyz; const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias; @@ -848,9 +846,9 @@ void ShadowMap::computeFrustumCorners(float3* UTILS_RESTRICT out, } void ShadowMap::snapLightFrustum(float2& s, float2& o, - mat4f const& Mv, mat4 worldOrigin, int2 resolution) noexcept { + mat4f const& Mv, double3 wsSnapCoords, int2 resolution) noexcept { - auto proj = [](mat4 m, double4 v) -> double3 { + auto proj = [](mat4 m, double3 v) -> double3 { // for directional light p.w == 1, exactly auto p = m * v; assert_invariant(p.w == 1.0); @@ -876,7 +874,7 @@ void ShadowMap::snapLightFrustum(float2& s, float2& o, mat4 const FMv{ F * Mv }; // This offsets the texture coordinates, so it has a fixed offset w.r.t the world - double2 const lsOrigin = proj(FMv, worldOrigin[3]).xy; + double2 const lsOrigin = proj(FMv, wsSnapCoords).xy; double2 const d = (fract(lsOrigin * resolution * 0.5) * 2.0) / resolution; // adjust offset @@ -1258,33 +1256,58 @@ void ShadowMap::updateSceneInfoSpot(mat4f const& Mv, FScene const& scene, } backend::Viewport ShadowMap::getViewport() const noexcept { - // We set a viewport with a 1-texel border for when we index outside the - // texture. This can only happen for the directional light when "focus shadow casters is used". + // We set a viewport with a 1-texel border for when we index outside the texture. + // This happens only for directional lights when "focus shadow casters" is used, + // or when shadowFar is smaller than the camera far. + // For spot- and point-lights we also use a 1-texel border, so that bilinear filtering + // can work properly if the shadowmap is in an atlas (and we can't rely on h/w clamp). const uint32_t dim = mOptions->mapSize; const uint16_t border = 1u; return { border, border, dim - 2u * border, dim - 2u * border }; } backend::Viewport ShadowMap::getScissor() const noexcept { - // We set a viewport with a 1-texel border for when we index outside the - // texture. This can only happen for the directional light when "focus shadow casters is used". + // We set a viewport with a 1-texel border for when we index outside the texture. + // This happens only for directional lights when "focus shadow casters" is used, + // or when shadowFar is smaller than the camera far. + // For spot- and point-lights we also use a 1-texel border, so that bilinear filtering + // can work properly if the shadowmap is in an atlas (and we can't rely on h/w clamp), so we + // don't scissor the border, so it gets filled with correct neighboring texels. const uint32_t dim = mOptions->mapSize; const uint16_t border = 1u; - switch (mShadowType) { case ShadowType::DIRECTIONAL: return { border, border, dim - 2u * border, dim - 2u * border }; case ShadowType::SPOT: case ShadowType::POINT: - default: return { 0, 0, dim, dim }; } } -math::float4 ShadowMap::getViewportNormalized(ShadowMapInfo const& shadowMapInfo) const noexcept { - const auto [l, b, w, h] = getViewport(); - const float texel = 1.0f / float(shadowMapInfo.atlasDimension); - const float4 v = float4{ l, b, l + w, b + h } * texel; +math::float4 ShadowMap::getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) const noexcept { + float border; // shadowmap border in texels + switch (mShadowType) { + case ShadowType::DIRECTIONAL: + // For directional lights, we need to allow the sampling to reach the border, it + // happens when "focus shadow casters" is used for instance. + border = 0.5f; + break; + case ShadowType::SPOT: + case ShadowType::POINT: + // For spot and point light, this is equal to the viewport. i.e. the valid + // texels are inside the viewport (w/ 1-texel border), the border will be used + // for bilinear filtering. + border = 1.0f; + break; + } + + float const texel = 1.0f / float(shadowMapInfo.atlasDimension); + float const dim = float(mOptions->mapSize); + float const l = border; + float const b = border; + float const w = dim - 2.0f * border; + float const h = dim - 2.0f * border; + float4 const v = float4{ l, b, l + w, b + h } * texel; if (shadowMapInfo.textureSpaceFlipped) { // this is equivalent to calling uvToRenderTargetUV() in the shader *after* clamping // texture coordinates to this normalized viewport. diff --git a/filament/src/ShadowMap.h b/filament/src/ShadowMap.h index bbb0bf744d..7e52dc14cf 100644 --- a/filament/src/ShadowMap.h +++ b/filament/src/ShadowMap.h @@ -222,7 +222,7 @@ private: const math::float3& dir); static inline void snapLightFrustum(math::float2& s, math::float2& o, - math::mat4f const& Mv, math::mat4 worldOrigin, math::int2 resolution) noexcept; + math::mat4f const& Mv, math::double3 wsSnapCoords, math::int2 resolution) noexcept; static inline void computeFrustumCorners(math::float3* out, const math::mat4f& projectionViewInverse, math::float2 csNearFar = { -1.0f, 1.0f }) noexcept; @@ -281,7 +281,7 @@ private: static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf, const math::mat4f& Mv, float znear, float zfar) noexcept; - math::float4 getViewportNormalized(ShadowMapInfo const& shadowMapInfo) const noexcept; + math::float4 getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) const noexcept; float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture, uint16_t shadowDimension) const noexcept; diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index 89a355b670..f445462586 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -101,6 +101,7 @@ struct App { bool actualSize = false; bool originIsFarAway = false; + float originDistance = 6378137; // Earth's radius in [m] struct Scene { Entity groundPlane; @@ -759,6 +760,7 @@ int main(int argc, char** argv) { ImGui::Checkbox("Disable buffer padding", debug.getPropertyAddress("d.renderer.disable_buffer_padding")); ImGui::Checkbox("Camera at origin", debug.getPropertyAddress("d.view.camera_at_origin")); ImGui::Checkbox("Far Origin", &app.originIsFarAway); + ImGui::SliderFloat("Origin", &app.originDistance, 0, 10000000); auto dataSource = debug.getDataSource("d.view.frame_info"); if (dataSource.data) { ImGuiExt::PlotLinesSeries("FrameInfo", 6, @@ -956,7 +958,7 @@ int main(int argc, char** argv) { tcm.setParent(tcm.getInstance(camera.getEntity()), root); tcm.setParent(tcm.getInstance(app.asset->getRoot()), root); tcm.setParent(tcm.getInstance(view->getFogEntity()), root); - tcm.setTransform(root, mat4f::translation(float3{ app.originIsFarAway ? 1e6f : 0.0f })); + tcm.setTransform(root, mat4f::translation(float3{ app.originIsFarAway ? app.originDistance : 0.0f })); // Check if color grading has changed. ColorGradingSettings& options = app.viewer->getSettings().view.colorGrading; diff --git a/shaders/src/getters.fs b/shaders/src/getters.fs index 46e6cf4e34..2f2cc4231a 100644 --- a/shaders/src/getters.fs +++ b/shaders/src/getters.fs @@ -109,7 +109,7 @@ highp vec4 getSpotLightSpacePosition(int index, highp vec3 dir, highp float zLig // for spotlights, the bias depends on z float bias = shadowUniforms.shadows[index].normalBias * zLight; - return computeLightSpacePosition(getWorldPosition(), getWorldNormalVector(), + return computeLightSpacePosition(getWorldPosition(), getWorldGeometricNormalVector(), dir, bias, lightFromWorldMatrix); } #endif @@ -141,7 +141,7 @@ highp vec4 getCascadeLightSpacePosition(int cascade) { return vertex_lightSpacePosition; } - return computeLightSpacePosition(getWorldPosition(), getWorldNormalVector(), + return computeLightSpacePosition(getWorldPosition(), getWorldGeometricNormalVector(), frameUniforms.lightDirection, shadowUniforms.shadows[cascade].normalBias, shadowUniforms.shadows[cascade].lightFromWorldMatrix); From d9c2893976042272ac5fd41b296a363d92b200b1 Mon Sep 17 00:00:00 2001 From: mackong Date: Wed, 13 Sep 2023 03:28:02 +0800 Subject: [PATCH 09/19] Fix possible change of scale sign when decomposing matrix (#7138) Co-authored-by: Mathias Agopian --- NEW_RELEASE_NOTES.md | 1 + android/gltfio-android/CMakeLists.txt | 3 + libs/gltfio/CMakeLists.txt | 3 + .../include/gltfio/TrsTransformManager.h | 114 ++++++++++++ libs/gltfio/src/Animator.cpp | 22 +-- libs/gltfio/src/AssetLoader.cpp | 9 +- libs/gltfio/src/FFilamentAsset.h | 10 +- libs/gltfio/src/FTrsTransformManager.h | 165 ++++++++++++++++++ libs/gltfio/src/FilamentAsset.cpp | 5 + libs/gltfio/src/TrsTransformManager.cpp | 98 +++++++++++ 10 files changed, 415 insertions(+), 15 deletions(-) create mode 100644 libs/gltfio/include/gltfio/TrsTransformManager.h create mode 100644 libs/gltfio/src/FTrsTransformManager.h create mode 100644 libs/gltfio/src/TrsTransformManager.cpp diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 1d21895439..59fa0685b1 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -8,4 +8,5 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut +- gltfio: Fix possible change of scale sign when decomposing transform matrix for animation - engine: Fixes "stable" shadows (see b/299310624) diff --git a/android/gltfio-android/CMakeLists.txt b/android/gltfio-android/CMakeLists.txt index a3bf446307..ffac6b8c61 100644 --- a/android/gltfio-android/CMakeLists.txt +++ b/android/gltfio-android/CMakeLists.txt @@ -52,6 +52,7 @@ set(GLTFIO_SRCS ${GLTFIO_DIR}/include/gltfio/FilamentInstance.h ${GLTFIO_DIR}/include/gltfio/MaterialProvider.h ${GLTFIO_DIR}/include/gltfio/NodeManager.h + ${GLTFIO_DIR}/include/gltfio/TrsTransformManager.h ${GLTFIO_DIR}/include/gltfio/ResourceLoader.h ${GLTFIO_DIR}/include/gltfio/TextureProvider.h ${GLTFIO_DIR}/include/gltfio/math.h @@ -69,10 +70,12 @@ set(GLTFIO_SRCS ${GLTFIO_DIR}/src/FilamentAsset.cpp ${GLTFIO_DIR}/src/FilamentInstance.cpp ${GLTFIO_DIR}/src/FNodeManager.h + ${GLTFIO_DIR}/src/FTrsTransformManager.h ${GLTFIO_DIR}/src/GltfEnums.h ${GLTFIO_DIR}/src/Ktx2Provider.cpp ${GLTFIO_DIR}/src/MaterialProvider.cpp ${GLTFIO_DIR}/src/NodeManager.cpp + ${GLTFIO_DIR}/src/TrsTransformManager.cpp ${GLTFIO_DIR}/src/ResourceLoader.cpp ${GLTFIO_DIR}/src/StbProvider.cpp ${GLTFIO_DIR}/src/TangentsJob.cpp diff --git a/libs/gltfio/CMakeLists.txt b/libs/gltfio/CMakeLists.txt index 2bc5da8823..fbc4397ab0 100644 --- a/libs/gltfio/CMakeLists.txt +++ b/libs/gltfio/CMakeLists.txt @@ -16,6 +16,7 @@ set(PUBLIC_HDRS include/gltfio/FilamentInstance.h include/gltfio/MaterialProvider.h include/gltfio/NodeManager.h + include/gltfio/TrsTransformManager.h include/gltfio/ResourceLoader.h include/gltfio/TextureProvider.h include/gltfio/math.h @@ -35,10 +36,12 @@ set(SRCS src/FilamentAsset.cpp src/FilamentInstance.cpp src/FNodeManager.h + src/FTrsTransformManager.h src/GltfEnums.h src/Ktx2Provider.cpp src/MaterialProvider.cpp src/NodeManager.cpp + src/TrsTransformManager.cpp src/ResourceLoader.cpp src/StbProvider.cpp src/TangentsJob.cpp diff --git a/libs/gltfio/include/gltfio/TrsTransformManager.h b/libs/gltfio/include/gltfio/TrsTransformManager.h new file mode 100644 index 0000000000..980457b7c8 --- /dev/null +++ b/libs/gltfio/include/gltfio/TrsTransformManager.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GLTFIO_TRSTRANSFORMMANAGER_H +#define GLTFIO_TRSTRANSFORMMANAGER_H + +#include + +#include +#include +#include +#include +#include + +using namespace filament::math; + +namespace utils { +class Entity; +} // namespace utils + +namespace filament::gltfio { + +class FTrsTransformManager; + +/** + * TrsTransformManager is used to add entities with glTF-specific trs information. + * + * Trs information here just used for Animation, DON'T use for transform. + */ +class UTILS_PUBLIC TrsTransformManager { +public: + using Instance = utils::EntityInstance; + using Entity = utils::Entity; + + /** + * Returns whether a particular Entity is associated with a component of this TrsTransformManager + * @param e An Entity. + * @return true if this Entity has a component associated with this manager. + */ + bool hasComponent(Entity e) const noexcept; + + /** + * Gets an Instance representing the trs transform component associated with the given Entity. + * @param e An Entity. + * @return An Instance object, which represents the trs transform component associated with the Entity e. + * @note Use Instance::isValid() to make sure the component exists. + * @see hasComponent() + */ + Instance getInstance(Entity e) const noexcept; + + /** + * Creates a trs transform component and associates it with the given entity. + * @param entity An Entity to associate a trs transform component with. + * @param translation The translation to initialize the trs transform component with. + * @param rotation The rotation to initialize the trs transform component with. + * @param scale The scale to initialize the trs transform component with. + * + * If this component already exists on the given entity, it is first destroyed as if + * destroy(Entity e) was called. + * + * @see destroy() + */ + void create(Entity entity); + void create(Entity entity, const float3& translation, const quatf& rotation, + const float3& scale); //!< \overload + + /** + * Destroys this component from the given entity. + * @param e An entity. + * + * @see create() + */ + void destroy(Entity e) noexcept; + + void setTranslation(Instance ci, const float3& translation) noexcept; + const float3& getTranslation(Instance ci) const noexcept; + + void setRotation(Instance ci, const quatf& rotation) noexcept; + const quatf& getRotation(Instance ci) const noexcept; + + void setScale(Instance ci, const float3& scale) noexcept; + const float3& getScale(Instance ci) const noexcept; + + void setTrs(Instance ci, const float3& translation, const quatf& rotation, + const float3& scale) noexcept; + const mat4f getTransform(Instance ci) const noexcept; + +protected: + TrsTransformManager() noexcept = default; + ~TrsTransformManager() = default; + +public: + TrsTransformManager(TrsTransformManager const&) = delete; + TrsTransformManager(TrsTransformManager&&) = delete; + TrsTransformManager& operator=(TrsTransformManager const&) = delete; + TrsTransformManager& operator=(TrsTransformManager&&) = delete; +}; + +} // namespace filament::gltfio + +#endif // GLTFIO_TRSTRANSFORMMANAGER_H diff --git a/libs/gltfio/src/Animator.cpp b/libs/gltfio/src/Animator.cpp index f6100c029e..99a4269d02 100644 --- a/libs/gltfio/src/Animator.cpp +++ b/libs/gltfio/src/Animator.cpp @@ -19,6 +19,7 @@ #include "FFilamentAsset.h" #include "FFilamentInstance.h" +#include "FTrsTransformManager.h" #include "downcast.h" #include @@ -74,6 +75,7 @@ struct AnimatorImpl { FFilamentInstance* instance = nullptr; RenderableManager* renderableManager; TransformManager* transformManager; + TrsTransformManager* trsTransformManager; vector weights; FixedCapacityVector crossFade; void addChannels(const FixedCapacityVector& nodeMap, const cgltf_animation& srcAnim, @@ -190,6 +192,7 @@ Animator::Animator(FFilamentAsset const* asset, FFilamentInstance* instance) { mImpl->instance = instance; mImpl->renderableManager = &asset->mEngine->getRenderableManager(); mImpl->transformManager = &asset->mEngine->getTransformManager(); + mImpl->trsTransformManager = asset->getTrsTransformManager(); const cgltf_data* srcAsset = asset->mSourceAsset->hierarchy; const cgltf_animation* srcAnims = srcAsset->animations; @@ -429,20 +432,13 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn size_t nextIndex) { const Sampler* sampler = channel.sourceData; const TimeValues& times = sampler->times; + TrsTransformManager::Instance trsNode = trsTransformManager->getInstance(channel.targetEntity); TransformManager::Instance node = transformManager->getInstance(channel.targetEntity); - // Perform the interpolation. This is a simple but inefficient implementation; Filament - // stores transforms as mat4's but glTF animation is based on TRS (translation rotation - // scale). - mat4f xform = transformManager->getTransform(node); - float3 scale; - quatf rotation; - float3 translation; - decomposeMatrix(xform, &translation, &rotation, &scale); - switch (channel.transformType) { case Channel::SCALE: { + float3 scale; const float3* srcVec3 = (const float3*) sampler->values.data(); if (sampler->interpolation == Sampler::CUBIC) { float3 vert0 = srcVec3[prevIndex * 3 + 1]; @@ -453,10 +449,12 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn } else { scale = ((1 - t) * srcVec3[prevIndex]) + (t * srcVec3[nextIndex]); } + trsTransformManager->setScale(trsNode, scale); break; } case Channel::TRANSLATION: { + float3 translation; const float3* srcVec3 = (const float3*) sampler->values.data(); if (sampler->interpolation == Sampler::CUBIC) { float3 vert0 = srcVec3[prevIndex * 3 + 1]; @@ -467,10 +465,12 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn } else { translation = ((1 - t) * srcVec3[prevIndex]) + (t * srcVec3[nextIndex]); } + trsTransformManager->setTranslation(trsNode, translation); break; } case Channel::ROTATION: { + quatf rotation; const quatf* srcQuat = (const quatf*) sampler->values.data(); if (sampler->interpolation == Sampler::CUBIC) { quatf vert0 = srcQuat[prevIndex * 3 + 1]; @@ -481,6 +481,7 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn } else { rotation = slerp(srcQuat[prevIndex], srcQuat[nextIndex], t); } + trsTransformManager->setRotation(trsNode, rotation); break; } @@ -519,8 +520,7 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn } } - xform = composeMatrix(translation, rotation, scale); - transformManager->setTransform(node, xform); + transformManager->setTransform(node, trsTransformManager->getTransform(trsNode)); } void AnimatorImpl::resetBoneMatrices(FFilamentInstance* instance) { diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index 329b5d4c7b..3fd23a40a1 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -21,6 +21,7 @@ #include "FFilamentAsset.h" #include "FNodeManager.h" +#include "FTrsTransformManager.h" #include "GltfEnums.h" #include @@ -295,6 +296,7 @@ public: MaterialProvider& mMaterials; Engine& mEngine; FNodeManager mNodeManager; + FTrsTransformManager mTrsTransformManager; // Transient state used only for the asset currently being loaded: FFilamentAsset* mAsset; @@ -400,7 +402,8 @@ void FAssetLoader::createRootAsset(const cgltf_data* srcAsset) { #endif mDummyBufferObject = nullptr; - mAsset = new FFilamentAsset(&mEngine, mNameManager, &mEntityManager, &mNodeManager, srcAsset); + mAsset = new FFilamentAsset(&mEngine, mNameManager, &mEntityManager, &mNodeManager, + &mTrsTransformManager, srcAsset); // It is not an error for a glTF file to have zero scenes. mAsset->mScenes.clear(); @@ -563,7 +566,9 @@ void FAssetLoader::recurseEntities(const cgltf_data* srcAsset, const cgltf_node* quatf* rotation = (quatf*) &node->rotation[0]; float3* scale = (float3*) &node->scale[0]; float3* translation = (float3*) &node->translation[0]; - localTransform = composeMatrix(*translation, *rotation, *scale); + mTrsTransformManager.create(entity, *translation, *rotation, *scale); + localTransform = mTrsTransformManager.getTransform( + mTrsTransformManager.getInstance(entity)); } auto parentTransform = mTransformManager.getInstance(parent); diff --git a/libs/gltfio/src/FFilamentAsset.h b/libs/gltfio/src/FFilamentAsset.h index 35ae8cdd57..245e5f0f05 100644 --- a/libs/gltfio/src/FFilamentAsset.h +++ b/libs/gltfio/src/FFilamentAsset.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -110,9 +111,9 @@ using MeshCache = utils::FixedCapacityVectortextures_count), mMeshCache(srcAsset->meshes_count) {} @@ -195,6 +196,10 @@ struct FFilamentAsset : public FilamentAsset { return mEngine; } + TrsTransformManager* getTrsTransformManager() const noexcept { + return mTrsTransformManager; + } + void releaseSourceData() noexcept; const void* getSourceAsset() const noexcept { @@ -242,6 +247,7 @@ struct FFilamentAsset : public FilamentAsset { utils::NameComponentManager* const mNameManager; utils::EntityManager* const mEntityManager; NodeManager* const mNodeManager; + TrsTransformManager* const mTrsTransformManager; std::vector mEntities; // sorted such that renderables come first std::vector mLightEntities; std::vector mCameraEntities; diff --git a/libs/gltfio/src/FTrsTransformManager.h b/libs/gltfio/src/FTrsTransformManager.h new file mode 100644 index 0000000000..c53342fcdb --- /dev/null +++ b/libs/gltfio/src/FTrsTransformManager.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GLTFIO_FTRSTRANSFORMMANAGER_H +#define GLTFIO_FTRSTRANSFORMMANAGER_H + +#include "downcast.h" +#include "gltfio/math.h" +#include "math/quat.h" +#include "utils/debug.h" + +#include + +#include +#include +#include +#include +#include + +namespace filament::gltfio { + +class UTILS_PRIVATE FTrsTransformManager : public TrsTransformManager { +public: + using Instance = TrsTransformManager::Instance; + + FTrsTransformManager() noexcept {} + + ~FTrsTransformManager() noexcept { + assert_invariant(mManager.getComponentCount() == 0); + } + + void terminate() noexcept; + + bool hasComponent(utils::Entity e) const noexcept { + return mManager.hasComponent(e); + } + + Instance getInstance(utils::Entity e) const noexcept { + return Instance(mManager.getInstance(e)); + } + + void create(utils::Entity entity) { + create(entity, float3{}, quatf{}, float3{1}); + } + + void create(utils::Entity entity, const float3& translation, + const quatf& rotation, const float3& scale) { + if (UTILS_UNLIKELY(mManager.hasComponent(entity))) { + destroy(entity); + } + UTILS_UNUSED_IN_RELEASE Instance ci = mManager.addComponent(entity); + assert_invariant(ci); + + if (ci) { + setTrs(ci, translation, rotation, scale); + } + } + + void destroy(utils::Entity e) noexcept { + if (Instance ci = mManager.getInstance(e); ci) { + mManager.removeComponent(e); + } + } + + void gc(utils::EntityManager& em) noexcept { + mManager.gc(em); + } + + void setTranslation(Instance ci, const float3& translation) noexcept { + assert_invariant(ci.isValid()); + mManager[ci].translation = translation; + } + + const float3& getTranslation(Instance ci) const noexcept { + return mManager[ci].translation; + } + + void setRotation(Instance ci, const quatf& rotation) noexcept { + assert_invariant(ci.isValid()); + mManager[ci].rotation = rotation; + } + + const quatf& getRotation(Instance ci) const noexcept { + return mManager[ci].rotation; + } + + void setScale(Instance ci, const float3& scale) noexcept { + assert_invariant(ci.isValid()); + mManager[ci].scale = scale; + } + + const float3& getScale(Instance ci) const noexcept { + return mManager[ci].scale; + } + + void setTrs(Instance ci, const float3& translation, + const quatf& rotation, const float3& scale) noexcept { + setTranslation(ci, translation); + setRotation(ci, rotation); + setScale(ci, scale); + } + + const mat4f getTransform(Instance ci) const noexcept { + return composeMatrix(getTranslation(ci), getRotation(ci), getScale(ci)); + } + +private: + enum { + TRANSLATION, + ROTATION, + SCALE, + }; + + using Base = utils::SingleInstanceComponentManager< + float3, + quatf, + float3>; + + struct Sim : public Base { + using Base::gc; + using Base::swap; + + typename Base::SoA& getSoA() { return mData; } + + struct Proxy { + UTILS_ALWAYS_INLINE + Proxy(Base& sim, utils::EntityInstanceBase::Type i) noexcept : + translation{ sim, i } { } + + union { + Field translation; + Field rotation; + Field scale; + }; + }; + + UTILS_ALWAYS_INLINE Proxy operator[](Instance i) noexcept { + return { *this, i }; + } + UTILS_ALWAYS_INLINE const Proxy operator[](Instance i) const noexcept { + return { const_cast(*this), i }; + } + }; + + Sim mManager; +}; + +FILAMENT_DOWNCAST(TrsTransformManager) + +} // namespace filament::gltfio + +#endif // GLTFIO_FTRSTRANSFORMMANAGER_H diff --git a/libs/gltfio/src/FilamentAsset.cpp b/libs/gltfio/src/FilamentAsset.cpp index 99b3c36a42..558640179c 100644 --- a/libs/gltfio/src/FilamentAsset.cpp +++ b/libs/gltfio/src/FilamentAsset.cpp @@ -60,6 +60,11 @@ FFilamentAsset::~FFilamentAsset() { } + // Destroy gltfio trs transform components. + for (auto entity : mEntities) { + mTrsTransformManager->destroy(entity); + } + // Destroy all renderable, light, transform, and camera components, // then destroy the actual entities. This includes instances. if (!mDetachedFilamentComponents) { diff --git a/libs/gltfio/src/TrsTransformManager.cpp b/libs/gltfio/src/TrsTransformManager.cpp new file mode 100644 index 0000000000..5548bc9453 --- /dev/null +++ b/libs/gltfio/src/TrsTransformManager.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FTrsTransformManager.h" + +#include + +#include "downcast.h" +#include "gltfio/TrsTransformManager.h" + +using namespace utils; + +namespace filament::gltfio { + +using Instance = TrsTransformManager::Instance; + +void FTrsTransformManager::terminate() noexcept { + auto& manager = mManager; + if (!manager.empty()) { +#ifndef NDEBUG + utils::slog.d << "cleaning up " << manager.getComponentCount() + << " leaked trs transform components" << utils::io::endl; +#endif + while (!manager.empty()) { + Instance ci = manager.end() - 1; + manager.removeComponent(manager.getEntity(ci)); + } + } +} + +bool TrsTransformManager::hasComponent(Entity e) const noexcept { + return downcast(this)->hasComponent(e); +} + +Instance TrsTransformManager::getInstance(Entity e) const noexcept { + return downcast(this)->getInstance(e); +} + +void TrsTransformManager::create(Entity entity) { + downcast(this)->create(entity); +} + +void TrsTransformManager::create(Entity entity, const float3& translation, + const quatf& rotation, const float3& scale) { + downcast(this)->create(entity, translation, rotation, scale); +} + +void TrsTransformManager::destroy(Entity e) noexcept { + downcast(this)->destroy(e); +} + +void TrsTransformManager::setTranslation(Instance ci, const float3& translation) noexcept { + downcast(this)->setTranslation(ci, translation); +} + +const float3& TrsTransformManager::getTranslation(Instance ci) const noexcept { + return downcast(this)->getTranslation(ci); +} + +void TrsTransformManager::setRotation(Instance ci, const quatf& rotation) noexcept { + downcast(this)->setRotation(ci, rotation); +} + +const quatf& TrsTransformManager::getRotation(Instance ci) const noexcept { + return downcast(this)->getRotation(ci); +} + +void TrsTransformManager::setScale(Instance ci, const float3& scale) noexcept { + downcast(this)->setScale(ci, scale); +} + +const float3& TrsTransformManager::getScale(Instance ci) const noexcept { + return downcast(this)->getScale(ci); +} + +void TrsTransformManager::setTrs(Instance ci, const float3& translation, + const quatf& rotation, const float3& scale) noexcept { + downcast(this)->setTrs(ci, translation, rotation, scale); +} + +const mat4f TrsTransformManager::getTransform(Instance ci) const noexcept { + return downcast(this)->getTransform(ci); +} + +} // namespace filament::gltfio From 58017a0e6a6340601a7594a2c1ca573ec5f1b95a Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Mon, 11 Sep 2023 14:38:58 -0700 Subject: [PATCH 10/19] Tweak documentation This is admittedly a very nitpicky change. For most of the changes, I went through the various Markdown files and added language names to the source blocks for better syntax highlighting on GitHub. It also makes it easier to copy and paste commands without copying the leading `$`. I avoided changing anything in `third_party`. Additionally, I added some instructions for compiling the Android samples on the command line and fixed some typos. --- BUILDING.md | 132 ++++++++++++++++---------------- CONTRIBUTING.md | 2 +- README.md | 2 +- android/samples/README.md | 26 +++++-- filament/README.md | 16 ++-- libs/filamat/README.md | 12 +-- site/content/posts/cocoapods.md | 4 +- tools/cmgen/README.md | 9 ++- tools/filamesh/README.md | 4 +- tools/matinfo/README.md | 4 +- tools/mipgen/README.md | 4 +- tools/specular-color/README.md | 12 +-- 12 files changed, 119 insertions(+), 108 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index 9c0f4d08c9..7146b751b0 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -40,20 +40,20 @@ inside the Filament source tree. To trigger an incremental debug build: -``` -$ ./build.sh debug +```shell +./build.sh debug ``` To trigger an incremental release build: -``` -$ ./build.sh release +```shell +./build.sh release ``` To trigger both incremental debug and release builds: -``` -$ ./build.sh debug release +```shell +./build.sh debug release ``` To install the libraries and executables in `out/debug/` and `out/release/`, add the `-i` flag. @@ -76,9 +76,9 @@ The following CMake options are boolean options specific to Filament: To turn an option on or off: -``` -$ cd -$ cmake . -DOPTION=ON # Replace OPTION with the option name, set to ON / OFF +```shell +cd +cmake . -DOPTION=ON # Replace OPTION with the option name, set to ON / OFF ``` Options can also be set with the CMake GUI. @@ -102,38 +102,38 @@ script. If you'd like to run `cmake` directly rather than using the build script, it can be invoked as follows, with some caveats that are explained further down. -``` -$ mkdir out/cmake-release -$ cd out/cmake-release -$ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../.. +```shell +mkdir out/cmake-release +cd out/cmake-release +cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../.. ``` Your Linux distribution might default to `gcc` instead of `clang`, if that's the case invoke `cmake` with the following command: -``` -$ mkdir out/cmake-release -$ cd out/cmake-release +```shell +mkdir out/cmake-release +cd out/cmake-release # Or use a specific version of clang, for instance /usr/bin/clang-14 -$ CC=/usr/bin/clang CXX=/usr/bin/clang++ CXXFLAGS=-stdlib=libc++ \ - cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../.. +CC=/usr/bin/clang CXX=/usr/bin/clang++ CXXFLAGS=-stdlib=libc++ \ + cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../.. ``` You can also export the `CC` and `CXX` environment variables to always point to `clang`. Another solution is to use `update-alternatives` to both change the default compiler, and point to a specific version of clang: -``` -$ update-alternatives --install /usr/bin/clang clang /usr/bin/clang-14 100 -$ update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-14 100 -$ update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 -$ update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 +```shell +update-alternatives --install /usr/bin/clang clang /usr/bin/clang-14 100 +update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-14 100 +update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 +update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 ``` Finally, invoke `ninja`: -``` -$ ninja +```shell +ninja ``` This will build Filament, its tests and samples, and various host tools. @@ -143,8 +143,8 @@ This will build Filament, its tests and samples, and various host tools. To compile Filament you must have the most recent version of Xcode installed and you need to make sure the command line tools are setup by running: -``` -$ xcode-select --install +```shell +xcode-select --install ``` If you wish to run the Vulkan backend instead of the default Metal backend, you must install @@ -152,11 +152,11 @@ the LunarG SDK, enable "System Global Components", and reboot your machine. Then run `cmake` and `ninja` to trigger a build: -``` -$ mkdir out/cmake-release -$ cd out/cmake-release -$ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../.. -$ ninja +```shell +mkdir out/cmake-release +cd out/cmake-release +cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../.. +ninja ``` ### iOS @@ -164,8 +164,8 @@ $ ninja The easiest way to build Filament for iOS is to use `build.sh` and the `-p ios` flag. For instance to build the debug target: -``` -$ ./build.sh -p ios debug +```shell +./build.sh -p ios debug ``` See [ios/samples/README.md](./ios/samples/README.md) for more information. @@ -191,10 +191,10 @@ using `fsutil.exe file queryCaseSensitiveInfo`. Next, open `x64 Native Tools Command Prompt for VS 2019`, create a working directory, and run CMake in it: -``` -> mkdir out -> cd out -> cmake .. +```bat +mkdir out +cd out +cmake .. ``` Open the generated solution file `TNT.sln` in Visual Studio. @@ -204,15 +204,15 @@ target in the _Solution Explorer_ and choose _Build_ to build a specific target. For example, build the `material_sandbox` sample and run it from the `out` directory with: -``` -> samples\Debug\material_sandbox.exe ..\assets\models\monkey\monkey.obj +```bat +samples\Debug\material_sandbox.exe ..\assets\models\monkey\monkey.obj ``` You can also use CMake to invoke the build without opening Visual Studio. For example, from the `out` folder run the following command. -``` -> cmake --build . --target gltf_viewer --config Release +```bat +cmake --build . --target gltf_viewer --config Release ``` ### Android @@ -237,8 +237,8 @@ To build Android on Windows machines, see [android/Windows.md](android/Windows.m The easiest way to build Filament for Android is to use `build.sh` and the `-p android` flag. For instance to build the release target: -``` -$ ./build.sh -p android release +```shell +./build.sh -p android release ``` Run `build.sh -h` for more information. @@ -248,23 +248,23 @@ Run `build.sh -h` for more information. Invoke CMake in a build directory of your choice, inside of filament's directory. The commands below show how to build Filament for ARM 64-bit (`aarch64`). -``` -$ mkdir out/android-build-release-aarch64 -$ cd out/android-build-release-aarch64 -$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=../../build/toolchain-aarch64-linux-android.cmake \ - -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../android-release/filament ../.. +```shell +mkdir out/android-build-release-aarch64 +cd out/android-build-release-aarch64 +cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=../../build/toolchain-aarch64-linux-android.cmake \ + -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../android-release/filament ../.. ``` And then invoke `ninja`: -``` -$ ninja install +```shell +ninja install ``` or -``` -$ ninja install/strip +```shell +ninja install/strip ``` This will generate Filament's Android binaries in `out/android-release`. This location is important @@ -296,8 +296,8 @@ AAR. Alternatively you can build the AAR from the command line by executing the following in the `android/` directory: -``` -$ ./gradlew -Pcom.google.android.filament.dist-dir=../../out/android-release/filament assembleRelease +```shell +./gradlew -Pcom.google.android.filament.dist-dir=../../out/android-release/filament assembleRelease ``` The `-Pcom.google.android.filament.dist-dir` can be used to specify a different installation @@ -311,7 +311,7 @@ sure to add the newly created module as a dependency to your application. If you do not wish to include all supported ABIs, make sure to create the appropriate flavors in your Gradle build file. For example: -``` +```gradle flavorDimensions 'cpuArch' productFlavors { arm8 { @@ -353,7 +353,7 @@ started, follow the instructions for building Filament on your platform ([macOS] Next, you need to install the Emscripten SDK. The following instructions show how to install the same version that our continuous builds use. -``` +```shell cd curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.15.zip > emsdk.zip unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk @@ -364,7 +364,7 @@ source ./emsdk_env.sh After this you can invoke the [easy build](#easy-build) script as follows: -``` +```shell export EMSDK= ./build.sh -p webgl release ``` @@ -374,7 +374,7 @@ creates a `samples` folder that can be used as the root of a simple static web s cannot open the HTML directly from the filesystem due to CORS. We recommend using the emrun tool to create a quick localhost server: -``` +```shell emrun out/cmake-webgl-release/web/samples --no_browser --port 8000 ``` @@ -395,7 +395,7 @@ Some of the samples accept FBX/OBJ meshes while others rely on the `filamesh` fi generate a `filamesh ` file from an FBX/OBJ asset, run the `filamesh` tool (`./tools/filamesh/filamesh` in your build directory): -``` +```shell filamesh ./assets/models/monkey/monkey.obj monkey.filamesh ``` @@ -405,7 +405,7 @@ files for the IBL (which are PNGs containing `R11F_G11F_B10F` data) or a path to containing two `.ktx` files (one for the IBL itself, one for the skybox). To generate an IBL simply use this command: -``` +```shell cmgen -f ktx -x ./ibls/ my_ibl.exr ``` @@ -427,9 +427,9 @@ value is the desired roughness between 0 and 1. To generate the documentation you must first install `doxygen` and `graphviz`, then run the following commands: -``` -$ cd filament/filament -$ doxygen docs/doxygen/filament.doxygen +```shell +cd filament/filament +doxygen docs/doxygen/filament.doxygen ``` Finally simply open `docs/html/index.html` in your web browser. @@ -439,7 +439,7 @@ Finally simply open `docs/html/index.html` in your web browser. To try out Filament's Vulkan support with SwiftShader, first build SwiftShader and set the `SWIFTSHADER_LD_LIBRARY_PATH` variable to the folder that contains `libvk_swiftshader.dylib`: -``` +```shell git clone https://github.com/google/swiftshader.git cd swiftshader/build cmake .. && make -j @@ -454,7 +454,7 @@ Continuous testing turnaround can be quite slow if you need to build SwiftShader provide an Ubuntu-based Docker image that has it already built. The Docker image also includes everything necessary for building Filament. You can fetch and run the image as follows: -``` +```shell docker pull ghcr.io/filament-assets/swiftshader docker run -it ghcr.io/filament-assets/swiftshader ``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 955e451e09..571600da2f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,7 +27,7 @@ again. ## Code Style -See [CodeStyle.md](/CODE_STYLE.md) +See [CODE_STYLE.md](/CODE_STYLE.md) ## Code reviews diff --git a/README.md b/README.md index 63f3a490ac..27b5c0c723 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,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.42.1' ``` diff --git a/android/samples/README.md b/android/samples/README.md index 151583bb82..cdfc93e2e2 100644 --- a/android/samples/README.md +++ b/android/samples/README.md @@ -87,9 +87,9 @@ compile Filament's native library and Filament's AAR for this project. The easie is to install all the required dependencies and to run the following commands at the root of the source tree: -``` -$ ./build.sh -p desktop -i release -$ ./build.sh -p android release +```shell +./build.sh -p desktop -i release +./build.sh -p android release ``` This will build all the native components and the AAR required by this sample application. @@ -100,8 +100,8 @@ distribution/install directory for desktop (produced by make/ninja install). Thi contain `bin/matc` and `bin/cmgen`. Example: -``` -$ ./gradlew -Pfilament_tools_dir=../../dist-release assembleDebug +```shell +./gradlew -Pfilament_tools_dir=../../dist-release assembleDebug ``` ## Important: SDK location @@ -110,14 +110,24 @@ Either ensure your `ANDROID_HOME` environment variable is set or make sure the r contains a `local.properties` file with the `sdk.dir` property pointing to your installation of the Android SDK. -## Android Studio +## Compiling + +### Android Studio You must use the latest stable release of Android Studio. To open the project, point Studio to the `android` folder. After opening the project and syncing to gradle, select the sample of your choice using the drop-down widget in the toolbar. -## Compiling - To compile and run each sample make sure you have selected the appropriate build variant (arm7, arm8, x86 or x86_64). If you are not sure you can simply select the "universal" variant which includes all the other ones. + +### Command Line + +From the `android` directory in the project root: + +```shell +./gradlew :samples:sample-hello-triangle:installDebug +``` + +Replace `sample-hello-triangle` with your preferred project. diff --git a/filament/README.md b/filament/README.md index 1560c96135..79ec596bde 100644 --- a/filament/README.md +++ b/filament/README.md @@ -61,7 +61,7 @@ with the platform name, for example, `filament-20181009-linux.tgz`. Create a file, `main.cpp`, in the same directory with the following contents: -``` +```c++ #include #include @@ -91,7 +91,7 @@ Copy your platform's Makefile below into a `Makefile` inside the same directory. ### Linux -``` +```make FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl CC=clang++ @@ -109,7 +109,7 @@ clean: ### macOS -``` +```make FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl FRAMEWORKS=-framework Cocoa -framework Metal -framework CoreVideo CC=clang++ @@ -137,7 +137,7 @@ be sure to also include `matdbg.lib` in `FILAMENT_LIBS`. When building Filament from source, the `USE_STATIC_CRT` CMake option can be used to change the run-time library version. -``` +```make FILAMENT_LIBS=filament.lib backend.lib bluegl.lib bluevk.lib filabridge.lib filaflat.lib \ utils.lib geometry.lib smol-v.lib ibl.lib vkshaders.lib CC=cl.exe @@ -171,12 +171,12 @@ and invoke `nmake` instead of `make`. ### Generating C++ documentation -To generate the documentation you must first install `doxygen` and `graphviz`, then run the +To generate the documentation you must first install `doxygen` and `graphviz`, then run the following commands: -``` -$ cd filament/filament -$ doxygen docs/doxygen/filament.doxygen +```shell +cd filament/filament +doxygen docs/doxygen/filament.doxygen ``` Finally simply open `docs/html/index.html` in your web browser. diff --git a/libs/filamat/README.md b/libs/filamat/README.md index dd81004275..61d98c6767 100644 --- a/libs/filamat/README.md +++ b/libs/filamat/README.md @@ -35,7 +35,7 @@ for example, `filament-20181009-linux.tgz`. Create a file, `main.cpp`, in the same directory with the following contents: -``` +```c++ #include #include @@ -85,7 +85,7 @@ Copy your platform's Makefile below into a `Makefile` inside the same directory. ### Linux -``` +```make FILAMENT_LIBS=-lfilamat -lfilabridge -lshaders -lutils -lsmol-v CC=clang++ @@ -103,7 +103,7 @@ clean: ### macOS -``` +```make FILAMENT_LIBS=-lfilamat -lfilabridge -lshaders -lutils -lsmol-v CC=clang++ @@ -129,7 +129,7 @@ flags](https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-tim When building Filamat from source, the `USE_STATIC_CRT` CMake option can be used to change the run-time library version. -``` +```make FILAMENT_LIBS=lib/x86_64/mt/filamat.lib lib/x86_64/mt/filabridge.lib lib/x86_64/mt/shaders.lib \ lib/x86_64/mt/utils.lib lib/x86_64/mt/smol-v.lib CC=clang-cl.exe @@ -164,7 +164,7 @@ and invoke `nmake` instead of `make`. For simplicity, this demo doesn't do anything useful with the built material package. To use the material with Filament, pass the material package's data into a Filament Material builder: -``` +```c++ Package package = builder.build(); filament::Material* myMaterial = Material::Builder() .package(package.getData(), package.getSize()) @@ -186,7 +186,7 @@ In addition, `filamat_lite` only performs a simple text match to determine which `MaterialInputs` structure are set. The `material` input variable must also always be refered to by the name `material`. -``` +```glsl void anotherFunction(inout MaterialInputs m) { // Incorrect! The MaterialInputs is being referred to by the name "m". m.metallic = 0.0; diff --git a/site/content/posts/cocoapods.md b/site/content/posts/cocoapods.md index 25d279c9df..0369e3f64a 100644 --- a/site/content/posts/cocoapods.md +++ b/site/content/posts/cocoapods.md @@ -45,8 +45,8 @@ end Then run: -``` -$ pod install +```shell +pod install ``` Close the project and then re-open the newly created HelloCocoaPods.xcworkspace file. diff --git a/tools/cmgen/README.md b/tools/cmgen/README.md index 160d48e0cd..d3f5259e87 100644 --- a/tools/cmgen/README.md +++ b/tools/cmgen/README.md @@ -7,9 +7,9 @@ The tool can consume a HDR environment map in latlong format (equirectilinear) a ## Usage -``` -$ cmgen [options] -$ cmgen [options] +```shell +cmgen [options] +cmgen [options] ``` ## Supported input formats @@ -21,6 +21,7 @@ $ cmgen [options] ## Options +``` --help, -h Print this message --license @@ -61,4 +62,4 @@ $ cmgen [options] Roughness pre-filter into --sh-shader Generate irradiance SH for shader code - +``` diff --git a/tools/filamesh/README.md b/tools/filamesh/README.md index f39ca1e457..ced71e413e 100644 --- a/tools/filamesh/README.md +++ b/tools/filamesh/README.md @@ -14,8 +14,8 @@ identified by an offset and count in the index buffer. Each part can have its ow ## Usage -``` -$ filamesh source_mesh destination_mesh +```shell +filamesh source_mesh destination_mesh ``` ## Format diff --git a/tools/matinfo/README.md b/tools/matinfo/README.md index a169535157..728ab14014 100644 --- a/tools/matinfo/README.md +++ b/tools/matinfo/README.md @@ -5,6 +5,6 @@ used for debug purpose only. ## Usage -``` -$ matinfo [options] +```shell +matinfo [options] ``` diff --git a/tools/mipgen/README.md b/tools/mipgen/README.md index e60853b219..cd49a0ab47 100644 --- a/tools/mipgen/README.md +++ b/tools/mipgen/README.md @@ -4,8 +4,8 @@ ## Usage -``` -$ mipgen [options] +```shell +mipgen [options] ``` Run `mipgen --help` for more information about available options. diff --git a/tools/specular-color/README.md b/tools/specular-color/README.md index 069b07b286..94d3729a55 100644 --- a/tools/specular-color/README.md +++ b/tools/specular-color/README.md @@ -11,8 +11,8 @@ grazing angles. See Hoffman 2019, "Fresnel Equations Considered Harmful". ## Usage -``` -$ specular-color +```shell +specular-color ``` The spectral data files can be obtained from @@ -20,12 +20,12 @@ The spectral data files can be obtained from For instance, to compute the base color of gold: -``` -$ specular-color data/gold.txt +```shell +specular-color data/gold.txt ``` To set the second angle, use `-a` to specify the angle in degrees: -``` -$ specular-color -a 75 data/gold.txt +```shell +specular-color -a 75 data/gold.txt ``` From c35a60808c9b24bb2699f72f36dc2aa0e1f5087a Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 11 Sep 2023 22:39:10 -0700 Subject: [PATCH 11/19] repair ShadowOptions::shadowFar the shadow far plane (shadowFar) was only partially taken into account --- .../google/android/filament/LightManager.java | 1 + filament/include/filament/LightManager.h | 1 + filament/src/ShadowMap.cpp | 23 ++++--------------- filament/src/ShadowMap.h | 8 +++---- filament/src/ShadowMapManager.cpp | 21 ++++++++++++++++- filament/src/ShadowMapManager.h | 2 +- 6 files changed, 32 insertions(+), 24 deletions(-) diff --git a/android/filament-android/src/main/java/com/google/android/filament/LightManager.java b/android/filament-android/src/main/java/com/google/android/filament/LightManager.java index 9e141a5ce3..bc930d7c6c 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/LightManager.java +++ b/android/filament-android/src/main/java/com/google/android/filament/LightManager.java @@ -258,6 +258,7 @@ public class LightManager { * shadows that are too far and wouldn't contribute to the scene much, improving * performance and quality. This value is always positive. * Use 0.0f to use the camera far distance. + * This only affect directional lights. */ public float shadowFar = 0.0f; diff --git a/filament/include/filament/LightManager.h b/filament/include/filament/LightManager.h index c29aa7d224..b7cb62e16a 100644 --- a/filament/include/filament/LightManager.h +++ b/filament/include/filament/LightManager.h @@ -245,6 +245,7 @@ public: * shadows that are too far and wouldn't contribute to the scene much, improving * performance and quality. This value is always positive. * Use 0.0f to use the camera far distance. + * This only affect directional lights. */ float shadowFar = 0.0f; diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index db1f43cbb9..f3c250e2d9 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -131,19 +131,6 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, else params.options.shadowFarHint = dzf * dz + camera.zf; #endif - // Adjust the camera's projection for the light's shadowFar - const mat4f cullingProjection{ [&](auto p) { - if (params.options.shadowFar > 0.0f) { - float const n = camera.zn; - float const f = params.options.shadowFar; - // orthographic projection - assert_invariant(std::abs(p[2].w) <= std::numeric_limits::epsilon()); - p[2].z = 2.0f / (n - f); - p[3].z = (f + n) / (n - f); - } - return p; - }(camera.cullingProjection) }; - const auto direction = params.options.transform * lightData.elementAt(index); /* @@ -164,7 +151,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, // view frustum vertices in world-space float3 wsViewFrustumVertices[8]; - const mat4f worldToClipMatrix = cullingProjection * camera.view; + const mat4f worldToClipMatrix = camera.cullingProjection * camera.view; const Frustum wsFrustum(worldToClipMatrix); computeFrustumCorners(wsViewFrustumVertices, inverse(worldToClipMatrix), sceneInfo.csNearFar); @@ -243,7 +230,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, // in stable mode we simply take the view volume bounding sphere, but we calculate it // in view space, so that it's perfectly stable. float3 vertices[8]; - computeFrustumCorners(vertices, inverse(cullingProjection), sceneInfo.csNearFar); + computeFrustumCorners(vertices, inverse(camera.cullingProjection), sceneInfo.csNearFar); viewVolumeBoundingSphere = computeBoundingSphere(vertices, 8); if (shadowReceiverVolumeBoundingSphere.w < viewVolumeBoundingSphere.w) { @@ -1081,7 +1068,7 @@ bool ShadowMap::intersectSegmentWithPlanarQuad(float3& UTILS_RESTRICT p, } float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture, - uint16_t shadowDimension) const noexcept { + uint16_t shadowDimension) noexcept { // The Jacobian of the transformation from texture-to-world is the matrix itself for // orthographic projections. We just need to inverse worldToShadowTexture, // which is guaranteed to be orthographic. @@ -1096,7 +1083,7 @@ float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture, } float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF, - uint16_t shadowDimension) const noexcept { + uint16_t shadowDimension) noexcept { // Here we compute the Jacobian of inverse(MbMtF * Wp). // The expression below has been computed with Mathematica. However, it's not very hard, // albeit error-prone, to do it by hand because MbMtF is a linear transform. @@ -1117,7 +1104,7 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF, constexpr bool JACOBIAN_ESTIMATE = false; if constexpr (JACOBIAN_ESTIMATE) { - // this estimates the Jacobian -- this is a lot heavier. This is mostly for reference + // This estimates the Jacobian -- this is a lot heavier. This is mostly for reference // and testing. const mat4f Si(inverse(MbMtF * Wp)); const float3 p0 = mat4f::project(Si, p); diff --git a/filament/src/ShadowMap.h b/filament/src/ShadowMap.h index 7e52dc14cf..f77ff6bfe8 100644 --- a/filament/src/ShadowMap.h +++ b/filament/src/ShadowMap.h @@ -283,11 +283,11 @@ private: math::float4 getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) const noexcept; - float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture, - uint16_t shadowDimension) const noexcept; + static float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture, + uint16_t shadowDimension) noexcept; - float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF, - uint16_t shadowDimension) const noexcept; + static float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF, + uint16_t shadowDimension) noexcept; static constexpr const Segment sBoxSegments[12] = { { 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 }, diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index fbb4e744e8..34c47519e6 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -435,8 +435,9 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG } ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEngine& engine, - FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData, + FView& view, CameraInfo cameraInfo, FScene::RenderableSoa& renderableData, FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept { + FScene* scene = view.getScene(); auto& lcm = engine.getLightManager(); @@ -444,6 +445,24 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng FLightManager::ShadowOptions const& options = lcm.getShadowOptions(directionalLight); FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight); + // Adjust the camera's projection for the light's shadowFar + + cameraInfo.zf = params.options.shadowFar > 0.0f ? params.options.shadowFar : cameraInfo.zf; + if (UTILS_UNLIKELY(params.options.shadowFar > 0.0f)) { + cameraInfo.zf = params.options.shadowFar; + float const n = cameraInfo.zn; + float const f = cameraInfo.zf; + if (std::abs(cameraInfo.cullingProjection[2].w) > std::numeric_limits::epsilon()) { + // perspective projection + cameraInfo.cullingProjection[2].z = (f + n) / (n - f); + cameraInfo.cullingProjection[3].z = (2 * f * n) / (n - f); + } else { + // orthographic projection + cameraInfo.cullingProjection[2].z = 2.0f / (n - f); + cameraInfo.cullingProjection[3].z = (f + n) / (n - f); + } + } + const ShadowMap::ShadowMapInfo shadowMapInfo{ .atlasDimension = mTextureAtlasRequirements.size, .textureDimension = uint16_t(options.mapSize), diff --git a/filament/src/ShadowMapManager.h b/filament/src/ShadowMapManager.h index 488e108e40..712e230266 100644 --- a/filament/src/ShadowMapManager.h +++ b/filament/src/ShadowMapManager.h @@ -109,7 +109,7 @@ public: private: ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine, - FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData, + FView& view, CameraInfo cameraInfo, FScene::RenderableSoa& renderableData, FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept; ShadowMapManager::ShadowTechnique updateSpotShadowMaps(FEngine& engine, From ec30ddd2fb7ed5bff4ed9f108e11707d57602f35 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 13 Sep 2023 11:21:04 -0700 Subject: [PATCH 12/19] vulkan: implicitly free command buffers (#7167) We were calling vkFreeCommandBuffers directly, but resetting the buffers implicitly (when vkBeginCommandBuffer is called) seems to be a lot more performant. Also, cleaned up destructor for VkBuffer to no longer require a separate terminate() method. --- filament/backend/src/vulkan/VulkanBlitter.cpp | 10 ++- filament/backend/src/vulkan/VulkanBuffer.cpp | 7 -- filament/backend/src/vulkan/VulkanBuffer.h | 1 - .../backend/src/vulkan/VulkanCommands.cpp | 68 ++++++++++--------- filament/backend/src/vulkan/VulkanCommands.h | 20 ++++-- filament/backend/src/vulkan/VulkanContext.cpp | 7 +- filament/backend/src/vulkan/VulkanDriver.cpp | 14 ++-- filament/backend/src/vulkan/VulkanHandles.h | 11 +-- .../src/vulkan/VulkanPipelineCache.cpp | 4 +- .../src/vulkan/VulkanResourceAllocator.h | 4 -- filament/backend/src/vulkan/VulkanResources.h | 4 ++ .../backend/src/vulkan/VulkanStagePool.cpp | 2 +- .../backend/src/vulkan/VulkanSwapChain.cpp | 2 +- filament/backend/src/vulkan/VulkanTexture.cpp | 6 +- 14 files changed, 76 insertions(+), 84 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanBlitter.cpp b/filament/backend/src/vulkan/VulkanBlitter.cpp index 22960d9dde..e9e4062ac6 100644 --- a/filament/backend/src/vulkan/VulkanBlitter.cpp +++ b/filament/backend/src/vulkan/VulkanBlitter.cpp @@ -147,7 +147,7 @@ void VulkanBlitter::blitColor(BlitArgs args) { } #endif VulkanCommandBuffer& commands = mCommands->get(); - VkCommandBuffer const cmdbuffer = commands.cmdbuffer; + VkCommandBuffer const cmdbuffer = commands.buffer(); commands.acquire(src.texture); commands.acquire(dst.texture); @@ -184,7 +184,7 @@ void VulkanBlitter::blitDepth(BlitArgs args) { } VulkanCommandBuffer& commands = mCommands->get(); - VkCommandBuffer const cmdbuffer = commands.cmdbuffer; + VkCommandBuffer const cmdbuffer = commands.buffer(); commands.acquire(src.texture); commands.acquire(dst.texture); blitFast(cmdbuffer, aspect, args.filter, args.srcTarget->getExtent(), src, dst, args.srcRectPair, @@ -197,13 +197,11 @@ void VulkanBlitter::terminate() noexcept { mDepthResolveProgram = nullptr; if (mTriangleBuffer) { - mTriangleBuffer->terminate(); delete mTriangleBuffer; mTriangleBuffer = nullptr; } if (mParamsBuffer) { - mParamsBuffer->terminate(); delete mParamsBuffer; mParamsBuffer = nullptr; } @@ -257,7 +255,7 @@ void VulkanBlitter::lazyInit() noexcept { }; VulkanCommandBuffer& commands = mCommands->get(); - VkCommandBuffer const cmdbuffer = commands.cmdbuffer; + VkCommandBuffer const cmdbuffer = commands.buffer(); mTriangleBuffer = new VulkanBuffer(mAllocator, mStagePool, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, sizeof(kTriangleVertices)); @@ -278,7 +276,7 @@ void VulkanBlitter::blitSlowDepth(VkFilter filter, const VkExtent2D srcExtent, V lazyInit(); VulkanCommandBuffer* commands = &mCommands->get(); - VkCommandBuffer const cmdbuffer = commands->cmdbuffer; + VkCommandBuffer const cmdbuffer = commands->buffer(); commands->acquire(src.texture); commands->acquire(dst.texture); diff --git a/filament/backend/src/vulkan/VulkanBuffer.cpp b/filament/backend/src/vulkan/VulkanBuffer.cpp index d09e2ae16d..1b5f59eafd 100644 --- a/filament/backend/src/vulkan/VulkanBuffer.cpp +++ b/filament/backend/src/vulkan/VulkanBuffer.cpp @@ -45,14 +45,7 @@ VulkanBuffer::VulkanBuffer(VmaAllocator allocator, VulkanStagePool& stagePool, } VulkanBuffer::~VulkanBuffer() { - assert_invariant(mGpuMemory == VK_NULL_HANDLE); - assert_invariant(mGpuBuffer == VK_NULL_HANDLE); -} - -void VulkanBuffer::terminate() { vmaDestroyBuffer(mAllocator, mGpuBuffer, mGpuMemory); - mGpuMemory = VK_NULL_HANDLE; - mGpuBuffer = VK_NULL_HANDLE; } void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint32_t byteOffset, diff --git a/filament/backend/src/vulkan/VulkanBuffer.h b/filament/backend/src/vulkan/VulkanBuffer.h index f9f3c7e3b6..d9a87962ce 100644 --- a/filament/backend/src/vulkan/VulkanBuffer.h +++ b/filament/backend/src/vulkan/VulkanBuffer.h @@ -28,7 +28,6 @@ public: VulkanBuffer(VmaAllocator allocator, VulkanStagePool& stagePool, VkBufferUsageFlags usage, uint32_t numBytes); ~VulkanBuffer(); - void terminate(); void loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint32_t byteOffset, uint32_t numBytes) const; VkBuffer getGpuBuffer() const { diff --git a/filament/backend/src/vulkan/VulkanCommands.cpp b/filament/backend/src/vulkan/VulkanCommands.cpp index dfbb3747fa..66a7c915a7 100644 --- a/filament/backend/src/vulkan/VulkanCommands.cpp +++ b/filament/backend/src/vulkan/VulkanCommands.cpp @@ -52,6 +52,23 @@ VulkanCmdFence::~VulkanCmdFence() { vkDestroyFence(device, fence, VKALLOC); } +VulkanCommandBuffer::VulkanCommandBuffer(VulkanResourceAllocator* allocator, VkDevice device, + VkCommandPool pool) + : mResourceManager(allocator) { + // Create the low-level command buffer. + const VkCommandBufferAllocateInfo allocateInfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + + // The buffer allocated here will be implicitly reset when vkBeginCommandBuffer is called. + vkAllocateCommandBuffers(device, &allocateInfo, &mBuffer); + + // We don't need to deallocate since destroying the pool will free all of the buffers. +} + CommandBufferObserver::~CommandBufferObserver() {} static VkCommandPool createPool(VkDevice device, uint32_t queueFamilyIndex) { @@ -130,7 +147,7 @@ VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFam } for (size_t i = 0; i < CAPACITY; ++i) { - mStorage[i] = std::make_unique(allocator); + mStorage[i] = std::make_unique(allocator, mDevice, mPool); } } @@ -151,7 +168,7 @@ VulkanCommandBuffer& VulkanCommands::get() { // If we ran out of available command buffers, stall until one finishes. This is very rare. // It occurs only when Filament invokes commit() or endFrame() a large number of times without // presenting the swap chain or waiting on a fence. - while (mAvailableCount == 0) { + while (mAvailableBufferCount == 0) { #if VK_REPORT_STALLS slog.i << "VulkanCommands has stalled. " << "If this occurs frequently, consider increasing VK_MAX_COMMAND_BUFFERS." @@ -165,7 +182,7 @@ VulkanCommandBuffer& VulkanCommands::get() { // Find an available slot. for (size_t i = 0; i < CAPACITY; ++i) { auto wrapper = mStorage[i].get(); - if (wrapper->cmdbuffer == VK_NULL_HANDLE) { + if (wrapper->buffer() == VK_NULL_HANDLE) { mCurrentCommandBufferIndex = static_cast(i); currentbuf = wrapper; break; @@ -173,16 +190,7 @@ VulkanCommandBuffer& VulkanCommands::get() { } assert_invariant(currentbuf); - --mAvailableCount; - - // Create the low-level command buffer. - const VkCommandBufferAllocateInfo allocateInfo { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .commandPool = mPool, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .commandBufferCount = 1 - }; - vkAllocateCommandBuffers(mDevice, &allocateInfo, ¤tbuf->cmdbuffer); + mAvailableBufferCount--; // Note that the fence wrapper uses shared_ptr because a DriverAPI fence can also have ownership // over it. The destruction of the low-level fence occurs either in VulkanCommands::gc(), or in @@ -194,7 +202,7 @@ VulkanCommandBuffer& VulkanCommands::get() { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; - vkBeginCommandBuffer(currentbuf->cmdbuffer, &binfo); + vkBeginCommandBuffer(currentbuf->buffer(), &binfo); // Notify the observer that a new command buffer has been activated. if (mObserver) { @@ -235,7 +243,7 @@ bool VulkanCommands::flush() { VulkanCommandBuffer const* currentbuf = mStorage[index].get(); VkSemaphore const renderingFinished = mSubmissionSignals[index]; - vkEndCommandBuffer(currentbuf->cmdbuffer); + vkEndCommandBuffer(currentbuf->buffer()); // If the injected semaphore is an "image available" semaphore that has not yet been signaled, // it is sometimes fine to start executing commands anyway, as along as we stall the GPU at the @@ -253,13 +261,15 @@ bool VulkanCommands::flush() { VK_NULL_HANDLE, }; + VkCommandBuffer const cmdbuffer = currentbuf->buffer(); + VkSubmitInfo submitInfo { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = 0, .pWaitSemaphores = signals, .pWaitDstStageMask = waitDestStageMasks, .commandBufferCount = 1, - .pCommandBuffers = ¤tbuf->cmdbuffer, + .pCommandBuffers = &cmdbuffer, .signalSemaphoreCount = 1u, .pSignalSemaphores = &renderingFinished, }; @@ -278,7 +288,7 @@ bool VulkanCommands::flush() { } #if FILAMENT_VULKAN_VERBOSE - slog.i << "Submitting cmdbuffer=" << currentbuf->cmdbuffer + slog.i << "Submitting cmdbuffer=" << cmdbuffer << " wait=(" << signals[0] << ", " << signals[1] << ") " << " signal=" << renderingFinished << io::endl; @@ -326,7 +336,7 @@ void VulkanCommands::wait() { size_t count = 0; for (size_t i = 0; i < CAPACITY; i++) { auto wrapper = mStorage[i].get(); - if (wrapper->cmdbuffer != VK_NULL_HANDLE + if (wrapper->buffer() != VK_NULL_HANDLE && mCurrentCommandBufferIndex != static_cast(i)) { fences[count++] = wrapper->fence->fence; } @@ -337,33 +347,25 @@ void VulkanCommands::wait() { } void VulkanCommands::gc() { - VkCommandBuffer buffers[CAPACITY]; - size_t count = 0; for (size_t i = 0; i < CAPACITY; i++) { auto wrapper = mStorage[i].get(); - if (wrapper->cmdbuffer == VK_NULL_HANDLE) { + if (wrapper->buffer() == VK_NULL_HANDLE) { continue; } VkResult const result = vkWaitForFences(mDevice, 1, &wrapper->fence->fence, VK_TRUE, 0); if (result != VK_SUCCESS) { continue; } - buffers[count++] = wrapper->cmdbuffer; - wrapper->cmdbuffer = VK_NULL_HANDLE; wrapper->fence->status.store(VK_SUCCESS); - wrapper->fence.reset(); - wrapper->clearResources(); - ++mAvailableCount; - } - if (count > 0) { - vkFreeCommandBuffers(mDevice, mPool, count, buffers); + wrapper->reset(); + mAvailableBufferCount++; } } void VulkanCommands::updateFences() { for (size_t i = 0; i < CAPACITY; i++) { auto wrapper = mStorage[i].get(); - if (wrapper->cmdbuffer != VK_NULL_HANDLE) { + if (wrapper->buffer() != VK_NULL_HANDLE) { VulkanCmdFence* fence = wrapper->fence.get(); if (fence) { VkResult status = vkGetFenceStatus(mDevice, fence->fence); @@ -384,7 +386,7 @@ void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timest #endif // TODO: Add group marker color to the Driver API - const VkCommandBuffer cmdbuffer = get().cmdbuffer; + VkCommandBuffer const cmdbuffer = get().buffer(); if (!mGroupMarkers) { mGroupMarkers = std::make_unique(); @@ -412,7 +414,7 @@ void VulkanCommands::popGroupMarker() { assert_invariant(mGroupMarkers); if (!mGroupMarkers->empty()) { - const VkCommandBuffer cmdbuffer = get().cmdbuffer; + VkCommandBuffer const cmdbuffer = get().buffer(); #if FILAMENT_VULKAN_VERBOSE auto const [marker, startTime] = mGroupMarkers->pop(); auto const endTime = std::chrono::high_resolution_clock::now(); @@ -437,7 +439,7 @@ void VulkanCommands::popGroupMarker() { } void VulkanCommands::insertEventMarker(char const* string, uint32_t len) { - VkCommandBuffer const cmdbuffer = get().cmdbuffer; + VkCommandBuffer const cmdbuffer = get().buffer(); if (mContext->isDebugUtilsSupported()) { VkDebugUtilsLabelEXT labelInfo = { .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, diff --git a/filament/backend/src/vulkan/VulkanCommands.h b/filament/backend/src/vulkan/VulkanCommands.h index f2c358fa36..8038ae2724 100644 --- a/filament/backend/src/vulkan/VulkanCommands.h +++ b/filament/backend/src/vulkan/VulkanCommands.h @@ -71,13 +71,10 @@ struct VulkanCmdFence { // DriverApi fence object and should not be destroyed until both the DriverApi object is freed and // we're done waiting on the most recent submission of the given command buffer. struct VulkanCommandBuffer { - VulkanCommandBuffer(VulkanResourceAllocator* allocator) - : mResourceManager(allocator) {} + VulkanCommandBuffer(VulkanResourceAllocator* allocator, VkDevice device, VkCommandPool pool); VulkanCommandBuffer(VulkanCommandBuffer const&) = delete; VulkanCommandBuffer& operator=(VulkanCommandBuffer const&) = delete; - VkCommandBuffer cmdbuffer = VK_NULL_HANDLE; - std::shared_ptr fence; inline void acquire(VulkanResource* resource) { mResourceManager.acquire(resource); @@ -87,12 +84,23 @@ struct VulkanCommandBuffer { mResourceManager.acquire(srcResources); } - inline void clearResources() { + inline void reset() { + fence.reset(); mResourceManager.clear(); } + inline VkCommandBuffer buffer() const { + if (fence) { + return mBuffer; + } + return VK_NULL_HANDLE; + } + + std::shared_ptr fence; + private: VulkanAcquireOnlyResourceManager mResourceManager; + VkCommandBuffer mBuffer; }; // Allows classes to be notified after a new command buffer has been activated. @@ -185,7 +193,7 @@ class VulkanCommands { VkSemaphore mInjectedSignal = {}; utils::FixedCapacityVector> mStorage; VkSemaphore mSubmissionSignals[CAPACITY] = {}; - size_t mAvailableCount = CAPACITY; + uint8_t mAvailableBufferCount = CAPACITY; CommandBufferObserver* mObserver = nullptr; std::unique_ptr mGroupMarkers; diff --git a/filament/backend/src/vulkan/VulkanContext.cpp b/filament/backend/src/vulkan/VulkanContext.cpp index b2c14cfe3c..f8a4e496d2 100644 --- a/filament/backend/src/vulkan/VulkanContext.cpp +++ b/filament/backend/src/vulkan/VulkanContext.cpp @@ -117,8 +117,9 @@ void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands, VulkanTimerQuery* query) { uint32_t const index = query->getStartingQueryIndex(); - vkCmdResetQueryPool(commands->cmdbuffer, mPool, index, 2); - vkCmdWriteTimestamp(commands->cmdbuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index); + auto const cmdbuffer = commands->buffer(); + vkCmdResetQueryPool(cmdbuffer, mPool, index, 2); + vkCmdWriteTimestamp(cmdbuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index); // We stash this because getResult might come before the query is actually processed. query->setFence(commands->fence); @@ -127,7 +128,7 @@ void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands, void VulkanTimestamps::endQuery(VulkanCommandBuffer const* commands, VulkanTimerQuery const* query) { uint32_t const index = query->getStoppingQueryIndex(); - vkCmdWriteTimestamp(commands->cmdbuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index); + vkCmdWriteTimestamp(commands->buffer(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index); } VulkanTimestamps::QueryResult VulkanTimestamps::getResult(VulkanTimerQuery const* query) { diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 875a583736..85bf6c17ef 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -873,7 +873,7 @@ void VulkanDriver::updateIndexBuffer(Handle ibh, BufferDescriptor VulkanCommandBuffer& commands = mCommands->get(); auto ib = mResourceAllocator.handle_cast(ibh); commands.acquire(ib); - ib->buffer.loadFromCpu(commands.cmdbuffer, p.buffer, byteOffset, p.size); + ib->buffer.loadFromCpu(commands.buffer(), p.buffer, byteOffset, p.size); scheduleDestroy(std::move(p)); } @@ -884,7 +884,7 @@ void VulkanDriver::updateBufferObject(Handle boh, BufferDescript auto bo = mResourceAllocator.handle_cast(boh); commands.acquire(bo); - bo->buffer.loadFromCpu(commands.cmdbuffer, bd.buffer, byteOffset, bd.size); + bo->buffer.loadFromCpu(commands.buffer(), bd.buffer, byteOffset, bd.size); scheduleDestroy(std::move(bd)); } @@ -895,7 +895,7 @@ void VulkanDriver::updateBufferObjectUnsynchronized(Handle boh, auto bo = mResourceAllocator.handle_cast(boh); commands.acquire(bo); // TODO: implement unsynchronized version - bo->buffer.loadFromCpu(commands.cmdbuffer, bd.buffer, byteOffset, bd.size); + bo->buffer.loadFromCpu(commands.buffer(), bd.buffer, byteOffset, bd.size); mResourceManager.acquire(bo); scheduleDestroy(std::move(bd)); } @@ -1021,7 +1021,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP // the non-sampling case. bool samplingDepthAttachment = false; VulkanCommandBuffer& commands = mCommands->get(); - VkCommandBuffer const cmdbuffer = commands.cmdbuffer; + VkCommandBuffer const cmdbuffer = commands.buffer(); UTILS_NOUNROLL for (uint8_t samplerGroupIdx = 0; samplerGroupIdx < Program::SAMPLER_BINDING_COUNT; @@ -1247,7 +1247,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP void VulkanDriver::endRenderPass(int) { VulkanCommandBuffer& commands = mCommands->get(); - VkCommandBuffer cmdbuffer = commands.cmdbuffer; + VkCommandBuffer cmdbuffer = commands.buffer(); vkCmdEndRenderPass(cmdbuffer); VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget; @@ -1299,7 +1299,7 @@ void VulkanDriver::nextSubpass(int) { assert_invariant(renderTarget); assert_invariant(mCurrentRenderPass.params.subpassMask); - vkCmdNextSubpass(mCommands->get().cmdbuffer, VK_SUBPASS_CONTENTS_INLINE); + vkCmdNextSubpass(mCommands->get().buffer(), VK_SUBPASS_CONTENTS_INLINE); mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass, ++mCurrentRenderPass.currentSubpass); @@ -1484,7 +1484,7 @@ void VulkanDriver::blit(TargetBufferFlags buffers, Handle dst, V void VulkanDriver::draw(PipelineState pipelineState, Handle rph, const uint32_t instanceCount) { VulkanCommandBuffer* commands = &mCommands->get(); - VkCommandBuffer cmdbuffer = commands->cmdbuffer; + VkCommandBuffer cmdbuffer = commands->buffer(); const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast(rph); Handle programHandle = pipelineState.program; diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index 6e9db5040a..276f2ab819 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -96,10 +96,6 @@ struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource { void setBuffer(VulkanBufferObject* bufferObject, uint32_t index); - inline void terminate() { - mResources.clear(); - } - utils::FixedCapacityVector buffers; private: @@ -114,9 +110,6 @@ struct VulkanIndexBuffer : public HwIndexBuffer, VulkanResource { buffer(allocator, stagePool, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, elementSize * indexCount), indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32) {} - void terminate() { - buffer.terminate(); - } VulkanBuffer buffer; const VkIndexType indexType; }; @@ -124,9 +117,7 @@ struct VulkanIndexBuffer : public HwIndexBuffer, VulkanResource { struct VulkanBufferObject : public HwBufferObject, VulkanResource { VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& stagePool, uint32_t byteCount, BufferObjectBinding bindingType, BufferUsage usage); - void terminate() { - buffer.terminate(); - } + VulkanBuffer buffer; const BufferObjectBinding bindingType; }; diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.cpp b/filament/backend/src/vulkan/VulkanPipelineCache.cpp index 0d74637bc7..e69cfae9a5 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.cpp +++ b/filament/backend/src/vulkan/VulkanPipelineCache.cpp @@ -166,7 +166,7 @@ bool VulkanPipelineCache::bindDescriptors(VkCommandBuffer cmdbuffer) noexcept { } bool VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) noexcept { - VkCommandBuffer const cmdbuffer = commands->cmdbuffer; + VkCommandBuffer const cmdbuffer = commands->buffer(); PipelineMap::iterator pipelineIter = mPipelines.find(mPipelineRequirements); @@ -678,7 +678,7 @@ void VulkanPipelineCache::terminate() noexcept { mDummyMemory = VK_NULL_HANDLE; } -void VulkanPipelineCache::onCommandBuffer(const VulkanCommandBuffer& cmdbuffer) { +void VulkanPipelineCache::onCommandBuffer(const VulkanCommandBuffer& commands) { // The timestamp associated with a given cache entry represents "time" as a count of flush // events since the cache was constructed. If any cache entry was most recently used over // VK_MAX_PIPELINE_AGE flush events in the past, then we can be sure that it is no longer diff --git a/filament/backend/src/vulkan/VulkanResourceAllocator.h b/filament/backend/src/vulkan/VulkanResourceAllocator.h index ab68865724..222c7be936 100644 --- a/filament/backend/src/vulkan/VulkanResourceAllocator.h +++ b/filament/backend/src/vulkan/VulkanResourceAllocator.h @@ -101,10 +101,6 @@ public: template inline void destruct(Handle handle) noexcept { auto obj = handle_cast(handle); - if constexpr (std::is_base_of_v - || std::is_base_of_v) { - obj->terminate(); - } TRACK_DECREMENT(); mHandleAllocatorImpl.deallocate(handle, obj); } diff --git a/filament/backend/src/vulkan/VulkanResources.h b/filament/backend/src/vulkan/VulkanResources.h index 61e039941f..538b07a82a 100644 --- a/filament/backend/src/vulkan/VulkanResources.h +++ b/filament/backend/src/vulkan/VulkanResources.h @@ -164,6 +164,10 @@ private: public: using const_iterator = FixedSizeArray::const_iterator; + inline ~FixedCapacityResourceSet() { + clear(); + } + inline const_iterator begin() { if (mInd == 0) { return mArray.cend(); diff --git a/filament/backend/src/vulkan/VulkanStagePool.cpp b/filament/backend/src/vulkan/VulkanStagePool.cpp index cb0f79b7e7..a8934d242b 100644 --- a/filament/backend/src/vulkan/VulkanStagePool.cpp +++ b/filament/backend/src/vulkan/VulkanStagePool.cpp @@ -111,7 +111,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi assert_invariant(result == VK_SUCCESS); VkImageAspectFlags const aspectFlags = getImageAspect(vkformat); - const VkCommandBuffer cmdbuffer = mCommands->get().cmdbuffer; + VkCommandBuffer const cmdbuffer = mCommands->get().buffer(); // We use VK_IMAGE_LAYOUT_GENERAL here because the spec says: // "Host access to image memory is only well-defined for linear images and for image diff --git a/filament/backend/src/vulkan/VulkanSwapChain.cpp b/filament/backend/src/vulkan/VulkanSwapChain.cpp index 286499f78b..d6c43b62f6 100644 --- a/filament/backend/src/vulkan/VulkanSwapChain.cpp +++ b/filament/backend/src/vulkan/VulkanSwapChain.cpp @@ -79,7 +79,7 @@ void VulkanSwapChain::update() { void VulkanSwapChain::present() { if (!mHeadless) { - VkCommandBuffer const cmdbuf = mCommands->get().cmdbuffer; + VkCommandBuffer const cmdbuf = mCommands->get().buffer(); VkImageSubresourceRange const subresources{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index 1a58d6f9dc..140c697f87 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -227,7 +227,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VkImageSubresourceRange range = { getImageAspect(), 0, levels, 0, layers }; VulkanCommandBuffer& commands = mCommands->get(); - VkCommandBuffer const cmdbuf = commands.cmdbuffer; + VkCommandBuffer const cmdbuf = commands.buffer(); commands.acquire(this); transitionLayout(cmdbuf, range, ImgUtil::getDefaultLayout(imageInfo.usage)); @@ -280,7 +280,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt vmaFlushAllocation(mAllocator, stage->memory, 0, hostData->size); VulkanCommandBuffer& commands = mCommands->get(); - VkCommandBuffer const cmdbuf = commands.cmdbuffer; + VkCommandBuffer const cmdbuf = commands.buffer(); commands.acquire(this); VkBufferImageCopy copyRegion = { @@ -343,7 +343,7 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u vmaFlushAllocation(mAllocator, stage->memory, 0, hostData.size); VulkanCommandBuffer& commands = mCommands->get(); - VkCommandBuffer const cmdbuf = commands.cmdbuffer; + VkCommandBuffer const cmdbuf = commands.buffer(); commands.acquire(this); // TODO: support blit-based format conversion for 3D images and cubemaps. From 21149954892e51f9337698c05c100908e91db22a Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Wed, 13 Sep 2023 12:33:10 -0700 Subject: [PATCH 13/19] Implement setMinMaxLevels for Metal (#7158) --- filament/backend/CMakeLists.txt | 1 + filament/backend/src/metal/MetalDriver.mm | 42 ++--- filament/backend/src/metal/MetalHandles.h | 15 +- filament/backend/src/metal/MetalHandles.mm | 14 +- filament/backend/test/BackendTest.cpp | 61 -------- filament/backend/test/BackendTest.h | 6 - filament/backend/test/BackendTestUtils.h | 174 +++++++++++++++++++++ filament/backend/test/test_LoadImage.cpp | 85 +--------- filament/backend/test/test_MipLevels.cpp | 171 ++++++++++++++++++++ filament/backend/test/test_ReadPixels.cpp | 1 + 10 files changed, 370 insertions(+), 200 deletions(-) create mode 100644 filament/backend/test/BackendTestUtils.h create mode 100644 filament/backend/test/test_MipLevels.cpp diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index 9129099800..3d35d38d22 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -412,6 +412,7 @@ if (APPLE) test/test_RenderExternalImage.cpp test/test_StencilBuffer.cpp test/test_Scissor.cpp + test/test_MipLevels.cpp ) target_link_libraries(backend_test PRIVATE diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 1d036d90a0..9fbba163bc 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -345,7 +345,7 @@ void MetalDriver::createRenderTargetR(Handle rth, auto colorTexture = handle_cast(buffer.handle); ASSERT_PRECONDITION(colorTexture->getMtlTextureForWrite(), "Color texture passed to render target has no texture allocation"); - colorTexture->updateLodRange(buffer.level); + colorTexture->extendLodRangeTo(buffer.level); colorAttachments[i] = { colorTexture, color[i].level, color[i].layer }; } @@ -356,7 +356,7 @@ void MetalDriver::createRenderTargetR(Handle rth, auto depthTexture = handle_cast(depth.handle); ASSERT_PRECONDITION(depthTexture->getMtlTextureForWrite(), "Depth texture passed to render target has no texture allocation."); - depthTexture->updateLodRange(depth.level); + depthTexture->extendLodRangeTo(depth.level); depthAttachment = { depthTexture, depth.level, depth.layer }; } @@ -367,7 +367,7 @@ void MetalDriver::createRenderTargetR(Handle rth, auto stencilTexture = handle_cast(stencil.handle); ASSERT_PRECONDITION(stencilTexture->getMtlTextureForWrite(), "Stencil texture passed to render target has no texture allocation."); - stencilTexture->updateLodRange(stencil.level); + stencilTexture->extendLodRangeTo(stencil.level); stencilAttachment = { stencilTexture, stencil.level, stencil.layer }; } @@ -789,6 +789,8 @@ void MetalDriver::setVertexBufferObject(Handle vbh, uint32_t ind } void MetalDriver::setMinMaxLevels(Handle th, uint32_t minLevel, uint32_t maxLevel) { + auto tex = handle_cast(th); + tex->setLodRange(minLevel, maxLevel); } void MetalDriver::update3DImage(Handle th, uint32_t level, @@ -900,14 +902,13 @@ void MetalDriver::updateSamplerGroup(Handle sbh, BufferDescripto // 2. LOD-clamped textures // // Both of these cases prevent us from knowing the final id that will be bound into - // the argument buffer representing the sampler group. So, we bind what we can now and wait - // until draw call time to bind any special cases (done in finalizeSamplerGroup). + // the argument buffer representing the sampler group. So, we wait until draw call time to bind + // textures (done in finalizeSamplerGroup). // The good news is that once a render pass has started, the texture bindings won't change. // A SamplerGroup is "finalized" when all of its textures have been set and is ready for use in // a draw call. - // Even if we do know all the final textures at this point, we still wait until draw call time - // to call finalizeSamplerGroup, which has one additional responsibility: to call useResources - // for all the textures, which is required by Metal. + // finalizeSamplerGroup has one additional responsibility: to call useResources for all the + // textures, which is required by Metal. for (size_t s = 0; s < data.size / sizeof(SamplerDescriptor); s++) { if (!samplers[s].t) { // Assign a default texture / sampler to empty slots. @@ -930,27 +931,6 @@ void MetalDriver::updateSamplerGroup(Handle sbh, BufferDescripto sb->setFinalizedSampler(s, sampler); sb->setTextureHandle(s, samplers[s].t); - - auto* t = handle_cast(samplers[s].t); - assert_invariant(t); - - // If this texture is an external texture, we defer binding the texture until draw call time - // (in finalizeSamplerGroup). - if (t->target == SamplerType::SAMPLER_EXTERNAL) { - continue; - } - - if (!t->allLodsValid()) { - // The texture doesn't have all of its LODs loaded, and this could change by the time we - // issue a draw call with this sampler group. So, we defer binding the texture until - // draw call time (in finalizeSamplerGroup). - continue; - } - - // If we get here, we know we have a valid MTLTexture that's guaranteed not to change. - id mtlTexture = t->getMtlTextureForRead(); - assert_invariant(mtlTexture); - sb->setFinalizedTexture(s, mtlTexture); } scheduleDestroy(std::move(data)); @@ -1376,8 +1356,8 @@ void MetalDriver::blit(TargetBufferFlags buffers, } void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) { - // All of the id objects have already been bound to the argument buffer. - // Here we bind any textures that were unable to be bound in updateSamplerGroup. + // All the id objects have already been bound to the argument buffer. + // Here we bind all the textures. id cmdBuffer = getPendingCommandBuffer(mContext); diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index b129d478d7..f21891a701 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -217,21 +217,14 @@ public: void generateMipmaps() noexcept; // A texture starts out with none of its mip levels (also referred to as LODs) available for - // reading. 3 actions update the range of LODs available: + // reading. 4 actions update the range of LODs available: // - calling loadImage // - calling generateMipmaps // - using the texture as a render target attachment - // The range of available mips can only increase, never decrease. + // - calling setMinMaxLevels // A texture's available mips are consistent throughout a render pass. - void updateLodRange(uint32_t level); - void updateLodRange(uint32_t minLevel, uint32_t maxLevel); - - // Returns true if the texture has all of its mip levels accessible for reading. - // For any MetalTexture, once this is true, will always return true. - // The value returned will remain consistent for an entire render pass. - bool allLodsValid() const { - return minLod == 0 && maxLod == levels - 1; - } + void setLodRange(uint32_t minLevel, uint32_t maxLevel); + void extendLodRangeTo(uint32_t level); static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format); diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 0b4d0b3c4d..ed8a894ffb 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -513,7 +513,7 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le : HwTexture(target, levels, samples, width, height, depth, format, usage), context(context), externalImage(context) { texture = metalTexture; - updateLodRange(0, levels - 1); + setLodRange(0, levels - 1); } MetalTexture::~MetalTexture() { @@ -658,14 +658,14 @@ void MetalTexture::loadImage(uint32_t level, MTLRegion region, PixelBufferDescri } } - updateLodRange(level); + extendLodRangeTo(level); } void MetalTexture::generateMipmaps() noexcept { id blitEncoder = [getPendingCommandBuffer(&context) blitCommandEncoder]; [blitEncoder generateMipmapsForTexture:texture]; [blitEncoder endEncoding]; - updateLodRange(0, texture.mipmapLevelCount - 1); + setLodRange(0, texture.mipmapLevelCount - 1); } void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice, @@ -788,18 +788,18 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region context.blitter->blit(getPendingCommandBuffer(&context), args, "Texture upload blit"); } -void MetalTexture::updateLodRange(uint32_t level) { +void MetalTexture::extendLodRangeTo(uint32_t level) { assert_invariant(!isInRenderPass(&context)); minLod = std::min(minLod, level); maxLod = std::max(maxLod, level); lodTextureView = nil; } -void MetalTexture::updateLodRange(uint32_t min, uint32_t max) { +void MetalTexture::setLodRange(uint32_t min, uint32_t max) { assert_invariant(!isInRenderPass(&context)); assert_invariant(min <= max); - minLod = std::min(minLod, min); - maxLod = std::max(maxLod, max); + minLod = min; + maxLod = max; lodTextureView = nil; } diff --git a/filament/backend/test/BackendTest.cpp b/filament/backend/test/BackendTest.cpp index c061d721f7..70261e8bf8 100644 --- a/filament/backend/test/BackendTest.cpp +++ b/filament/backend/test/BackendTest.cpp @@ -211,66 +211,5 @@ int runTests() { return RUN_ALL_TESTS(); } -void getPixelInfo(PixelDataFormat format, PixelDataType type, size_t& outComponents, int& outBpp) { - assert_invariant(type != PixelDataType::COMPRESSED); - switch (format) { - case PixelDataFormat::UNUSED: - case PixelDataFormat::R: - case PixelDataFormat::R_INTEGER: - case PixelDataFormat::DEPTH_COMPONENT: - case PixelDataFormat::ALPHA: - outComponents = 1; - break; - case PixelDataFormat::RG: - case PixelDataFormat::RG_INTEGER: - case PixelDataFormat::DEPTH_STENCIL: - outComponents = 2; - break; - case PixelDataFormat::RGB: - case PixelDataFormat::RGB_INTEGER: - outComponents = 3; - break; - case PixelDataFormat::RGBA: - case PixelDataFormat::RGBA_INTEGER: - outComponents = 4; - break; - } - - outBpp = outComponents; - switch (type) { - case PixelDataType::COMPRESSED: // Impossible -- to squash the IDE warnings - case PixelDataType::UBYTE: - case PixelDataType::BYTE: - // nothing to do - break; - case PixelDataType::USHORT: - case PixelDataType::SHORT: - case PixelDataType::HALF: - outBpp *= 2; - break; - case PixelDataType::UINT: - case PixelDataType::INT: - case PixelDataType::FLOAT: - outBpp *= 4; - break; - case PixelDataType::UINT_10F_11F_11F_REV: - // Special case, format must be RGB and uses 4 bytes - assert_invariant(format == PixelDataFormat::RGB); - outBpp = 4; - break; - case PixelDataType::UINT_2_10_10_10_REV: - // Special case, format must be RGBA and uses 4 bytes - assert_invariant(format == PixelDataFormat::RGBA); - outBpp = 4; - break; - case PixelDataType::USHORT_565: - // Special case, format must be RGB and uses 2 bytes - assert_invariant(format == PixelDataFormat::RGB); - outBpp = 2; - break; - } -} - - } // namespace test diff --git a/filament/backend/test/BackendTest.h b/filament/backend/test/BackendTest.h index 933c076d67..777a96f040 100644 --- a/filament/backend/test/BackendTest.h +++ b/filament/backend/test/BackendTest.h @@ -75,12 +75,6 @@ private: filament::backend::Handle uniform; }; - -// Utilities - -void getPixelInfo(filament::backend::PixelDataFormat format, filament::backend::PixelDataType type, - size_t& outComponents, int& outBpp); - } // namespace test #endif diff --git a/filament/backend/test/BackendTestUtils.h b/filament/backend/test/BackendTestUtils.h new file mode 100644 index 0000000000..209ea4abd4 --- /dev/null +++ b/filament/backend/test/BackendTestUtils.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef TNT_BACKENDTESTUTILS_H +#define TNT_BACKENDTESTUTILS_H + +#include + +#include + +using namespace filament; +using namespace filament::backend; + +inline void getPixelInfo(PixelDataFormat format, PixelDataType type, size_t& outComponents, int& outBpp) { + assert_invariant(type != PixelDataType::COMPRESSED); + switch (format) { + case PixelDataFormat::UNUSED: + case PixelDataFormat::R: + case PixelDataFormat::R_INTEGER: + case PixelDataFormat::DEPTH_COMPONENT: + case PixelDataFormat::ALPHA: + outComponents = 1; + break; + case PixelDataFormat::RG: + case PixelDataFormat::RG_INTEGER: + case PixelDataFormat::DEPTH_STENCIL: + outComponents = 2; + break; + case PixelDataFormat::RGB: + case PixelDataFormat::RGB_INTEGER: + outComponents = 3; + break; + case PixelDataFormat::RGBA: + case PixelDataFormat::RGBA_INTEGER: + outComponents = 4; + break; + } + + outBpp = outComponents; + switch (type) { + case PixelDataType::COMPRESSED: // Impossible -- to squash the IDE warnings + case PixelDataType::UBYTE: + case PixelDataType::BYTE: + // nothing to do + break; + case PixelDataType::USHORT: + case PixelDataType::SHORT: + case PixelDataType::HALF: + outBpp *= 2; + break; + case PixelDataType::UINT: + case PixelDataType::INT: + case PixelDataType::FLOAT: + outBpp *= 4; + break; + case PixelDataType::UINT_10F_11F_11F_REV: + // Special case, format must be RGB and uses 4 bytes + assert_invariant(format == PixelDataFormat::RGB); + outBpp = 4; + break; + case PixelDataType::UINT_2_10_10_10_REV: + // Special case, format must be RGBA and uses 4 bytes + assert_invariant(format == PixelDataFormat::RGBA); + outBpp = 4; + break; + case PixelDataType::USHORT_565: + // Special case, format must be RGB and uses 2 bytes + assert_invariant(format == PixelDataFormat::RGB); + outBpp = 2; + break; + } +} + +template +static void fillCheckerboard(void* buffer, size_t size, size_t stride, size_t components, + ComponentType value) { + ComponentType* row = (ComponentType*)buffer; + int p = 0; + for (int r = 0; r < size; r++) { + ComponentType* pixel = row; + for (int col = 0; col < size; col++) { + // Generate a checkerboard pattern. + if ((p & 0x0010) ^ ((p / size) & 0x0010)) { + // Turn on the first component (red). + pixel[0] = value; + } + pixel += components; + p++; + } + row += stride * components; + } +} + +static PixelBufferDescriptor checkerboardPixelBuffer(PixelDataFormat format, PixelDataType type, + size_t size, size_t bufferPadding = 0) { + size_t components; int bpp; + getPixelInfo(format, type, components, bpp); + + size_t bufferSize = size + bufferPadding * 2; + uint8_t* buffer = (uint8_t*) calloc(1, bufferSize * bufferSize * bpp); + + uint8_t* ptr = buffer + (bufferSize * bufferPadding * bpp) + (bufferPadding * bpp); + + switch (type) { + case PixelDataType::BYTE: + fillCheckerboard(ptr, size, bufferSize, components, 1); + break; + + case PixelDataType::UBYTE: + fillCheckerboard(ptr, size, bufferSize, components, 0xFF); + break; + + case PixelDataType::SHORT: + fillCheckerboard(ptr, size, bufferSize, components, 1); + break; + + case PixelDataType::USHORT: + fillCheckerboard(ptr, size, bufferSize, components, 1u); + break; + + case PixelDataType::UINT: + fillCheckerboard(ptr, size, bufferSize, components, 1u); + break; + + case PixelDataType::INT: + fillCheckerboard(ptr, size, bufferSize, components, 1); + break; + + case PixelDataType::FLOAT: + fillCheckerboard(ptr, size, bufferSize, components, 1.0f); + break; + + case PixelDataType::HALF: + fillCheckerboard(ptr, size, bufferSize, components, math::half(1.0f)); + break; + + case PixelDataType::UINT_2_10_10_10_REV: + fillCheckerboard(ptr, size, bufferSize, 1, 0xC00003FF /* red */); + break; + + case PixelDataType::USHORT_565: + fillCheckerboard(ptr, size, bufferSize, 1, 0xF800 /* red */); + break; + + case PixelDataType::UINT_10F_11F_11F_REV: + fillCheckerboard(ptr, size, bufferSize, 1, 0x000003C0 /* red */); + break; + + case PixelDataType::COMPRESSED: + break; + } + + PixelBufferDescriptor descriptor(buffer, bufferSize * bufferSize * bpp, format, type, + 1, bufferPadding, bufferPadding, bufferSize, [](void* buffer, size_t size, void* user) { + free(buffer); + }, nullptr); + return descriptor; +} + +#endif // TNT_BACKENDTESTUTILS_H diff --git a/filament/backend/test/test_LoadImage.cpp b/filament/backend/test/test_LoadImage.cpp index f4797f7aae..ddfab69a8a 100644 --- a/filament/backend/test/test_LoadImage.cpp +++ b/filament/backend/test/test_LoadImage.cpp @@ -18,6 +18,7 @@ #include "ShaderGenerator.h" #include "TrianglePrimitive.h" +#include "BackendTestUtils.h" #include "private/filament/SamplerInterfaceBlock.h" #include "private/backend/SamplerGroup.h" @@ -113,91 +114,7 @@ namespace test { template inline componentType getMaxValue(); -template -static void fillCheckerboard(void* buffer, size_t size, size_t stride, size_t components, - ComponentType value) { - ComponentType* row = (ComponentType*)buffer; - int p = 0; - for (int r = 0; r < size; r++) { - ComponentType* pixel = row; - for (int col = 0; col < size; col++) { - // Generate a checkerboard pattern. - if ((p & 0x0010) ^ ((p / size) & 0x0010)) { - // Turn on the first component (red). - pixel[0] = value; - } - pixel += components; - p++; - } - row += stride * components; - } -} -static PixelBufferDescriptor checkerboardPixelBuffer(PixelDataFormat format, PixelDataType type, - size_t size, size_t bufferPadding = 0) { - size_t components; int bpp; - getPixelInfo(format, type, components, bpp); - - size_t bufferSize = size + bufferPadding * 2; - uint8_t* buffer = (uint8_t*) calloc(1, bufferSize * bufferSize * bpp); - - uint8_t* ptr = buffer + (bufferSize * bufferPadding * bpp) + (bufferPadding * bpp); - - switch (type) { - case PixelDataType::BYTE: - fillCheckerboard(ptr, size, bufferSize, components, 1); - break; - - case PixelDataType::UBYTE: - fillCheckerboard(ptr, size, bufferSize, components, 0xFF); - break; - - case PixelDataType::SHORT: - fillCheckerboard(ptr, size, bufferSize, components, 1); - break; - - case PixelDataType::USHORT: - fillCheckerboard(ptr, size, bufferSize, components, 1u); - break; - - case PixelDataType::UINT: - fillCheckerboard(ptr, size, bufferSize, components, 1u); - break; - - case PixelDataType::INT: - fillCheckerboard(ptr, size, bufferSize, components, 1); - break; - - case PixelDataType::FLOAT: - fillCheckerboard(ptr, size, bufferSize, components, 1.0f); - break; - - case PixelDataType::HALF: - fillCheckerboard(ptr, size, bufferSize, components, math::half(1.0f)); - break; - - case PixelDataType::UINT_2_10_10_10_REV: - fillCheckerboard(ptr, size, bufferSize, 1, 0xC00003FF /* red */); - break; - - case PixelDataType::USHORT_565: - fillCheckerboard(ptr, size, bufferSize, 1, 0xF800 /* red */); - break; - - case PixelDataType::UINT_10F_11F_11F_REV: - fillCheckerboard(ptr, size, bufferSize, 1, 0x000003C0 /* red */); - break; - - case PixelDataType::COMPRESSED: - break; - } - - PixelBufferDescriptor descriptor(buffer, bufferSize * bufferSize * bpp, format, type, - 1, bufferPadding, bufferPadding, bufferSize, [](void* buffer, size_t size, void* user) { - free(buffer); - }, nullptr); - return descriptor; -} inline std::string stringReplace(const std::string& find, const std::string& replace, std::string source) { diff --git a/filament/backend/test/test_MipLevels.cpp b/filament/backend/test/test_MipLevels.cpp new file mode 100644 index 0000000000..e677e904d5 --- /dev/null +++ b/filament/backend/test/test_MipLevels.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "BackendTest.h" + +#include "ShaderGenerator.h" +#include "TrianglePrimitive.h" +#include "BackendTestUtils.h" + +#include "private/backend/SamplerGroup.h" + +namespace { + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Shaders +//////////////////////////////////////////////////////////////////////////////////////////////////// + +std::string vertex (R"(#version 450 core + +layout(location = 0) in vec4 mesh_position; +layout(location = 0) out vec2 uv; + +void main() { + gl_Position = vec4(mesh_position.xy, 0.0, 1.0); + uv = (mesh_position.xy * 0.5 + 0.5); +} +)"); + +std::string fragment (R"(#version 450 core + +layout(location = 0) out vec4 fragColor; +layout(location = 0) in vec2 uv; + +layout(location = 0, set = 1) uniform sampler2D backend_test_sib_tex; + +void main() { + fragColor = textureLod(backend_test_sib_tex, uv, 1); +} +)"); + +} + +namespace test { + +using namespace filament; +using namespace filament::backend; + +TEST_F(BackendTest, SetMinMaxLevel) { + auto& api = getDriverApi(); + api.startCapture(0); + + // The test is executed within this block scope to force destructors to run before + // executeCommands(). + { + // Create a SwapChain and make it current. + auto swapChain = createSwapChain(); + api.makeCurrent(swapChain, swapChain); + + // Create a program that samples a texture. + SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() + .name("backend_test_sib") + .stageFlags(backend::ShaderStageFlags::FRAGMENT) + .add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) + .build(); + ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); + Program p = shaderGen.getProgram(api); + Program::Sampler sampler { utils::CString("backend_test_sib_tex"), 0 }; + p.setSamplerGroup(0, ShaderStageFlags::FRAGMENT, &sampler, 1); + auto program = api.createProgram(std::move(p)); + + // Create a texture that has 4 mip levels. Each level is a different color. + // Level 0: 128x128 (red) + // Level 1: 64x64 (green) + // Level 2: 32x32 (blue) + // Level 3: 16x16 (yellow) + const size_t kTextureSize = 128; + const size_t kMipLevels = 4; + Handle texture = api.createTexture(SamplerType::SAMPLER_2D, kMipLevels, + TextureFormat::RGBA8, 1, kTextureSize, kTextureSize, 1, + TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE); + + // Create image data. + auto pixelFormat = PixelDataFormat::RGBA; + auto pixelType = PixelDataType::UBYTE; + size_t components; int bpp; + getPixelInfo(pixelFormat, pixelType, components, bpp); + uint32_t colors[] = { + 0xFF0000FF, /* red */ + 0xFF00FF00, /* green */ + 0xFFFF0000, /* blue */ + 0xFF00FFFF, /* yellow */ + }; + for (int l = 0; l < kMipLevels; l++) { + size_t mipSize = kTextureSize >> l; + auto* buffer = (uint8_t*)calloc(1, mipSize * mipSize * bpp); + fillCheckerboard(buffer, mipSize, mipSize, 1, colors[l]); + PixelBufferDescriptor descriptor( + buffer, mipSize * mipSize * bpp, pixelFormat, pixelType, 1, 0, 0, + mipSize, [](void* buffer, size_t size, void* user) { free(buffer); }, + nullptr); + api.update3DImage( + texture, l, 0, 0, 0, mipSize, mipSize, 1, std::move(descriptor)); + } + + api.setMinMaxLevels(texture, 1, 3); + + backend::Handle defaultRenderTarget = api.createDefaultRenderTarget(0); + + RenderPassParams params = {}; + fullViewport(params); + params.flags.clear = TargetBufferFlags::COLOR; + params.clearColor = {0.f, 0.f, 0.5f, 1.f}; + params.flags.discardStart = TargetBufferFlags::ALL; + params.flags.discardEnd = TargetBufferFlags::NONE; + + PipelineState state; + state.scissor = params.viewport; + state.program = program; + state.rasterState.colorWrite = true; + state.rasterState.depthWrite = false; + state.rasterState.depthFunc = SamplerCompareFunc::A; + state.rasterState.culling = CullingMode::NONE; + + api.beginFrame(0, 0); + + SamplerGroup samplers(1); + SamplerParams samplerParams {}; + samplerParams.filterMag = SamplerMagFilter::NEAREST; + samplerParams.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST; + samplers.setSampler(0, { texture, samplerParams }); + backend::Handle samplerGroup = api.createSamplerGroup(1); + api.updateSamplerGroup(samplerGroup, samplers.toBufferDescriptor(api)); + api.bindSamplers(0, samplerGroup); + + // Render a triangle to the screen, sampling from mip level 1. + // Because the min level is 1, the result color should be blue. + TrianglePrimitive triangle(api); + api.beginRenderPass(defaultRenderTarget, params); + api.draw(state, triangle.getRenderPrimitive(), 1); + api.endRenderPass(); + + api.commit(swapChain); + api.endFrame(0); + + api.stopCapture(0); + + // Cleanup. + api.destroySwapChain(swapChain); + } + + api.finish(); + + executeCommands(); + getDriver().purge(); +} + +} // namespace test \ No newline at end of file diff --git a/filament/backend/test/test_ReadPixels.cpp b/filament/backend/test/test_ReadPixels.cpp index e824792602..34b2a12337 100644 --- a/filament/backend/test/test_ReadPixels.cpp +++ b/filament/backend/test/test_ReadPixels.cpp @@ -18,6 +18,7 @@ #include "ShaderGenerator.h" #include "TrianglePrimitive.h" +#include "BackendTestUtils.h" #include From d4d03e4a35a5884a2057a6c2bd6ba20de0376d63 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 13 Sep 2023 21:17:31 -0700 Subject: [PATCH 14/19] vulkan: reset fences instead create/destroy (#7169) We allocate all the fences beforehand to reduce calls to vkCreateFence. Also remove blocking code in `getFenceStatus` since there is not a usecase that would require that. --- .../backend/src/vulkan/VulkanCommands.cpp | 123 +++++++++--------- filament/backend/src/vulkan/VulkanCommands.h | 6 +- filament/backend/src/vulkan/VulkanDriver.cpp | 17 ++- 3 files changed, 75 insertions(+), 71 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanCommands.cpp b/filament/backend/src/vulkan/VulkanCommands.cpp index 66a7c915a7..8a2b2c61a7 100644 --- a/filament/backend/src/vulkan/VulkanCommands.cpp +++ b/filament/backend/src/vulkan/VulkanCommands.cpp @@ -35,23 +35,14 @@ namespace filament::backend { using Timestamp = VulkanGroupMarkers::Timestamp; -VulkanCmdFence::VulkanCmdFence(VkDevice device, bool signaled) : device(device) { - VkFenceCreateInfo fenceCreateInfo { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; - if (signaled) { - fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - } - vkCreateFence(device, &fenceCreateInfo, VKALLOC, &fence); - +VulkanCmdFence::VulkanCmdFence(VkFence ifence) + : fence(ifence) { // Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence gets // submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually finishes // executing the command buffer, the status changes to VK_SUCCESS. status.store(VK_INCOMPLETE); } -VulkanCmdFence::~VulkanCmdFence() { - vkDestroyFence(device, fence, VKALLOC); -} - VulkanCommandBuffer::VulkanCommandBuffer(VulkanResourceAllocator* allocator, VkDevice device, VkCommandPool pool) : mResourceManager(allocator) { @@ -64,19 +55,18 @@ VulkanCommandBuffer::VulkanCommandBuffer(VulkanResourceAllocator* allocator, VkD }; // The buffer allocated here will be implicitly reset when vkBeginCommandBuffer is called. - vkAllocateCommandBuffers(device, &allocateInfo, &mBuffer); - // We don't need to deallocate since destroying the pool will free all of the buffers. + vkAllocateCommandBuffers(device, &allocateInfo, &mBuffer); } CommandBufferObserver::~CommandBufferObserver() {} static VkCommandPool createPool(VkDevice device, uint32_t queueFamilyIndex) { VkCommandPoolCreateInfo createInfo = { - .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - .flags = - VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, - .queueFamilyIndex = queueFamilyIndex, + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = + VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, + .queueFamilyIndex = queueFamilyIndex, }; VkCommandPool pool; vkCreateCommandPool(device, &createInfo, VKALLOC, &pool); @@ -123,7 +113,7 @@ std::pair VulkanGroupMarkers::top() const { assert_invariant(!empty()); auto const marker = mMarkers.back(); #if FILAMENT_VULKAN_VERBOSE - auto const topTimestamp = mTimestamps.top(); + auto const topTimestamp = mTimestamps.front(); return std::make_pair(marker, topTimestamp); #else return std::make_pair(marker, Timestamp{}); @@ -146,6 +136,11 @@ VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFam vkCreateSemaphore(mDevice, &sci, nullptr, &semaphore); } + VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO}; + for (auto& fence: mFences) { + vkCreateFence(device, &fenceCreateInfo, VKALLOC, &fence); + } + for (size_t i = 0; i < CAPACITY; ++i) { mStorage[i] = std::make_unique(allocator, mDevice, mPool); } @@ -155,9 +150,12 @@ VulkanCommands::~VulkanCommands() { wait(); gc(); vkDestroyCommandPool(mDevice, mPool, VKALLOC); - for (VkSemaphore sema : mSubmissionSignals) { + for (VkSemaphore sema: mSubmissionSignals) { vkDestroySemaphore(mDevice, sema, VKALLOC); } + for (VkFence fence: mFences) { + vkDestroyFence(mDevice, fence, VKALLOC); + } } VulkanCommandBuffer& VulkanCommands::get() { @@ -170,9 +168,9 @@ VulkanCommandBuffer& VulkanCommands::get() { // presenting the swap chain or waiting on a fence. while (mAvailableBufferCount == 0) { #if VK_REPORT_STALLS - slog.i << "VulkanCommands has stalled. " - << "If this occurs frequently, consider increasing VK_MAX_COMMAND_BUFFERS." - << io::endl; + slog.i << "VulkanCommands has stalled. " + << "If this occurs frequently, consider increasing VK_MAX_COMMAND_BUFFERS." + << io::endl; #endif wait(); gc(); @@ -195,12 +193,12 @@ VulkanCommandBuffer& VulkanCommands::get() { // Note that the fence wrapper uses shared_ptr because a DriverAPI fence can also have ownership // over it. The destruction of the low-level fence occurs either in VulkanCommands::gc(), or in // VulkanDriver::destroyFence(), both of which are safe spots. - currentbuf->fence = std::make_shared(mDevice); + currentbuf->fence = std::make_shared(mFences[mCurrentCommandBufferIndex]); // Begin writing into the command buffer. - const VkCommandBufferBeginInfo binfo { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + const VkCommandBufferBeginInfo binfo{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; vkBeginCommandBuffer(currentbuf->buffer(), &binfo); @@ -215,7 +213,6 @@ VulkanCommandBuffer& VulkanCommands::get() { auto [marker, time] = mCarriedOverMarkers->pop(); pushGroupMarker(marker.c_str(), time); } - return *currentbuf; } @@ -225,7 +222,6 @@ bool VulkanCommands::flush() { return false; } - // Before actually submitting, we need to pop any leftover group markers. // Note that this needs to occur before vkEndCommandBuffer. while (mGroupMarkers && !mGroupMarkers->empty()) { @@ -252,26 +248,26 @@ bool VulkanCommands::flush() { // the only safe option because the previously submitted command buffer might have set up some // state that the new command buffer depends on. VkPipelineStageFlags waitDestStageMasks[2] = { - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, }; VkSemaphore signals[2] = { - VK_NULL_HANDLE, - VK_NULL_HANDLE, + VK_NULL_HANDLE, + VK_NULL_HANDLE, }; VkCommandBuffer const cmdbuffer = currentbuf->buffer(); - VkSubmitInfo submitInfo { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .waitSemaphoreCount = 0, - .pWaitSemaphores = signals, - .pWaitDstStageMask = waitDestStageMasks, - .commandBufferCount = 1, - .pCommandBuffers = &cmdbuffer, - .signalSemaphoreCount = 1u, - .pSignalSemaphores = &renderingFinished, + VkSubmitInfo submitInfo{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .waitSemaphoreCount = 0, + .pWaitSemaphores = signals, + .pWaitDstStageMask = waitDestStageMasks, + .commandBufferCount = 1, + .pCommandBuffers = &cmdbuffer, + .signalSemaphoreCount = 1u, + .pSignalSemaphores = &renderingFinished, }; if (mSubmissionSignal) { @@ -289,9 +285,9 @@ bool VulkanCommands::flush() { #if FILAMENT_VULKAN_VERBOSE slog.i << "Submitting cmdbuffer=" << cmdbuffer - << " wait=(" << signals[0] << ", " << signals[1] << ") " - << " signal=" << renderingFinished - << io::endl; + << " wait=(" << signals[0] << ", " << signals[1] << ") " + << " signal=" << renderingFinished + << io::endl; #endif auto& cmdfence = currentbuf->fence; @@ -303,7 +299,7 @@ bool VulkanCommands::flush() { #if FILAMENT_VULKAN_VERBOSE if (result != VK_SUCCESS) { - utils::slog.d <<"Failed command buffer submission result: " << result << utils::io::endl; + utils::slog.d << "Failed command buffer submission result: " << result << utils::io::endl; } #endif assert_invariant(result == VK_SUCCESS); @@ -343,23 +339,32 @@ void VulkanCommands::wait() { } if (count > 0) { vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX); + vkResetFences(mDevice, count, fences); } } void VulkanCommands::gc() { + VkFence fences[CAPACITY]; + size_t count = 0; + for (size_t i = 0; i < CAPACITY; i++) { auto wrapper = mStorage[i].get(); if (wrapper->buffer() == VK_NULL_HANDLE) { continue; } - VkResult const result = vkWaitForFences(mDevice, 1, &wrapper->fence->fence, VK_TRUE, 0); + VkResult const result = vkGetFenceStatus(mDevice, wrapper->fence->fence); if (result != VK_SUCCESS) { continue; } + fences[count++] = wrapper->fence->fence; wrapper->fence->status.store(VK_SUCCESS); wrapper->reset(); mAvailableBufferCount++; } + + if (count > 0) { + vkResetFences(mDevice, count, fences); + } } void VulkanCommands::updateFences() { @@ -411,19 +416,19 @@ void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timest } void VulkanCommands::popGroupMarker() { - assert_invariant(mGroupMarkers); + assert_invariant(mGroupMarkers); if (!mGroupMarkers->empty()) { VkCommandBuffer const cmdbuffer = get().buffer(); - #if FILAMENT_VULKAN_VERBOSE - auto const [marker, startTime] = mGroupMarkers->pop(); - auto const endTime = std::chrono::high_resolution_clock::now(); - std::chrono::duration diff = endTime - startTime; - utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms\n" - << utils::io::flush; - #else - mGroupMarkers->pop(); - #endif +#if FILAMENT_VULKAN_VERBOSE + auto const [marker, startTime] = mGroupMarkers->pop(); + auto const endTime = std::chrono::high_resolution_clock::now(); + std::chrono::duration diff = endTime - startTime; + utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms\n" + << utils::io::flush; +#else + mGroupMarkers->pop(); +#endif if (mContext->isDebugUtilsSupported()) { vkCmdEndDebugUtilsLabelEXT(cmdbuffer); @@ -449,9 +454,9 @@ void VulkanCommands::insertEventMarker(char const* string, uint32_t len) { vkCmdInsertDebugUtilsLabelEXT(cmdbuffer, &labelInfo); } else if (mContext->isDebugMarkersSupported()) { VkDebugMarkerMarkerInfoEXT markerInfo = { - .sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT, - .pMarkerName = string, - .color = {0.0f, 1.0f, 0.0f, 1.0f}, + .sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT, + .pMarkerName = string, + .color = {0.0f, 1.0f, 0.0f, 1.0f}, }; vkCmdDebugMarkerInsertEXT(cmdbuffer, &markerInfo); } diff --git a/filament/backend/src/vulkan/VulkanCommands.h b/filament/backend/src/vulkan/VulkanCommands.h index 8038ae2724..15605cfef8 100644 --- a/filament/backend/src/vulkan/VulkanCommands.h +++ b/filament/backend/src/vulkan/VulkanCommands.h @@ -58,9 +58,8 @@ private: // Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences. struct VulkanCmdFence { - VulkanCmdFence(VkDevice device, bool signaled = false); - ~VulkanCmdFence(); - const VkDevice device; + VulkanCmdFence(VkFence ifence); + ~VulkanCmdFence() = default; VkFence fence; utils::Condition condition; utils::Mutex mutex; @@ -192,6 +191,7 @@ class VulkanCommands { VkSemaphore mSubmissionSignal = {}; VkSemaphore mInjectedSignal = {}; utils::FixedCapacityVector> mStorage; + VkFence mFences[CAPACITY] = {}; VkSemaphore mSubmissionSignals[CAPACITY] = {}; uint8_t mAvailableBufferCount = CAPACITY; CommandBufferObserver* mObserver = nullptr; diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 85bf6c17ef..40cb457c8c 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -691,16 +691,15 @@ FenceStatus VulkanDriver::getFenceStatus(Handle fh) { // Internally we use the VK_INCOMPLETE status to mean "not yet submitted". // When this fence gets submitted, its status changes to VK_NOT_READY. std::unique_lock lock(cmdfence->mutex); - if (cmdfence->status.load() == VK_INCOMPLETE) { - // This will obviously timeout if Filament creates a fence and immediately waits on it - // without calling endFrame() or commit(). - cmdfence->condition.wait(lock); - } else { - lock.unlock(); + if (cmdfence->status.load() == VK_SUCCESS) { + return FenceStatus::CONDITION_SATISFIED; } - VkResult result = - vkWaitForFences(mPlatform->getDevice(), 1, &cmdfence->fence, VK_TRUE, 0); - return result == VK_SUCCESS ? FenceStatus::CONDITION_SATISFIED : FenceStatus::TIMEOUT_EXPIRED; + + // Two other states are possible: + // - VK_INCOMPLETE: the corresponding buffer has not yet been submitted. + // - VK_NOT_READY: the buffer has been submitted but not yet signaled. + // In either case, we return TIMEOUT_EXPIRED to indicate the fence has not been signaled. + return FenceStatus::TIMEOUT_EXPIRED; } // We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that From c80fbfdf1792cf98431aacf3cdd8537ed0ea0655 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 13 Sep 2023 22:57:26 -0700 Subject: [PATCH 15/19] Release Filament 1.42.2 --- NEW_RELEASE_NOTES.md | 3 --- README.md | 4 ++-- RELEASE_NOTES.md | 5 +++++ android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 59fa0685b1..4a1a9c7fa7 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,6 +7,3 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut - -- gltfio: Fix possible change of scale sign when decomposing transform matrix for animation -- engine: Fixes "stable" shadows (see b/299310624) diff --git a/README.md b/README.md index 27b5c0c723..9c17d278e0 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.42.1' + implementation 'com.google.android.filament:filament-android:1.42.2' } ``` @@ -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.42.1' +pod 'Filament', '~> 1.42.2' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 5506414321..b5998ea124 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,11 @@ 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.42.3 + +- gltfio: Fix possible change of scale sign when decomposing transform matrix for animation +- engine: Fixes "stable" shadows (see b/299310624) + ## v1.42.2 - Fix possible NPE when updating fog options from Java/Kotlin diff --git a/android/gradle.properties b/android/gradle.properties index 528d6fba23..b528f96684 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.42.1 +VERSION_NAME=1.42.2 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 8c09b6521b..e276b6739f 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.42.1" + spec.version = "1.42.2" 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.42.1/filament-v1.42.1-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.42.2/filament-v1.42.2-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 3bb5eaa82c..880dfff5e3 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.42.1", + "version": "1.42.2", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From f0a0a9b2e1478c335275ffdf0ec853f62a29df9b Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 13 Sep 2023 23:05:11 -0700 Subject: [PATCH 16/19] Bump version to 1.42.3 --- README.md | 4 ++-- android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9c17d278e0..e5d79f0e49 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.42.2' + implementation 'com.google.android.filament:filament-android:1.42.3' } ``` @@ -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.42.2' +pod 'Filament', '~> 1.42.3' ``` ### Snapshots diff --git a/android/gradle.properties b/android/gradle.properties index b528f96684..3ab23ceaed 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.42.2 +VERSION_NAME=1.42.3 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index e276b6739f..7220380f97 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.42.2" + spec.version = "1.42.3" 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.42.2/filament-v1.42.2-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.42.3/filament-v1.42.3-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 880dfff5e3..e941531abb 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.42.2", + "version": "1.42.3", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From fdffd93949aab2175a84b928602bbee0bfe09010 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 14 Sep 2023 17:09:33 -0700 Subject: [PATCH 17/19] vulkan: fix fence deadlock (#7173) Should only reset fences in VulkanCommands::gc --- filament/backend/src/vulkan/VulkanCommands.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanCommands.cpp b/filament/backend/src/vulkan/VulkanCommands.cpp index 8a2b2c61a7..45e55ac3f5 100644 --- a/filament/backend/src/vulkan/VulkanCommands.cpp +++ b/filament/backend/src/vulkan/VulkanCommands.cpp @@ -287,7 +287,8 @@ bool VulkanCommands::flush() { slog.i << "Submitting cmdbuffer=" << cmdbuffer << " wait=(" << signals[0] << ", " << signals[1] << ") " << " signal=" << renderingFinished - << io::endl; + << " fence=" << currentbuf->fence->fence + << utils::io::endl; #endif auto& cmdfence = currentbuf->fence; @@ -339,7 +340,7 @@ void VulkanCommands::wait() { } if (count > 0) { vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX); - vkResetFences(mDevice, count, fences); + updateFences(); } } From 7dd66860879f737a63dbcc1669298c945cb2f820 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Mon, 18 Sep 2023 11:06:46 -0700 Subject: [PATCH 18/19] Correct version to 1.43.0 --- README.md | 4 ++-- android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- libs/filabridge/include/filament/MaterialEnums.h | 2 +- web/filament-js/package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e5d79f0e49..1f06eefc7b 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.42.3' + implementation 'com.google.android.filament:filament-android:1.43.0' } ``` @@ -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.42.3' +pod 'Filament', '~> 1.43.0' ``` ### Snapshots diff --git a/android/gradle.properties b/android/gradle.properties index 3ab23ceaed..026f77ea0b 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.42.3 +VERSION_NAME=1.43.0 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 7220380f97..728c6bc5aa 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.42.3" + spec.version = "1.43.0" 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.42.3/filament-v1.42.3-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.43.0/filament-v1.43.0-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/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index f697212c11..c511c2937d 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -28,7 +28,7 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 42; +static constexpr size_t MATERIAL_VERSION = 43; /** * Supported shading models diff --git a/web/filament-js/package.json b/web/filament-js/package.json index e941531abb..6d5c6bb528 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.42.3", + "version": "1.43.0", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From ee31ca6fc0ea21b7b1bb4cba4de6c7a71f9830a2 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Mon, 18 Sep 2023 14:20:30 -0700 Subject: [PATCH 19/19] Fix RELEASE_NOTES version --- RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b5998ea124..16bc2a619f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,7 +7,7 @@ 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.42.3 +## v1.43.0 - gltfio: Fix possible change of scale sign when decomposing transform matrix for animation - engine: Fixes "stable" shadows (see b/299310624)