Compare commits

..

1 Commits

Author SHA1 Message Date
Powei Feng
48592d7d22 Bump version to 1.69.5 2026-02-24 19:37:59 -08:00
36 changed files with 236 additions and 266 deletions

View File

@@ -6,6 +6,3 @@
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- engine: fix crash when using variance shadow maps
- materials: better shadow normal-bias calculations [⚠️ **New Material Version**]

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.69.4'
implementation 'com.google.android.filament:filament-android:1.69.5'
}
```
@@ -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.69.4'
pod 'Filament', '~> 1.69.5'
```
## Documentation

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.69.4
VERSION_NAME=1.69.5
POM_DESCRIPTION=Real-time physically based rendering engine for Android.

View File

@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.69.4'
implementation 'com.google.android.filament:filament-android:1.69.3'
}
</code></pre>
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
@@ -195,7 +195,7 @@ dependencies {
</div>
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
<p>iOS projects can use CocoaPods to install the latest release:</p>
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.69.4'
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.69.3'
</code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul>

View File

@@ -228,7 +228,6 @@ branch called <code>my-pr-branch</code>, to <code>filament</code>. This PR requi
it in the following fashion</p>
<h3 id="using-a-script-to-update-the-golden-repo"><a class="header" href="#using-a-script-to-update-the-golden-repo">Using a script to update the golden repo</a></h3>
<ul>
<li>Make sure you've completed the steps in 'Setting up python'</li>
<li>Run interactive mode in the <code>update_golden.py</code> script.
<pre><code>python3 test/renderdiff/src/update_golden.py
</code></pre>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -91,7 +91,7 @@ public:
GLenum getIndicesType() const noexcept {
return indicesType;
}
};
} gl;
static bool queryOpenGLVersion(GLint* major, GLint* minor) noexcept;

View File

@@ -271,8 +271,8 @@ std::unique_ptr<MaterialDefinition> MaterialDefinition::create(FEngine& engine,
void MaterialDefinition::terminate(FEngine& engine) {
DriverApi& driver = engine.getDriverApi();
perViewDescriptorSetLayoutPcf.terminate(engine.getDescriptorSetLayoutFactory(), driver);
perViewDescriptorSetLayoutS2d.terminate(engine.getDescriptorSetLayoutFactory(), driver);
perViewDescriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
perViewDescriptorSetLayoutVsm.terminate(engine.getDescriptorSetLayoutFactory(), driver);
descriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
}
@@ -607,23 +607,23 @@ void MaterialDefinition::processDescriptorSets(FEngine& engine) {
refractionMode == RefractionMode::SCREEN_SPACE;
bool const hasFog = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG));
this->perViewDescriptorSetLayoutPcfDescription = descriptor_sets::getPerViewDescriptorSetLayout(
this->perViewDescriptorSetLayoutDescription = descriptor_sets::getPerViewDescriptorSetLayout(
materialDomain, isLit, isSSR, hasFog, false);
this->perViewDescriptorSetLayoutS2dDescription = descriptor_sets::getPerViewDescriptorSetLayout(
this->perViewDescriptorSetLayoutVsmDescription = descriptor_sets::getPerViewDescriptorSetLayout(
materialDomain, isLit, isSSR, hasFog, true);
// set the labels
this->descriptorSetLayoutDescription.label = CString{ name }.append("_perMat");
this->perViewDescriptorSetLayoutPcfDescription.label = CString{ name }.append("_perView");
this->perViewDescriptorSetLayoutS2dDescription.label = CString{ name }.append("_perViewVsm");
this->perViewDescriptorSetLayoutDescription.label = CString{ name }.append("_perView");
this->perViewDescriptorSetLayoutVsmDescription.label = CString{ name }.append("_perViewVsm");
// get the PER_RENDERABLE and PER_VIEW descriptor binding info
for (auto&& [bindingPoint, dsl] : {
std::pair{ DescriptorSetBindingPoints::PER_RENDERABLE,
descriptor_sets::getPerRenderableLayout() },
std::pair{ DescriptorSetBindingPoints::PER_VIEW,
this->perViewDescriptorSetLayoutPcfDescription }}) {
this->perViewDescriptorSetLayoutDescription }}) {
Program::DescriptorBindingsInfo& descriptors = programDescriptorBindings[+bindingPoint];
descriptors.reserve(dsl.descriptors.size());
for (auto const& entry: dsl.descriptors) {
@@ -636,17 +636,17 @@ void MaterialDefinition::processDescriptorSets(FEngine& engine) {
descriptorSetLayoutFactory, driver,
this->descriptorSetLayoutDescription };
this->perViewDescriptorSetLayoutPcf = {
this->perViewDescriptorSetLayout = {
descriptorSetLayoutFactory, driver,
this->perViewDescriptorSetLayoutPcfDescription };
this->perViewDescriptorSetLayoutDescription };
this->perViewDescriptorSetLayoutS2d = {
this->perViewDescriptorSetLayoutVsm = {
descriptorSetLayoutFactory, driver,
this->perViewDescriptorSetLayoutS2dDescription };
this->perViewDescriptorSetLayoutVsmDescription };
}
backend::DescriptorSetLayout const& MaterialDefinition::getPerViewDescriptorSetLayoutDescription(
Variant const variant, bool const useS2dDescriptorSetLayout) const noexcept {
Variant const variant, bool const useVsmDescriptorSetLayout) const noexcept {
if (materialDomain == MaterialDomain::SURFACE) {
if (Variant::isValidDepthVariant(variant)) {
// Use the layout description used to create the per view depth variant layout.
@@ -657,10 +657,10 @@ backend::DescriptorSetLayout const& MaterialDefinition::getPerViewDescriptorSetL
return descriptor_sets::getSsrVariantLayout();
}
}
if (useS2dDescriptorSetLayout) {
return perViewDescriptorSetLayoutS2dDescription;
if (useVsmDescriptorSetLayout) {
return perViewDescriptorSetLayoutVsmDescription;
}
return perViewDescriptorSetLayoutPcfDescription;
return perViewDescriptorSetLayoutDescription;
}
Handle<HwProgram> MaterialDefinition::compileProgram(
@@ -689,7 +689,7 @@ Handle<HwProgram> MaterialDefinition::compileProgram(
pb.descriptorLayout(+DescriptorSetBindingPoints::PER_VIEW,
getPerViewDescriptorSetLayoutDescription(
specialization.variant,
Variant::isShadowSampler2DVariant(specialization.variant)));
Variant::isVSMVariant(specialization.variant)));
pb.descriptorLayout(+DescriptorSetBindingPoints::PER_RENDERABLE,
descriptor_sets::getPerRenderableLayout());
pb.descriptorLayout(

View File

@@ -94,17 +94,17 @@ struct MaterialDefinition {
backend::ShaderModel const sm, bool isStereoSupported) const noexcept;
backend::DescriptorSetLayout const& getPerViewDescriptorSetLayoutDescription(
Variant const variant, bool useS2dDescriptorSetLayout) const noexcept;
Variant const variant, bool useVsmDescriptorSetLayout) const noexcept;
// Keep track of the definitions of the descriptor set layouts, as these
// may be used by some backends in parallel compilation of programs.
backend::DescriptorSetLayout perViewDescriptorSetLayoutPcfDescription;
backend::DescriptorSetLayout perViewDescriptorSetLayoutS2dDescription;
backend::DescriptorSetLayout perViewDescriptorSetLayoutDescription;
backend::DescriptorSetLayout perViewDescriptorSetLayoutVsmDescription;
backend::DescriptorSetLayout descriptorSetLayoutDescription;
// try to order by frequency of use
filament::DescriptorSetLayout perViewDescriptorSetLayoutPcf;
filament::DescriptorSetLayout perViewDescriptorSetLayoutS2d;
filament::DescriptorSetLayout perViewDescriptorSetLayout;
filament::DescriptorSetLayout perViewDescriptorSetLayoutVsm;
filament::DescriptorSetLayout descriptorSetLayout;
backend::Program::DescriptorSetInfo programDescriptorBindings;

View File

@@ -802,7 +802,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
// use our special SSR variant, it can only be applied to object that have
// the SCREEN_SPACE ReflectionMode.
passBuilder.variant(Variant{ Variant::SPECIAL_SSR_VARIANT });
passBuilder.variant(Variant{ Variant::SPECIAL_SSR });
// generate all our drawing commands, except blended objects.
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);

View File

@@ -568,7 +568,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
if constexpr (isDepthPass) {
cmd.info.materialVariant = variant;
cmd.info.rasterState = {};
cmd.info.rasterState.colorWrite = Variant::isPickingVariant(variant) || Variant::isDepthMomentsVariant(variant);
cmd.info.rasterState.colorWrite = Variant::isPickingVariant(variant) || Variant::isVSMVariant(variant);
cmd.info.rasterState.depthWrite = true;
cmd.info.rasterState.depthFunc = RasterState::DepthFunc::GE;
cmd.info.rasterState.alphaToCoverage = false;
@@ -616,7 +616,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
bool const hasSkinningOrMorphing = hasSkinning || hasMorphing;
// if we are already an SSR variant, the SRE bit is already set
static_assert(Variant::SPECIAL_SSR_VARIANT & Variant::SRE);
static_assert(Variant::SPECIAL_SSR & Variant::SRE);
Variant renderableVariant{ variant };
// we can't have SSR and shadowing together by construction

View File

@@ -213,14 +213,12 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
* Final shadow map transform
*/
// Final shadow transform (focused warped light-space), world space to clip space
// Final shadow transform (focused warped light-space)
const mat4f S = F * (W * LMpMv);
// 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 auto [Mt, Mn] = getTextureCoordsMapping(shadowMapInfo, getViewport());
// world space to texture atlas space
const mat4f St = highPrecisionMultiply(Mt, S);
ShaderParameters shaderParameters;
@@ -228,12 +226,14 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
// note: in texelSizeWorldSpace() below, we can use Mb * Mt * F * W because
// L * Mp * Mv is a rigid transform for directional lights, and doesn't matter.
// if Wp[3][1] is 0, then LiSPSM was canceled.
// if Wp[3][1] is 0, then LISPSM was cancelled.
if (useLispsm && Wp[3][1] != 0.0f) {
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(S, shadowMapInfo.shadowDimension);
shaderParameters.texelSizeAtOneMeterWs =
texelSizeWorldSpace(Wp, mat4f(Mt * F), shadowMapInfo.shadowDimension);
} else {
// We know we're using an ortho projection
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(S.upperLeft(), shadowMapInfo.shadowDimension);
shaderParameters.texelSizeAtOneMeterWs =
texelSizeWorldSpace(St.upperLeft(), shadowMapInfo.shadowDimension);
}
if (!shadowMapInfo.vsm) {
shaderParameters.lightSpace = St;
@@ -1117,67 +1117,84 @@ bool ShadowMap::intersectSegmentWithPlanarQuad(float3& UTILS_RESTRICT p,
return hit;
}
float2 ShadowMap::texelSizeWorldSpace(const mat3f& clipFromWorld, uint16_t shadowDimension) noexcept {
float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture,
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 shadowMapFromWorld,
// orthographic projections. We just need to inverse worldToShadowTexture,
// which is guaranteed to be orthographic.
// The two first columns give us how a texel maps in world-space.
float const oneTexel = 2.0f / float(shadowDimension);
mat3f const worldFromClip(inverse(clipFromWorld));
float3 const Jx = worldFromClip[0];
float3 const Jy = worldFromClip[1];
float2 const s = float2{ length(Jx), length(Jy) } * oneTexel;
const float ures = 1.0f / float(shadowDimension);
const float vres = 1.0f / float(shadowDimension);
const mat3f shadowTextureToWorld(inverse(worldToShadowTexture));
const float3 Jx = shadowTextureToWorld[0];
const float3 Jy = shadowTextureToWorld[1];
const float s = std::max(length(Jx) * ures, length(Jy) * vres);
return s;
}
/**
* Calculates the Jacobian matrix J = ∂(u,v,d)/∂(x,y,z) for a 4x4 perspective projection.
*
* Given a view-space point P = [x, y, z, 1]^T and a projection matrix M,
* the projected homogeneous coordinates are [X, Y, Z, W]^T = M * P.
* The resulting NDC coordinates are u = X/W, v = Y/W, and depth d = Z/W.
*
* To find the Jacobian on the CPU, we apply the quotient rule to each component:
* ∂(X/W) / ∂xi = ( (∂X/∂xi) * W - X * (∂W/∂xi) ) / W^2
*
* In matrix form, this can be expressed as:
* J = (1/W) * [ M_sub - (1/W) * (T ⊗ w_grad) ]
*
* Where:
* - W: The homogeneous w-component after projection (usually -z for standard mats).
* - M_sub: The top-left 3x3 submatrix of M.
* - T: The column vector [X, Y, Z]^T (pre-perspective divide).
* - w_grad: The row vector [m30, m31, m32] (the first three elements of M's last row).
* - ⊗: The outer product, resulting in a 3x3 matrix.
*
* This Jacobian describes the local "stretch" of the projection. For LiSPSM or
* shadow mapping, the inverse Jacobian J^-1 provides the world-space footprint
* of a shadow texel, which is essential for calculating an accurate,
* non-constant depth bias to eliminate shadow acne.
*
* @param M The 4x4 projection matrix.
* @param p The 3D point in view-space where the Jacobian is evaluated.
* @return A 3x3 matrix representing the partial derivatives of (u,v,d) w.r.t (x,y,z).
*/
static mat3f jacobian(mat4f const& M, float3 const& p) noexcept {
float4 const T = M * p;
mat3f const M_sub = M.upperLeft();
float3 const w_grad = { M[0].w, M[1].w, M[2].w };
mat3f const t_cross_w{
w_grad.x * T.xyz,
w_grad.y * T.xyz,
w_grad.z * T.xyz
};
return (M_sub - t_cross_w / T.w) / T.w;
}
float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF,
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.
// So we really only need to calculate the Jacobian of inverse(Wp) at inverse(MbMtF).
//
// Because we're only interested in the length of the columns of the Jacobian, we can use
// Mb * Mt * F * Wp instead of the full expression Mb * Mt * F * Wp * Wv * L * Mp * Mv,
// because Wv * L * Mp * Mv is a rigid transform, which doesn't affect the length of
// the Jacobian's column vectors.
float2 ShadowMap::texelSizeWorldSpace(mat4f const& S, uint16_t const shadowDimension) noexcept {
// The Jacobian is not constant, so we evaluate it in the center of the shadow-map texture.
// It might be better to do this computation in the vertex shader.
float3 const p = { 0.0f, 0.0f, 0.0f }; // clip-space
float const oneTexel = 2.0f / float(shadowDimension);
mat3f const J = jacobian(inverse(S), p);
float2 const s = float2{ length(J[0]), length(J[1]) } * oneTexel;
float3 const p = { 0.5f, 0.5f, 0.0f };
const float ures = 1.0f / float(shadowDimension);
const float vres = 1.0f / float(shadowDimension);
const float dres = 1.0f / 65536.0f;
constexpr bool JACOBIAN_ESTIMATE = false;
if constexpr (JACOBIAN_ESTIMATE) {
// 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);
const float3 p1 = mat4f::project(Si, p + float3{ 1, 0, 0 } * ures);
const float3 p2 = mat4f::project(Si, p + float3{ 0, 1, 0 } * vres);
const float3 p3 = mat4f::project(Si, p + float3{ 0, 0, 1 } * dres);
const float3 Jx = p1 - p0;
const float3 Jy = p2 - p0;
const float3 UTILS_UNUSED Jz = p3 - p0;
const float s = std::max(length(Jx), length(Jy));
return s;
}
const float n = Wp[0][0];
const float A = Wp[1][1];
const float B = Wp[3][1];
const float sx = MbMtF[0][0];
const float sy = MbMtF[1][1];
const float sz = MbMtF[2][2];
const float ox = MbMtF[3][0];
const float oy = MbMtF[3][1];
const float oz = MbMtF[3][2];
const float X = p.x - ox;
const float Y = p.y - oy;
const float Z = p.z - oz;
const float dz = A * sy - Y;
const float nsxsz = n * sx * sz;
const float j = -(B * sy) / (nsxsz * dz * dz);
const mat3f J(mat3f::row_major_init{
j * dz * sz, j * X * sz, 0.0f,
0.0f, j * nsxsz, 0.0f,
0.0f, j * Z * sx, j * dz * sx
});
float3 const Jx = J[0] * ures;
float3 const Jy = J[1] * vres;
UTILS_UNUSED float3 const Jz = J[2] * dres;
const float s = std::max(length(Jx), length(Jy));
return s;
}

View File

@@ -135,7 +135,7 @@ public:
math::mat4f lightSpace{};
math::float4 lightFromWorldZ{};
math::float4 scissorNormalized{};
math::float2 texelSizeAtOneMeterWs{};
float texelSizeAtOneMeterWs{};
};
// Call once per frame if the light, scene (or visible layers) or camera changes.
@@ -319,8 +319,11 @@ private:
math::float4 getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) const noexcept;
static math::float2 texelSizeWorldSpace(const math::mat3f& clipFromWorld, uint16_t shadowDimension) noexcept;
static math::float2 texelSizeWorldSpace(const math::mat4f& clipFromWorld, uint16_t shadowDimension) noexcept;
static float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture,
uint16_t shadowDimension) noexcept;
static float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF,
uint16_t shadowDimension) noexcept;
static constexpr Segment sBoxSegments[12] = {
{ 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 },

View File

@@ -742,16 +742,17 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
// Texel size is constant for directional light (although that's not true when LISPSM
// is used, but in that case we're pretending it is).
float2 const wsTexelSize = shaderParameters.texelSizeAtOneMeterWs;
const float wsTexelSize = shaderParameters.texelSizeAtOneMeterWs;
auto& s = mShadowUb.edit();
s.shadows[shadowIndex].layer = shadowMap.getLayer();
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
s.shadows[shadowIndex].normalBias = wsTexelSize * normalBias;
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSize;
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSize;
s.shadows[shadowIndex].elvsm = options.vsm.elvsm;
s.shadows[shadowIndex].bulbRadiusLs =
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / length(wsTexelSize);
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / wsTexelSize;
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
cascadeHasVisibleShadows |= 0x1u << i;
@@ -832,7 +833,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap, FEngine& engin
// and if we need to generate it, update all the UBO data
if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex();
const float2 wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
// note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
@@ -844,11 +845,12 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap, FEngine& engin
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
s.shadows[shadowIndex].bulbRadiusLs =
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
/ length(wsTexelSizeAtOneMeter);
/ wsTexelSizeAtOneMeter;
}
}
@@ -924,7 +926,7 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
// and if we need to generate it, update all the UBO data
if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex();
const float2 wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
// note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
@@ -936,11 +938,12 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
s.shadows[shadowIndex].bulbRadiusLs =
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
/ length(wsTexelSizeAtOneMeter);
/ wsTexelSizeAtOneMeter;
}
}

View File

@@ -257,9 +257,9 @@ filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout(
}
// mDefinition.perViewDescriptorSetLayout{Vsm} is already resolved for MaterialDomain
if (useVsmDescriptorSetLayout) {
return mDefinition.perViewDescriptorSetLayoutS2d;
return mDefinition.perViewDescriptorSetLayoutVsm;
}
return mDefinition.perViewDescriptorSetLayoutPcf;
return mDefinition.perViewDescriptorSetLayout;
}
void FMaterial::compile(CompilerPriorityQueue const priority,

View File

@@ -139,7 +139,7 @@ public:
// This is mostly intended to be used for post-process materials; but it's also useful for
// Surface material that behave like post-process material (i.e. that don't really have variants or for
// which VSM is nonsensical, like for unlit materials)
return mDefinition.perViewDescriptorSetLayoutPcf;
return mDefinition.perViewDescriptorSetLayout;
}
DescriptorSetLayout const& getPerViewDescriptorSetLayout(
@@ -378,11 +378,11 @@ private:
Variant variant = {}) const noexcept;
bool isSharedVariant(Variant const variant) const {
// HACK: The default material "should" have MNT | DEP, but then we'd have to compile it as a
// HACK: The default material "should" have VSM | DEP, but then we'd have to compile it as a
// lit material, which would increase binary size. Perhaps we could specially compile it
// with this variant, but with the shader program cache in active development, the days of
// the default material are numbered anyway.
constexpr Variant::type_t vsmAndDep = Variant::MNT | Variant::DEP;
constexpr Variant::type_t vsmAndDep = Variant::VSM | Variant::DEP;
return mDefinition.materialDomain == MaterialDomain::SURFACE && !mIsDefaultMaterial &&
!mDefinition.hasCustomDepthShader && Variant::isValidDepthVariant(variant) &&
(variant.key & vsmAndDep) != vsmAndDep;

View File

@@ -969,7 +969,10 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
variant.setDirectionalLighting(view.hasDirectionalLighting());
variant.setDynamicLighting(view.hasDynamicLighting());
variant.setFog(view.hasFog());
variant.setShadowSampler2D(view.hasShadowing() && view.getShadowType() != ShadowType::PCF);
// The VSM bit has a different meaning for STANDARD_VARIANT (as opposed to DEPTH_VARIANT),
// In the STANDARD_VARIANT case, we are *using* the shadow-map, and the VSM only decides which
// type of sampler is used (samplerShadow or sampler2D).
variant.setVsm(view.hasShadowing() && view.getShadowType() != ShadowType::PCF);
variant.setStereo(view.hasStereo());
/*
@@ -978,7 +981,10 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
if (view.needsShadowMap()) {
Variant shadowVariant(Variant::DEPTH_VARIANT);
shadowVariant.setDepthMoments(view.getShadowType() == ShadowType::VSM);
// The VSM bit has a different meaning for DEPTH_VARIANT (as opposed to STANDARD_VARIANT),
// In the DEPTH_VARIANT case, we are *generating* the shadow-map, and some computations
// are handled differently. In addition, the color buffer is used.
shadowVariant.setVsm(view.getShadowType() == ShadowType::VSM);
auto shadows = view.renderShadowMaps(engine, fg, cameraInfo, mShaderUserTime,
RenderPassBuilder{ commandArena }

View File

@@ -143,7 +143,7 @@ FView::FView(FEngine& engine)
#ifndef NDEBUG
// This can fail if another view has already registered this data source
mDebugState->owner = debugRegistry.registerDataSource("d.view.frame_info",
[weak = std::weak_ptr<DebugState>(mDebugState)]() -> DebugRegistry::DataSource {
[weak = std::weak_ptr(mDebugState)]() -> DebugRegistry::DataSource {
// the View could have been destroyed by the time we do this
auto const state = weak.lock();
if (!state) {

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.69.4"
spec.version = "1.69.5"
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.69.4/filament-v1.69.4-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.69.5/filament-v1.69.5-ios.tgz" }
spec.libraries = 'c++'

View File

@@ -36,7 +36,7 @@ backend::DescriptorSetLayout const& getPerRenderableLayout() noexcept;
backend::DescriptorSetLayout getPerViewDescriptorSetLayout(
MaterialDomain domain,
bool isLit, bool isSSR, bool hasFog,
bool isShadowSampler2D) noexcept;
bool isVSM) noexcept;
backend::DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
Variant variant,

View File

@@ -299,9 +299,10 @@ struct ShadowUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
math::mat4f lightFromWorldMatrix; // 64
math::float4 lightFromWorldZ; // 16
math::float4 scissorNormalized; // 16
float texelSizeAtOneMeter; // 4
float bulbRadiusLs; // 4
float nearOverFarMinusNear; // 4
math::float2 normalBias; // 4
float normalBias; // 4
bool elvsm; // 4
uint32_t layer; // 4
uint32_t reserved1; // 4

View File

@@ -48,21 +48,20 @@ struct Variant {
// SRE: Shadow Receiver
// SKN: Skinning
// DEP: Depth only
// FOG: Fog (standard)
// PCK: Picking (depth)
// MNT: Output depth moments (depth)
// S2D: Sampler type for shadows (0: samplerShadowArray, 1: sampler2DArray) (standard)
// FOG: Fog
// PCK: Picking (depth variant only)
// VSM: Variance shadow maps (depth) / sampler type (standard)
// STE: Instanced stereo rendering
//
// X: either 1 or 0
// +-----+-----+-----+-----+-----+-----+-----+-----+
// Variant | STE | S2D | FOG | DEP | SKN | SRE | DYN | DIR | 256
// Variant | STE | VSM | FOG | DEP | SKN | SRE | DYN | DIR | 256
// +-----+-----+-----+-----+-----+-----+-----+-----+
// MNT PCK
// PCK
//
// Standard variants:
// +-----+-----+-----+-----+-----+-----+-----+-----+
// | STE | S2D | FOG | 0 | SKN | SRE | DYN | DIR | 128 - 44 = 84
// | STE | VSM | FOG | 0 | SKN | SRE | DYN | DIR | 128 - 44 = 84
// +-----+-----+-----+-----+-----+-----+-----+-----+
// Vertex shader X 0 0 0 X X X X
// Fragment shader 0 X X 0 0 X X X
@@ -73,7 +72,7 @@ struct Variant {
//
// Depth variants:
// +-----+-----+-----+-----+-----+-----+-----+-----+
// | STE | MNT | PCK | 1 | SKN | 0 | 0 | 0 | 16 - 4 = 12
// | STE | VSM | PCK | 1 | SKN | 0 | 0 | 0 | 16 - 4 = 12
// +-----+-----+-----+-----+-----+-----+-----+-----+
// Vertex depth X X 0 1 X 0 0 0
// Fragment depth 0 0 X 1 0 0 0 0
@@ -97,15 +96,13 @@ struct Variant {
static constexpr type_t DEP = 0x10; // depth only variants
static constexpr type_t FOG = 0x20; // fog (standard)
static constexpr type_t PCK = 0x20; // picking (depth)
static constexpr type_t S2D = 0x40; // sampler type
static constexpr type_t MNT = 0x40; // variance shadow maps
static constexpr type_t VSM = 0x40; // variance shadow maps / sampler type
static constexpr type_t STE = 0x80; // instanced stereo
static constexpr type_t NO_VARIANT = 0u;
// special variants (variants that use the reserved space)
static constexpr type_t SPECIAL_SSR_VARIANT= S2D | SRE ;
static constexpr type_t SPECIAL_SSR_MASK = STE | S2D | DEP | SRE | DYN | DIR;
static constexpr type_t SPECIAL_SSR = VSM | SRE; // screen-space reflections variant
static constexpr type_t STANDARD_MASK = DEP;
static constexpr type_t STANDARD_VARIANT = 0u;
@@ -130,30 +127,29 @@ struct Variant {
void setSkinning(bool v) noexcept { set(v, SKN); }
void setFog(bool v) noexcept { set(v, FOG); }
void setPicking(bool v) noexcept { set(v, PCK); }
void setShadowSampler2D(bool v) noexcept { set(v, S2D); }
void setDepthMoments(bool v) noexcept { set(v, MNT); }
void setVsm(bool v) noexcept { set(v, VSM); }
void setStereo(bool v) noexcept { set(v, STE); }
static constexpr bool isValidDepthVariant(Variant variant) noexcept {
// Can't have VSM and PICKING together with DEPTH variants
constexpr type_t RESERVED_MASK = MNT | PCK | DEP | SRE | DYN | DIR;
constexpr type_t RESERVED_VALUE = MNT | PCK | DEP;
constexpr type_t RESERVED_MASK = VSM | PCK | DEP | SRE | DYN | DIR;
constexpr type_t RESERVED_VALUE = VSM | PCK | DEP;
return ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) &&
((variant.key & RESERVED_MASK) != RESERVED_VALUE);
}
static constexpr bool isValidStandardVariant(Variant variant) noexcept {
// can't have shadow receiver if we don't have any lighting
constexpr type_t RESERVED0_MASK = S2D | FOG | SRE | DYN | DIR;
constexpr type_t RESERVED0_VALUE = S2D | FOG | SRE;
constexpr type_t RESERVED0_MASK = VSM | FOG | SRE | DYN | DIR;
constexpr type_t RESERVED0_VALUE = VSM | FOG | SRE;
// can't have shadow receiver if we don't have any lighting
constexpr type_t RESERVED1_MASK = S2D | SRE | DYN | DIR;
constexpr type_t RESERVED1_MASK = VSM | SRE | DYN | DIR;
constexpr type_t RESERVED1_VALUE = SRE;
// can't have VSM without shadow receiver
constexpr type_t RESERVED2_MASK = S2D | SRE;
constexpr type_t RESERVED2_VALUE = S2D;
constexpr type_t RESERVED2_MASK = VSM | SRE;
constexpr type_t RESERVED2_VALUE = VSM;
return ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) &&
((variant.key & RESERVED0_MASK) != RESERVED0_VALUE) &&
@@ -178,15 +174,11 @@ struct Variant {
}
static constexpr bool isSSRVariant(Variant variant) noexcept {
return (variant.key & SPECIAL_SSR_MASK) == SPECIAL_SSR_VARIANT;
return (variant.key & (STE | VSM | DEP | SRE | DYN | DIR)) == (VSM | SRE);
}
static constexpr bool isShadowSampler2DVariant(Variant variant) noexcept {
return !isSSRVariant(variant) && ((variant.key & (S2D | DEP)) == S2D);
}
static constexpr bool isDepthMomentsVariant(Variant variant) noexcept {
return !isSSRVariant(variant) && ((variant.key & (MNT | DEP)) == (MNT | DEP));
static constexpr bool isVSMVariant(Variant variant) noexcept {
return !isSSRVariant(variant) && ((variant.key & VSM) == VSM);
}
static constexpr bool isShadowReceiverVariant(Variant variant) noexcept {
@@ -210,13 +202,13 @@ struct Variant {
// vertex shader.
if ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) {
if (isSSRVariant(variant)) {
variant.key &= ~SPECIAL_SSR_VARIANT;
variant.key &= ~(VSM | SRE);
}
return variant & (STE | SKN | SRE | DYN | DIR);
}
if ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) {
// Only MNT, skinning, and stereo affect the vertex shader's DEPTH variant
return variant & (STE | MNT | SKN | DEP);
// Only VSM, skinning, and stereo affect the vertex shader's DEPTH variant
return variant & (STE | VSM | SKN | DEP);
}
return {};
}
@@ -225,11 +217,11 @@ struct Variant {
// filter out fragment variants that are not needed. For e.g. skinning doesn't
// affect the fragment shader.
if ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) {
return variant & (S2D | FOG | SRE | DYN | DIR);
return variant & (VSM | FOG | SRE | DYN | DIR);
}
if ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) {
// Only VSM & PICKING affects the fragment shader's DEPTH variant
return variant & (MNT | PCK | DEP);
return variant & (VSM | PCK | DEP);
}
return {};
}
@@ -238,8 +230,8 @@ struct Variant {
// special case for depth variant
if (isValidDepthVariant(variant)) {
if (!isLit) {
// if we're unlit, we never need the MNT variant
return variant & ~MNT;
// if we're unlit, we never need the VSM variant
return variant & ~VSM;
}
return variant;
}
@@ -250,9 +242,9 @@ struct Variant {
// when the shading mode is unlit, remove all the lighting variants
return variant & UNLIT_MASK;
}
// if shadow receiver is disabled, we pick the shadow sampler
// if shadow receiver is disabled, turn off VSM
if (!(variant.key & SRE)) {
return variant & ~S2D;
return variant & ~VSM;
}
return variant;
}

View File

@@ -216,7 +216,7 @@ utils::CString getDescriptorName(DescriptorSetBindingPoints const set,
DescriptorSetLayout getPerViewDescriptorSetLayout(
MaterialDomain const domain,
bool const isLit, bool const isSSR, bool const hasFog,
bool const isShadowSampler2D) noexcept {
bool const isVSM) noexcept {
switch (domain) {
case MaterialDomain::SURFACE: {
@@ -255,7 +255,7 @@ DescriptorSetLayout getPerViewDescriptorSetLayout(
}
// change the SHADOW_MAP descriptor type for VSM
if (isShadowSampler2D) {
if (isVSM) {
auto const pos = std::find_if(layout.descriptors.begin(), layout.descriptors.end(),
[](auto const& v) {
return v.binding == PerViewBindingPoints::SHADOW_MAP;
@@ -285,7 +285,8 @@ DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
return ssrVariantDescriptorSetLayout;
}
// We need to filter out all the descriptors not included in the "resolved" layout below
return getPerViewDescriptorSetLayout(domain, isLit, isSSR, hasFog, Variant::isShadowSampler2DVariant(variant));
return getPerViewDescriptorSetLayout(domain, isLit, isSSR, hasFog,
Variant::isVSMVariant(variant));
}
DescriptorType getDescriptorType(SamplerType const type, SamplerFormat const format) {

View File

@@ -32,46 +32,41 @@ namespace filament {
Variant Variant::filterUserVariant(
Variant variant, UserVariantFilterMask filterMask) noexcept {
// these are easy to filter by just removing the corresponding bit
if (filterMask & uint32_t(UserVariantFilterBit::DIRECTIONAL_LIGHTING)) {
if (filterMask & (uint32_t)UserVariantFilterBit::DIRECTIONAL_LIGHTING) {
variant.key &= ~DIR;
}
if (filterMask & uint32_t(UserVariantFilterBit::DYNAMIC_LIGHTING)) {
if (filterMask & (uint32_t)UserVariantFilterBit::DYNAMIC_LIGHTING) {
variant.key &= ~DYN;
}
if (filterMask & uint32_t(UserVariantFilterBit::SKINNING)) {
if (filterMask & (uint32_t)UserVariantFilterBit::SKINNING) {
variant.key &= ~SKN;
}
if (filterMask & uint32_t(UserVariantFilterBit::STE)) {
if (filterMask & (uint32_t)UserVariantFilterBit::STE) {
variant.key &= ~(filterMask & STE);
}
if (isValidDepthVariant(variant)) {
// depth variants can have their MNT bit filtered
if (filterMask & uint32_t(UserVariantFilterBit::VSM)) {
variant.key &= ~MNT;
}
} else {
if (!isValidDepthVariant(variant)) {
// we can't remove FOG from depth variants, this would, in fact, remove picking
if (filterMask & uint32_t(UserVariantFilterBit::FOG)) {
if (filterMask & (uint32_t)UserVariantFilterBit::FOG) {
variant.key &= ~FOG;
}
} else {
// depth variants can have their VSM bit filtered
if (filterMask & (uint32_t)UserVariantFilterBit::VSM) {
variant.key &= ~VSM;
}
}
if (!isSSRVariant(variant)) {
// SSR variant needs to be handled separately
if (filterMask & uint32_t(UserVariantFilterBit::SHADOW_RECEIVER)) {
if (filterMask & (uint32_t)UserVariantFilterBit::SHADOW_RECEIVER) {
variant.key &= ~SRE;
}
if (filterMask & uint32_t(UserVariantFilterBit::VSM)) {
variant.key &= ~S2D;
if (filterMask & (uint32_t)UserVariantFilterBit::VSM) {
variant.key &= ~VSM;
}
} else {
// see if we need to filter out the SSR variants
if (filterMask & uint32_t(UserVariantFilterBit::SSR)) {
variant.key &= ~SPECIAL_SSR_VARIANT;
if (filterMask & (uint32_t)UserVariantFilterBit::SSR) {
variant.key &= ~SPECIAL_SSR;
}
}
return variant;

View File

@@ -60,8 +60,7 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(io::sstream& out,
CodeGenerator::generateDefine(out, "VARIANT_HAS_SHADOWING",
litVariants && filament::Variant::isShadowReceiverVariant(variant));
CodeGenerator::generateDefine(out, "VARIANT_HAS_VSM",
filament::Variant::isShadowSampler2DVariant(variant) ||
filament::Variant::isDepthMomentsVariant(variant));
filament::Variant::isVSMVariant(variant));
CodeGenerator::generateDefine(out, "VARIANT_HAS_STEREO",
hasStereo(variant, featureLevel));
CodeGenerator::generateDefine(out, "VARIANT_DEPTH",

View File

@@ -49,7 +49,7 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexce
// reason we name them "unused*" to ensure we're not using them by mistake (type/format don't
// matter).
static SamplerInterfaceBlock const sibShadowSamplerPcf{ SamplerInterfaceBlock::Builder()
static SamplerInterfaceBlock const sibPcf{ SamplerInterfaceBlock::Builder()
.name("sampler0")
.stageFlags(backend::ShaderStageFlags::FRAGMENT)
.add( {{ "shadowMap", +PerViewBindingPoints::SHADOW_MAP, Type::SAMPLER_2D_ARRAY, Format::SHADOW, Precision::MEDIUM, FILTERABLE, !MULTISAMPLE, ALL_STAGES },
@@ -62,7 +62,7 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexce
)
.build() };
static SamplerInterfaceBlock const sibShadowSampler2D{ SamplerInterfaceBlock::Builder()
static SamplerInterfaceBlock const sibVsm{ SamplerInterfaceBlock::Builder()
.name("sampler0")
.stageFlags(backend::ShaderStageFlags::FRAGMENT)
.add( {{ "shadowMap", +PerViewBindingPoints::SHADOW_MAP, Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::HIGH, FILTERABLE, !MULTISAMPLE, ALL_STAGES },
@@ -85,10 +85,10 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexce
if (Variant::isSSRVariant(variant)) {
return sibSsr;
} else if (Variant::isShadowSampler2DVariant(variant)) {
return sibShadowSampler2D;
} else if (Variant::isVSMVariant(variant)) {
return sibVsm;
} else {
return sibShadowSamplerPcf;
return sibPcf;
}
}

View File

@@ -48,11 +48,10 @@ std::string formatVariantString(Variant variant, MaterialDomain domain) noexcept
if (variant.key & Variant::DEP) variantString += "DEP|";
if (variant.key & Variant::DEP) {
if (variant.key & Variant::PCK) variantString += "PCK|";
if (variant.key & Variant::MNT) variantString += "MNT|";
} else {
if (variant.key & Variant::FOG) variantString += "FOG|";
if (variant.key & Variant::S2D) variantString += "S2D|";
}
if (variant.key & Variant::VSM) variantString += "VSM|";
if (variant.key & Variant::STE) variantString += "STE|";
variantString = variantString.substr(0, variantString.length() - 1);
}

View File

@@ -111,7 +111,7 @@ highp vec4 getSpotLightSpacePosition(int index, highp vec3 dir, highp float zLig
highp mat4 lightFromWorldMatrix = shadowUniforms.shadows[index].lightFromWorldMatrix;
// for spotlights, the bias depends on z
highp vec2 bias = shadowUniforms.shadows[index].normalBias * zLight;
float bias = shadowUniforms.shadows[index].normalBias * zLight;
return computeLightSpacePosition(getWorldPosition(), getWorldGeometricNormalVector(),
dir, bias, lightFromWorldMatrix);

View File

@@ -12,51 +12,12 @@
*/
highp vec4 computeLightSpacePosition(highp vec3 p, const highp vec3 n,
const highp vec3 dir, const highp vec2 b, highp_mat4 lightFromWorldMatrix) {
const highp vec3 dir, const float b, highp_mat4 lightFromWorldMatrix) {
#if !defined(VARIANT_HAS_VSM)
// --------------------------------------------------------------------------------------
// Anisotropic Normal Bias for Shadow Mapping
// --------------------------------------------------------------------------------------
// To prevent shadow acne, we must push the geometry along its normal to clear the
// quantization steps of the shadow map's discrete depth grid. The exact physical depth
// error we must clear is proportional to the shadow texel's world-space dimensions.
//
// This implementation computes the exact geometric projection of the rectangular
// shadow map texel onto the surface normal.
//
// 1. Coordinate Space Transition:
// We project the world-space normal onto the light's X and Y basis vectors (L_right,
// L_up). This gives us the lateral components of the normal in Light Space (n_Lx, n_Ly).
//
// 2. The Implicit sin(theta) Slope Scale:
// Because the normal is a unit vector, the magnitude of its lateral components in
// light space inherently equals sin(theta), where theta is the angle of incidence.
// This perfectly and automatically scales the bias from 0.0 (top-down, flat surface)
// to maximum (grazing angle).
//
// 3. Exact Anisotropic Footprint (The L1 Norm):
// Shadow texels are rarely perfectly square due to Cascaded Shadow Maps (CSM) or
// Light Space Perspective Shadow Maps (LiSPSM). Jx and Jy are the physical world-space
// dimensions of the texel.
// By evaluating `abs(n_Lx * Jx) + abs(n_Ly * Jy)`, we compute the exact scalar
// projection of the rectangular texel footprint.
// - It is superior to `max(Jx, Jy)` which assumes a massive square and causes Peter Panning.
// - It is superior to `length()` which assumes an ellipse and under-biases the corners.
// --------------------------------------------------------------------------------------
// Extract the first row (Light's Right vector in World Space)
highp vec3 L_right = vec3(lightFromWorldMatrix[0][0], lightFromWorldMatrix[1][0], lightFromWorldMatrix[2][0]);
// Extract the second row (Light's Up vector in World Space)
highp vec3 L_up = vec3(lightFromWorldMatrix[0][1], lightFromWorldMatrix[1][1], lightFromWorldMatrix[2][1]);
// Project the world normal onto the shadow map's 2D grid
highp float n_Lx = dot(n, L_right);
highp float n_Ly = dot(n, L_up);
// Apply the anisotropic normal bias
p += n * (abs(n_Lx * b.x) + abs(n_Ly * b.y));
highp float cosTheta = saturate(dot(n, dir));
highp float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
p += n * (sinTheta * b);
#endif
return mulMat4x4Float3(lightFromWorldMatrix, p);

View File

@@ -5,9 +5,10 @@ struct ShadowData {
highp mat4 lightFromWorldMatrix;
highp vec4 lightFromWorldZ;
highp vec4 scissorNormalized;
mediump float texelSizeAtOneMeter;
mediump float bulbRadiusLs;
mediump float nearOverFarMinusNear;
highp vec2 normalBias;
mediump float normalBias;
bool elvsm;
mediump uint layer;
mediump uint reserved1;

View File

@@ -76,11 +76,10 @@ into **branch** of `filament-assets`. This branch is paired with a PR or commit
As an example, imagine I am working on a PR, and I've uploaded my change, which is in a
branch called `my-pr-branch`, to `filament`. This PR requires updating the golden. We would do
it in the following fashion on a macOS machine:
it in the following fashion
### Using a script to update the golden repo
- Make sure you've completed the steps in 'Setting up python'
- Run interactive mode in the `update_golden.py` script.
```
python3 test/renderdiff/src/update_golden.py

View File

@@ -16,7 +16,6 @@ import os
import shutil
import re
import sys
import tempfile
from utils import execute, ArgParseImpl, mkdir_p
@@ -100,11 +99,10 @@ class GoldenManager:
code, old_commit = execute(f'git log --format=%B -n 1', cwd=assets_dir)
if tag and len(tag) > 0:
old_commit += f'\nFILAMENT={tag}'
with tempfile.NamedTemporaryFile('w', delete=False) as f:
COMMIT_FILE = '/tmp/golden_commit.txt'
with open(COMMIT_FILE, 'w') as f:
f.write(old_commit)
commit_file = f.name
self._git_exec(f'commit --amend -F {commit_file}')
os.remove(commit_file)
self._git_exec(f'commit --amend -F {COMMIT_FILE}')
# Do the actual merge
self._git_exec(f'checkout main')
@@ -141,11 +139,11 @@ class GoldenManager:
os.path.join(rdiff_dir, f))
self._git_exec(f'add {os.path.join(GOLDENS_DIR, f)}')
with tempfile.NamedTemporaryFile('w', delete=False) as f:
TMP_GOLDEN_COMMIT_FILE = '/tmp/golden_commit.txt'
with open(TMP_GOLDEN_COMMIT_FILE, 'w') as f:
f.write(commit_msg)
tmp_golden_commit_file = f.name
self._git_exec(f'commit -a -F {tmp_golden_commit_file}')
os.remove(tmp_golden_commit_file)
self._git_exec(f'commit -a -F {TMP_GOLDEN_COMMIT_FILE}')
if push_to_remote and \
(self.access_token_ or self.access_type_ == ACCESS_TYPE_SSH):
self._git_exec(f'push -f origin {branch}')

View File

@@ -64,19 +64,19 @@ def _do_update(golden_manager, config):
def _same_image_diffimg(diffimg_path, img1, img2):
cmd = [diffimg_path, img1, img2]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
raise RuntimeError(f"diffimg failed with return code {result.returncode}:\n{result.stderr}")
output = result.stdout.strip()
if not output:
raise RuntimeError("diffimg produced no output")
try:
res_json = json.loads(output)
return res_json.get('passed', False)
except json.JSONDecodeError as e:
raise RuntimeError(f"Failed to parse diffimg output: {e}\nOutput: {output}")
result = subprocess.run(cmd, capture_output=True, text=True)
output = result.stdout.strip()
if not output:
return False
try:
res_json = json.loads(output)
return res_json.get('passed', False)
except json.JSONDecodeError:
return False
except Exception:
return False
def _get_deletes_updates(update_dir, golden_dir, diffimg_path):
ret_delete = []
@@ -93,13 +93,13 @@ def _get_deletes_updates(update_dir, golden_dir, diffimg_path):
# However, update_golden typically only updates/adds based on the new render set.
# But strict sync might imply deleting missing ones.
# The original logic was: delete = list(base - new).
delete_files = list(base_files - new_files)
delete = list(base - new)
# Files in new but not in base are definitely updates (additions)
update_files = list(new_files - base_files)
update = list(new - base)
# Files in both need comparison
for fpath in base_files.intersection(new_files):
for fpath in base.intersection(new_files):
base_fpath = os.path.join(golden_dir, fpath)
new_fpath = os.path.join(update_dir, fpath)
@@ -110,10 +110,10 @@ def _get_deletes_updates(update_dir, golden_dir, diffimg_path):
is_different = _file_as_str(new_fpath) != _file_as_str(base_fpath)
if is_different:
ret_update.append(fpath)
update.append(fpath)
ret_update += update_files
ret_delete += delete_files
ret_update += update
ret_delete += delete
return ret_delete, ret_update
@@ -179,7 +179,7 @@ if __name__ == "__main__":
parser = ArgParseImpl()
parser.add_argument('--branch', help='Branch of the golden repo to write to')
parser.add_argument('--golden-repo-token', help='Access token for the golden repo')
parser.add_argument('--push-to-remote', action="store_true", help='Push the golden repo changes to remote')
parser.add_argument('--push-to-remote', action="store_true", help='Access token for the golden repo')
parser.add_argument('--diffimg', help='Path to the diffimg tool',
default='./out/cmake-release/tools/diffimg/diffimg')

View File

@@ -18,7 +18,6 @@ import shutil
import argparse
import sys
import pathlib
import shlex
def execute(cmd,
cwd=None,
@@ -26,7 +25,7 @@ def execute(cmd,
stdin=None,
env=None,
raise_errors=False):
in_env = os.environ.copy()
in_env = os.environ
in_env.update(env if env else {})
home = os.environ['HOME']
if f'{home}/bin' not in in_env['PATH']:
@@ -46,11 +45,11 @@ def execute(cmd,
'universal_newlines': True
}
if capture_output:
process = subprocess.Popen(shlex.split(cmd), **kwargs)
process = subprocess.Popen(cmd.split(' '), **kwargs)
output, err_output = process.communicate()
return_code = process.returncode
else:
return_code = subprocess.call(shlex.split(cmd), **kwargs)
return_code = subprocess.call(cmd.split(' '), **kwargs)
if return_code:
# Error

View File

@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.69.4",
"version": "1.69.5",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",