Compare commits

..
47 Commits
Author SHA1 Message Date
Benjamin Doherty d3be6a99e3 Test commit 2024-09-16 12:09:26 -07:00
Powei Feng 3e556588fc Merge branch 'rc/1.54.3' into release 2024-09-10 12:26:56 -07:00
Powei Feng 32b0625f36 Revert "reenable the SimplificationPass in spirv-opt"
This reverts commit 30387af61c.

Causes breakage on Pixel 8pro for 1P apps.
2024-09-09 10:37:11 -07:00
Sungun Park bb1d1c7349 Merge branch 'rc/1.54.2' into release 2024-09-04 18:55:59 +00:00
Sungun Park c967fb7860 Bump version to 1.54.3 2024-09-04 18:55:59 +00:00
Sungun Park 1b4afbab51 Release Filament 1.54.2 2024-09-04 18:55:52 +00:00
Powei Feng c677607353 github: Split Android CI to per ABI builds
- Pulled the android continuous workflow into an action
 - Split the android continuous build into 3 ABIs armv7, armv8a,
   and x86_64 (note that x86 is not present).
 - Remove the upload artifacts step from previous workflow.
   This was meant for letting client try a tip-of-tree Android
   build.  We can revisit this later. (Also removed mention
   in README.md)
 - Split the android continous into debug build and a release
   build commands, enabling deletion of intermediate output
   directory.
2024-09-04 11:22:43 -07:00
Powei Feng 422fcea2cf Make builderMakeName public
This call is used in the BuilderNameMixin template definition,
which is a public API.
2024-09-04 17:07:51 +00:00
Powei Feng cdb539b3cf Make builderMakeName public
This call is used in the BuilderNameMixin template definition,
which is a public API.
2024-09-04 10:04:20 -07:00
Mathias Agopian b0d3f14243 a handle with a tag would sometime return "no tag"
this is because the key used to retrieve the tag was not "truncated"
like it was when inserting the tag in the hash-map.
2024-09-03 17:13:59 +00:00
Ben Doherty 16bed4de00 Enable mmap on iOS (#8100) 2024-09-03 09:58:13 -07:00
Mathias Agopian 2b620e65fd fix potential framegraph textures use-after free
make sure to unset all textures in the per-view sampler group after
they are used, because the resource could be destroyed after the
pass is finished

- unset the fog and ibl_specular after the color pass
- move that cleanup a bit earlier
- in the case of screen-space reflection the structure pass is
  set, but might not be used in the color pass, so we also need to
  unset it after the SSR pass and before any other passes.
2024-08-30 16:24:25 -07:00
Mathias Agopian 2aa51db614 add texture tagging in the FrameGraph
Also tag user texture "FTexture" if user doesn't provide a tag, this
is so that we can distinguish them from internal textures that might
not be tagged.
2024-08-30 16:24:25 -07:00
Mathias AgopianandPowei Feng 2bbbb7f4d1 Update filament/src/details/Renderer.cpp
Co-authored-by: Powei Feng <powei@google.com>
2024-08-30 16:22:49 -07:00
Mathias Agopian 339e8da976 repair the "no buffer padding case"
with a previous change we were too aggressive in falling back to 
"no buffer padding", we need to do this only in the case where
"as subpass" would have been used if supported by the h/w.
2024-08-30 16:22:49 -07:00
Mathias Agopian c36dd955f4 make pushGroupMarker and insertEventMarker null-terminated strings
This is because we had this assumption on vk and mtl backends already
and also because SYSTRACE works this way and we didn't have non-null
terminated strings. This makes things more consistant and less bug 
prone.
2024-08-30 15:29:09 -07:00
Mathias Agopian 950be941eb disabling ResourceAllocator led to incorrect assert
this is because the "disposer" is now separate from the 
ResourceAllocator, so even if we don't use the resource allocator we
need to register the handle with the disposer.
2024-08-30 15:28:54 -07:00
Mathias Agopian 3857e3789c a handle with a tag would sometime return "no tag"
this is because the key used to retrieve the tag was not "truncated"
like it was when inserting the tag in the hash-map.
2024-08-30 15:28:37 -07:00
Mathias Agopian 2202b5ab8c Add support for depth clamp and use it for shadows
vk, metal and desktop gl all support depth clamp, GLES/android also does
with ANGLE. Add support for it in the backends.

use depth clamp to improve directional shadow quality; this allows
to render everything that's behind the camera at the same "zero" depth,
so we can reduce the depth range we need.

Fixes #6293
2024-08-30 10:56:31 -07:00
Sungun Park 639b933fd6 Rename customRenderTarget (#8093)
The currentRenderTarget is a misnomer as it's a custom RT. Rename it
properly.
2024-08-29 11:47:24 -07:00
Mathias Agopian 6653c6c08b fix a typo in RenderPassBuilder::renderFlags() 2024-08-28 21:51:03 -07:00
Maximilien Dagois 777f664b1b Refactored the handling of usage flags in the VulkanTexture constructor 2024-08-28 12:58:31 -07:00
Powei Feng f5f1e56123 Re-enable DebugRegistry::setProperty for release build (#8086)
`DebugRegistry::setProperty` is no longer just applicable to debug
builds.

This change was previously added in 6c0bd36 but then reverted
in a7317e7. We re-enable it and separate it from the shadow
changes in this commit.
2024-08-28 12:17:11 -07:00
Powei Feng 283d240409 Re-enable DebugRegistry::setProperty for release build (#8086)
`DebugRegistry::setProperty` is no longer just applicable to debug
builds.

This change was previously added in 6c0bd36 but then reverted
in a7317e7. We re-enable it and separate it from the shadow
changes in this commit.
2024-08-28 06:17:26 +00:00
Sungun Park f743bedef9 Bump version to 1.54.2 2024-08-27 23:30:37 +00:00
Sungun Park 0e0f3a5518 Release Filament 1.54.1 2024-08-27 23:30:26 +00:00
Powei Feng ba5413622f Decouple subpass from buffer padding (#8085)
Coupling subpass on/off with buffer padding has unintended
consequences when we need to turn subpass off for tests. We
uncouple them in this change.
2024-08-27 22:33:31 +00:00
Ben Doherty ba8d429fcb Metal: annotate memory allocation failures with buffer tag (#8082) 2024-08-27 10:49:35 -07:00
Mathias Agopian f11e5cb081 remove uses of the blackboard
the framegraph blackboard tends to create a lot of problems hard to 
debug, so we are now more explicit. here we remove usages of the
blackboard in RenderUtils.cpp.
2024-08-27 09:55:00 -07:00
Mathias Agopian c84f5d2a7f fix screen-space reflection when sub-pass colorgrading is used
the problem was that in that case, SSR was using the colorgraded
color buffer as history.
2024-08-27 09:55:00 -07:00
Sungun Park a7317e7a99 Revert two depth relevant changes (#8083)
This reverts commits
- b70aa43727 "depth clamp cannot work with VSM"
- 6c0bd360b3 "Add support for depth clamp and use it for shadows"
2024-08-26 23:30:15 +00:00
Mathias Agopian be4391950d fix gltfio ubershaders
default parameters were not initialized which could cause them to
be incorrectly evaluated in the shader. this is actually a pretty
crazy bug that has been around since forever and which we were
"lucky" to not run into sooner.

this was exposed with the specular extension that was added recently.
2024-08-26 10:24:42 -07:00
Ben Doherty 6f20cf4b02 Support tagging driver handles with a name (#8038) 2024-08-24 09:17:05 -07:00
Mathias Agopian 9674a5ae3c add support for subresources in "blit"
This is needed for debugging, but is a pretty low-hanging fruit.
2024-08-23 22:37:01 -07:00
Balaji MandPowei Feng 485c05789b return statement moved to separate line
Co-authored-by: Powei Feng <powei@google.com>
2024-08-23 11:02:19 -07:00
Balaji M b058794dd1 decoding meshoptimized gltf just once 2024-08-23 11:02:19 -07:00
Powei Feng be22e9305d matc: initialize CustomVariable (#8076)
The default values were not provided via default construction.
Caused matc to crash on Linux.
2024-08-23 16:09:56 +00:00
Sungun Park 4ae7aa6a1b Fix compatibility warning for multiview (#8075)
Verify the compatibility between a compiled material and the engine's
setting only when the engine is set up for stereo.

Default materials are always compiled with either 'instanced' or
'multiview. Therefore Filament will emit warnings unintentionally if the
engine is set up for stereo. This commit fixes it.
2024-08-22 20:39:04 +00:00
Mathias Agopian 5e7106b521 custom variables can now have their precision specified
e.g.:

    variables : [
        {
            name : vertex,
            precision : medium,
         }
    ],
2024-08-22 13:08:48 -07:00
Mathias Agopian 30387af61c reenable the SimplificationPass in spirv-opt
Issues with it were fixed (https://github.com/KhronosGroup/SPIRV-Cross/pull/2163),
so it should be safe to reenable. The invalid glsl code seems to be resolved.
2024-08-22 13:08:23 -07:00
Mathias Agopian 1c2ffc9ed4 new screenshot debug option in gltf_viewer
screenshots are saved in the current directory as 
"screenshotXX.ppm" with XX increasing after each
screenshot.
2024-08-20 22:41:49 -07:00
Sungun Park 574518ea74 Cleanup color pass for multiview (#8062) 2024-08-20 21:44:17 +00:00
Ben Doherty d5a274fa25 Add better glShaderSource fix for IMG devices (#8061) 2024-08-20 12:28:13 -07:00
Mathias Agopian 3351db1178 improve FrameSkipper when disregarding skipping
It is legal for the app to draw a frame even when FrameSkipper 
detects that a frame should be skipped. In that case we used to
overwrite the most recent fence, but this wasn't ideal. We now
proceed as if the fence had signaled, i.e. we destroy it and
move all the fences up, creating a new one at the end of the 
delayed array.
2024-08-20 12:07:59 -07:00
Mathias Agopian b70aa43727 depth clamp cannot work with VSM
This is because the shadowmap doesn't store depth values so it doesn't 
work to "flatten" all casters behind de camera to the near plane.
The bulk of shadowing works but the filtering/edges don't always.

Disable depth-clamping when VSM is enabled.
2024-08-20 12:04:01 -07:00
Sungun Park 1f9a11802d Minor tweak (#8053)
Move the common part outside the loop.
2024-08-20 17:13:55 +00:00
Sungun Park 89835a7a67 Fix a bug for calculating distance (#8058)
This is a missing part from the change
22d99bac3d
2024-08-20 16:28:29 +00:00
69 changed files with 671 additions and 352 deletions
@@ -0,0 +1,17 @@
name: 'Android Continuous'
inputs:
build-abi:
description: 'The target platform ABI'
required: true
default: 'armeabi-v7a'
runs:
using: "composite"
steps:
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Run build script
run: |
cd build/android && printf "y" | ./build.sh continuous ${{ inputs.build-abi }}
shell: bash
+25 -22
View File
@@ -8,32 +8,35 @@ on:
- rc/**
jobs:
build-android:
name: build-android
build-android-armv7:
name: build-android-armv7
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
- uses: actions/setup-java@v3
- name: Run Android Continuous
uses: ./.github/actions/android-continuous
with:
distribution: 'temurin'
java-version: '17'
- name: Run build script
run: |
cd build/android && printf "y" | ./build.sh continuous
- uses: actions/upload-artifact@v1.0.0
build-abi: armeabi-v7a
build-android-armv8a:
name: build-android-armv8a
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
- name: Run Android Continuous
uses: ./.github/actions/android-continuous
with:
name: filament-android
path: out/filament-android-release.aar
- uses: actions/upload-artifact@v1.0.0
build-abi: arm64-v8a
build-android-x86_64:
name: build-android-x86_64
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
- name: Run Android Continuous
uses: ./.github/actions/android-continuous
with:
name: filamat-android-full
path: out/filamat-android-release.aar
- uses: actions/upload-artifact@v1.0.0
with:
name: gltfio-android-release
path: out/gltfio-android-release.aar
- uses: actions/upload-artifact@v1.0.0
with:
name: filament-utils-android-release
path: out/filament-utils-android-release.aar
build-abi: x86_64
+3 -1
View File
@@ -49,8 +49,10 @@ jobs:
distribution: 'temurin'
java-version: '17'
- name: Run build script
# Only build 1 64 bit target during presubmit to cut down build times during presubmit
# Continuous builds will build everything
run: |
cd build/android && printf "y" | ./build.sh presubmit
cd build/android && printf "y" | ./build.sh presubmit arm64-v8a
build-ios:
name: build-iOS
+1 -1
View File
@@ -120,7 +120,7 @@ jobs:
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
cd build/android && printf "y" | ./build.sh release
cd build/android && printf "y" | ./build.sh release armeabi-v7a,arm64-v8a,x86,x86_64
cd ../..
mv out/filament-android-release.aar out/filament-${TAG}-android.aar
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
-2
View File
@@ -7,5 +7,3 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- Add a `name` API to Filament objects for debugging handle use-after-free assertions
+2 -12
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.54.1'
implementation 'com.google.android.filament:filament-android:1.54.3'
}
```
@@ -51,19 +51,9 @@ 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.54.1'
pod 'Filament', '~> 1.54.3'
```
### Snapshots
If you prefer to live on the edge, you can download a continuous build by following the following
steps:
1. Find the [commit](https://github.com/google/filament/commits/main) you're interested in.
2. Click the green check mark under the commit message.
3. Click on the _Details_ link for the platform you're interested in.
4. On the top left click _Summary_, then in the _Artifacts_ section choose the desired artifact.
## Documentation
- [Filament](https://google.github.io/filament/Filament.html), an in-depth explanation of
+7
View File
@@ -7,6 +7,13 @@ 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.54.3
## v1.54.2
- Add a `name` API to Filament objects for debugging handle use-after-free assertions
## v1.54.1
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.54.1
VERSION_NAME=1.54.3
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+15 -7
View File
@@ -60,13 +60,7 @@ if [[ ! -d "${ANDROID_HOME}/ndk/$FILAMENT_NDK_VERSION" ]]; then
yes | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --licenses
${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager "ndk;$FILAMENT_NDK_VERSION"
fi
# Only build 1 64 bit target during presubmit to cut down build times during presubmit
# Continuous builds will build everything
ANDROID_ABIS=
if [[ "$TARGET" == "presubmit" ]]; then
ANDROID_ABIS="-q arm64-v8a"
fi
# Build the Android sample-gltf-viewer APK during release.
BUILD_SAMPLES=
@@ -74,5 +68,19 @@ if [[ "$TARGET" == "release" ]]; then
BUILD_SAMPLES="-k sample-gltf-viewer"
fi
function build_android() {
local ABI=$1
# Do the following in two steps so that we do not run out of space
if [[ -n "${BUILD_DEBUG}" ]]; then
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION} ./build.sh -p android -q ${ABI} -c ${BUILD_SAMPLES} ${GENERATE_ARCHIVES} ${BUILD_DEBUG}
rm -rf out/cmake-android-debug-*
fi
if [[ -n "${BUILD_RELEASE}" ]]; then
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION} ./build.sh -p android -q ${ABI} -c ${BUILD_SAMPLES} ${GENERATE_ARCHIVES} ${BUILD_RELEASE}
rm -rf out/cmake-android-release-*
fi
}
pushd `dirname $0`/../.. > /dev/null
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION} ./build.sh -p android $ANDROID_ABIS -c $BUILD_SAMPLES $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE
build_android $2
+11 -2
View File
@@ -1308,7 +1308,12 @@ Description
declare a variable called `eyeDirection` you can access it in the fragment shader using
`variable_eyeDirection`. In the vertex shader, the interpolant name is simply a member of
the `MaterialVertexInputs` structure (`material.eyeDirection` in your example). Each
interpolant is of type `float4` (`vec4`) in the shaders.
interpolant is of type `float4` (`vec4`) in the shaders. By default the precision of the
interpolant is `highp` in *both* the vertex and fragment shaders.
An alternate syntax can be used to specify both the name and precision of the interpolant.
In this case the specified precision is used as-is in both fragment and vertex stages, in
particular if `default` is specified the default precision is used is the fragment shader
(`mediump`) and in the vertex shader (`highp`).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
@@ -1320,7 +1325,11 @@ material {
}
],
variables : [
eyeDirection
eyeDirection,
{
name : eyeColor,
precision : medium
}
],
vertexDomain : device,
depthWrite : false,
@@ -1058,7 +1058,7 @@ struct RasterState {
bool inverseFrontFaces : 1; // 31
//! padding, must be 0
uint8_t padding : 1; // 32
bool depthClamp : 1; // 32
};
uint32_t u = 0;
};
@@ -309,6 +309,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isDepthStencilBlitSupported, backend::TextureFormat, format)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthClampSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
@@ -443,12 +444,10 @@ DECL_DRIVER_API_N(setPushConstant,
backend::PushConstantVariant, value)
DECL_DRIVER_API_N(insertEventMarker,
const char*, string,
uint32_t, len = 0)
const char*, string)
DECL_DRIVER_API_N(pushGroupMarker,
const char*, string,
uint32_t, len = 0)
const char*, string)
DECL_DRIVER_API_0(popGroupMarker)
@@ -208,14 +208,8 @@ public:
// TODO: for now, only pool handles check for use-after-free, so we only keep tags for
// those
if (isPoolHandle(id)) {
// Truncate the tag's age to N bits.
constexpr uint8_t N = 2; // support a history of 4 tags
constexpr uint8_t mask = (1 << N) - 1;
uint8_t const age = (id & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
uint8_t const newAge = age & mask;
uint32_t const key = (id & ~HANDLE_AGE_MASK) | (newAge << HANDLE_AGE_SHIFT);
// Truncate the age to get the debug tag
uint32_t const key = id & ~(HANDLE_DEBUG_TAG_MASK ^ HANDLE_AGE_MASK);
// This line is the costly part. In the future, we could potentially use a custom
// allocator.
mDebugTags[key] = std::move(tag);
@@ -226,7 +220,8 @@ public:
if (!isPoolHandle(id)) {
return "(no tag)";
}
if (auto pos = mDebugTags.find(id); pos != mDebugTags.end()) {
uint32_t const key = id & ~(HANDLE_DEBUG_TAG_MASK ^ HANDLE_AGE_MASK);
if (auto pos = mDebugTags.find(key); pos != mDebugTags.end()) {
return pos->second;
}
return "(no tag)";
@@ -349,12 +344,24 @@ private:
}
}
// we handle a 4 bits age per address
static constexpr uint32_t HANDLE_HEAP_FLAG = 0x80000000u; // pool vs heap handle
static constexpr uint32_t HANDLE_AGE_MASK = 0x78000000u; // handle's age
static constexpr uint32_t HANDLE_INDEX_MASK = 0x07FFFFFFu; // handle index
static constexpr uint32_t HANDLE_TAG_MASK = HANDLE_AGE_MASK;
static constexpr uint32_t HANDLE_AGE_SHIFT = 27;
// number if bits allotted to the handle's age (currently 4 max)
static constexpr uint32_t HANDLE_AGE_BIT_COUNT = 4;
// number if bits allotted to the handle's debug tag (HANDLE_AGE_BIT_COUNT max)
static constexpr uint32_t HANDLE_DEBUG_TAG_BIT_COUNT = 2;
// bit shift for both the age and debug tag
static constexpr uint32_t HANDLE_AGE_SHIFT = 27;
// mask for the heap (vs pool) flag
static constexpr uint32_t HANDLE_HEAP_FLAG = 0x80000000u;
// mask for the age
static constexpr uint32_t HANDLE_AGE_MASK =
((1 << HANDLE_AGE_BIT_COUNT) - 1) << HANDLE_AGE_SHIFT;
// mask for the debug tag
static constexpr uint32_t HANDLE_DEBUG_TAG_MASK =
((1 << HANDLE_DEBUG_TAG_BIT_COUNT) - 1) << HANDLE_AGE_SHIFT;
// mask for the index
static constexpr uint32_t HANDLE_INDEX_MASK = 0x07FFFFFFu;
static_assert(HANDLE_DEBUG_TAG_BIT_COUNT <= HANDLE_AGE_BIT_COUNT);
static bool isPoolHandle(HandleBase::HandleId id) noexcept {
return (id & HANDLE_HEAP_FLAG) == 0u;
@@ -369,7 +376,7 @@ private:
// a non-pool handle.
if (UTILS_LIKELY(isPoolHandle(id))) {
char* const base = (char*)mHandleArena.getArea().begin();
uint32_t const tag = id & HANDLE_TAG_MASK;
uint32_t const tag = id & HANDLE_AGE_MASK;
size_t const offset = (id & HANDLE_INDEX_MASK) * Allocator::getAlignment();
return { static_cast<void*>(base + offset), tag };
}
@@ -384,7 +391,7 @@ private:
size_t const offset = (char*)p - base;
assert_invariant((offset % Allocator::getAlignment()) == 0);
auto id = HandleBase::HandleId(offset / Allocator::getAlignment());
id |= tag & HANDLE_TAG_MASK;
id |= tag & HANDLE_AGE_MASK;
assert_invariant((id & HANDLE_HEAP_FLAG) == 0);
return id;
}
+1 -1
View File
@@ -24,7 +24,7 @@
#include <utils/debug.h>
#include <utils/ostream.h>
#if !defined(WIN32) && !defined(__EMSCRIPTEN__) && !defined(IOS)
#if !defined(WIN32) && !defined(__EMSCRIPTEN__)
# include <sys/mman.h>
# include <unistd.h>
# define HAS_MMAP 1
+2
View File
@@ -160,6 +160,8 @@ public:
size_t size, bool forceGpuBuffer = false);
~MetalBuffer();
[[nodiscard]] bool wasAllocationSuccessful() const noexcept { return mBuffer || mCpuBuffer; }
MetalBuffer(const MetalBuffer& rhs) = delete;
MetalBuffer& operator=(const MetalBuffer& rhs) = delete;
+2 -2
View File
@@ -53,8 +53,8 @@ MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType,
mBuffer = { [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate],
TrackedMetalBuffer::Type::GENERIC };
}
FILAMENT_CHECK_POSTCONDITION(mBuffer)
<< "Could not allocate Metal buffer of size " << size << ".";
// mBuffer might fail to be allocated. Clients can check for this by calling
// wasAllocationSuccessful().
}
MetalBuffer::~MetalBuffer() {
@@ -112,6 +112,7 @@ struct MetalContext {
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
CullModeStateTracker cullModeState;
WindingStateTracker windingState;
DepthClampStateTracker depthClampState;
Handle<HwRenderPrimitive> currentRenderPrimitive;
// State caches.
+3 -2
View File
@@ -57,7 +57,6 @@ class MetalDriver final : public DriverBase {
public:
static Driver* create(MetalPlatform* platform, const Platform::DriverConfig& driverConfig);
void runAtNextTick(const std::function<void()>& fn) noexcept;
private:
@@ -73,10 +72,12 @@ private:
/*
* Tasks run regularly on the driver thread.
* Not thread-safe; tasks are run from the driver thead and must be enqueued from the driver
* thread.
*/
void runAtNextTick(const std::function<void()>& fn) noexcept;
void executeTickOps() noexcept;
std::vector<std::function<void()>> mTickOps;
std::mutex mTickOpsLock;
/*
* Driver interface
+41 -8
View File
@@ -320,17 +320,40 @@ void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih) {
MetalVertexBufferInfo const* const vbi = handle_cast<const MetalVertexBufferInfo>(vbih);
construct_handle<MetalVertexBuffer>(vbh, *mContext, vertexCount, vbi->bufferCount, vbih);
// No actual GPU memory is allocated here, so no need to check for allocation success.
}
void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {
auto elementSize = (uint8_t) getElementTypeSize(elementType);
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
auto elementSize = (uint8_t)getElementTypeSize(elementType);
auto* indexBuffer =
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
auto& buffer = indexBuffer->buffer;
// If the allocation was not successful, postpone the error message until the next tick, to give
// Filament a chance to call setDebugTag on the handle; this way we get a nicer error message.
if (UTILS_UNLIKELY(!buffer.wasAllocationSuccessful())) {
const size_t byteCount = buffer.getSize();
runAtNextTick([byteCount, this, ibh]() {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Could not allocate Metal index buffer of size " << byteCount
<< ", tag=" << mHandleAllocator.getHandleTag(ibh.getId()).c_str_safe();
});
}
}
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
auto* bufferObject =
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
// If the allocation was not successful, postpone the error message until the next tick, to give
// Filament a chance to call setDebugTag on the handle; this way we get a nicer error message.
if (UTILS_UNLIKELY(!bufferObject->getBuffer()->wasAllocationSuccessful())) {
runAtNextTick([byteCount, this, boh]() {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Could not allocate Metal buffer of size " << byteCount
<< ", tag=" << mHandleAllocator.getHandleTag(boh.getId()).c_str_safe();
});
}
}
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
@@ -834,6 +857,10 @@ bool MetalDriver::isProtectedTexturesSupported() {
return false;
}
bool MetalDriver::isDepthClampSupported() {
return true;
}
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -1275,11 +1302,11 @@ void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
pushConstants.setPushConstant(value, index);
}
void MetalDriver::insertEventMarker(const char* string, uint32_t len) {
void MetalDriver::insertEventMarker(const char* string) {
}
void MetalDriver::pushGroupMarker(const char* string, uint32_t len) {
void MetalDriver::pushGroupMarker(const char* string) {
mContext->groupMarkers.push(string);
}
@@ -1751,6 +1778,13 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
[mContext->currentRenderPassEncoder setFrontFacingWinding:winding];
}
// depth clip mode
MTLDepthClipMode depthClipMode = rs.depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
mContext->depthClampState.updateState(depthClipMode);
if (mContext->depthClampState.stateChanged()) {
[mContext->currentRenderPassEncoder setDepthClipMode:depthClipMode];
}
// Set the depth-stencil state, if a state change is needed.
DepthStencilState depthState;
if (depthAttachment) {
@@ -1760,6 +1794,8 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
if (stencilAttachment) {
const auto& ss = ps.stencilState;
// Ben test
auto& front = depthState.front;
front.stencilCompare = getMetalCompareFunction(ss.front.stencilFunc);
front.stencilOperationStencilFail = getMetalStencilOperation(ss.front.stencilOpStencilFail);
@@ -2066,15 +2102,12 @@ void MetalDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag)
}
void MetalDriver::runAtNextTick(const std::function<void()>& fn) noexcept {
std::lock_guard<std::mutex> const lock(mTickOpsLock);
mTickOps.push_back(fn);
}
void MetalDriver::executeTickOps() noexcept {
std::vector<std::function<void()>> ops;
mTickOpsLock.lock();
std::swap(ops, mTickOps);
mTickOpsLock.unlock();
for (const auto& f : ops) {
f();
}
@@ -257,10 +257,6 @@ void MetalSwapChain::present() {
}
}
#ifndef FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD
#define FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD 1
#endif
class PresentDrawableData {
public:
PresentDrawableData() = delete;
@@ -279,14 +275,10 @@ public:
[that->mDrawable present];
}
#if FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD == 1
// mDrawable is acquired on the driver thread. Typically, we would release this object on
// the same thread, but after receiving consistent crash reports from within
// [CAMetalDrawable dealloc], we suspect this object requires releasing on the main thread.
dispatch_async(dispatch_get_main_queue(), ^{ cleanupAndDestroy(that); });
#else
that->mDriver->runAtNextTick([that]() { cleanupAndDestroy(that); });
#endif
}
private:
+1
View File
@@ -382,6 +382,7 @@ using SamplerStateCache = StateCache<SamplerState, id<MTLSamplerState>, SamplerS
using CullModeStateTracker = StateTracker<MTLCullMode>;
using WindingStateTracker = StateTracker<MTLWinding>;
using DepthClampStateTracker = StateTracker<MTLDepthClipMode>;
// Argument encoder
+6 -2
View File
@@ -202,6 +202,10 @@ bool NoopDriver::isProtectedTexturesSupported() {
return true;
}
bool NoopDriver::isDepthClampSupported() {
return false;
}
bool NoopDriver::isWorkaroundNeeded(Workaround) {
return false;
}
@@ -316,10 +320,10 @@ void NoopDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
}
void NoopDriver::insertEventMarker(char const* string, uint32_t len) {
void NoopDriver::insertEventMarker(char const* string) {
}
void NoopDriver::pushGroupMarker(char const* string, uint32_t len) {
void NoopDriver::pushGroupMarker(char const* string) {
}
void NoopDriver::popGroupMarker(int) {
@@ -541,9 +541,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
bugs->delay_fbo_destruction = true;
// PowerVR seems to have no problem with this (which is good for us)
bugs->allow_read_only_ancillary_feedback_loop = true;
// PowerVR doesn't respect lengths passed to glShaderSource, so concatenate them into a
// single string.
bugs->concatenate_shader_strings = true;
} else if (strstr(renderer, "Apple")) {
// Apple GPU
} else if (strstr(renderer, "Tegra") ||
@@ -679,6 +676,7 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
#ifndef __EMSCRIPTEN__
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
#endif
ext->EXT_depth_clamp = exts.has("GL_EXT_depth_clamp"sv);
ext->EXT_discard_framebuffer = exts.has("GL_EXT_discard_framebuffer"sv);
#ifndef __EMSCRIPTEN__
ext->EXT_disjoint_timer_query = exts.has("GL_EXT_disjoint_timer_query"sv);
@@ -749,6 +747,7 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
ext->EXT_color_buffer_half_float = true; // Assumes core profile.
ext->EXT_clip_cull_distance = true;
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
ext->EXT_depth_clamp = true;
ext->EXT_discard_framebuffer = false;
ext->EXT_disjoint_timer_query = true;
ext->EXT_multisampled_render_to_texture = false;
+6 -13
View File
@@ -220,8 +220,9 @@ public:
bool EXT_color_buffer_float;
bool EXT_color_buffer_half_float;
bool EXT_debug_marker;
bool EXT_disjoint_timer_query;
bool EXT_depth_clamp;
bool EXT_discard_framebuffer;
bool EXT_disjoint_timer_query;
bool EXT_multisampled_render_to_texture2;
bool EXT_multisampled_render_to_texture;
bool EXT_protected_textures;
@@ -239,10 +240,10 @@ public:
bool KHR_parallel_shader_compile;
bool KHR_texture_compression_astc_hdr;
bool KHR_texture_compression_astc_ldr;
bool OES_depth_texture;
bool OES_depth24;
bool OES_packed_depth_stencil;
bool OES_EGL_image_external_essl3;
bool OES_depth24;
bool OES_depth_texture;
bool OES_packed_depth_stencil;
bool OES_rgb8_rgba8;
bool OES_standard_derivatives;
bool OES_texture_npot;
@@ -315,12 +316,6 @@ public:
// bugs or performance issues.
bool force_feature_level0;
// Some drivers don't respect the length argument of glShaderSource() and (apparently)
// require each shader source string to be null-terminated. This works around the issue by
// concatenating the strings into a single null-terminated string before passing it to
// glShaderSource().
bool concatenate_shader_strings;
} bugs = {};
// state getters -- as needed.
@@ -567,9 +562,6 @@ private:
{ bugs.force_feature_level0,
"force_feature_level0",
""},
{ bugs.concatenate_shader_strings,
"concatenate_shader_strings",
""},
}};
// this is chosen to minimize code size
@@ -636,6 +628,7 @@ constexpr size_t OpenGLContext::getIndexForCap(GLenum cap) noexcept { //NOLINT
#ifdef BACKEND_OPENGL_VERSION_GL
case GL_PROGRAM_POINT_SIZE: index = 10; break;
#endif
case GL_DEPTH_CLAMP: index = 11; break;
default: break;
}
assert_invariant(index < state.enables.caps.size());
+16 -4
View File
@@ -451,6 +451,14 @@ void OpenGLDriver::setRasterState(RasterState rs) noexcept {
} else {
gl.disable(GL_SAMPLE_ALPHA_TO_COVERAGE);
}
if (gl.ext.EXT_depth_clamp) {
if (rs.depthClamp) {
gl.enable(GL_DEPTH_CLAMP);
} else {
gl.disable(GL_DEPTH_CLAMP);
}
}
}
void OpenGLDriver::setStencilState(StencilState ss) noexcept {
@@ -2119,6 +2127,10 @@ bool OpenGLDriver::isProtectedTexturesSupported() {
return getContext().ext.EXT_protected_textures;
}
bool OpenGLDriver::isDepthClampSupported() {
return getContext().ext.EXT_depth_clamp;
}
bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -3184,23 +3196,23 @@ void OpenGLDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::insertEventMarker(char const* string, uint32_t len) {
void OpenGLDriver::insertEventMarker(char const* string) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
auto& gl = mContext;
if (gl.ext.EXT_debug_marker) {
glInsertEventMarkerEXT(GLsizei(len ? len : strlen(string)), string);
glInsertEventMarkerEXT(GLsizei(strlen(string)), string);
}
#endif
#endif
}
void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
void OpenGLDriver::pushGroupMarker(char const* string) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_OPENGL
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
glPushGroupMarkerEXT(GLsizei(len ? len : strlen(string)), string);
glPushGroupMarkerEXT(GLsizei(strlen(string)), string);
}
#endif
#endif
@@ -597,40 +597,30 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
version = "#version 310 es\n";
}
const std::array<const char*, 5> sources = {
version.data(),
prolog.data(),
specializationConstantString.c_str(),
packingFunctions.data(),
body.data()
std::array<std::string_view, 5> sources = {
version,
prolog,
specializationConstantString,
packingFunctions,
{ body.data(), body.size() - 1 } // null-terminated
};
const std::array<GLint, 5> lengths = {
(GLint)version.length(),
(GLint)prolog.length(),
(GLint)specializationConstantString.length(),
(GLint)packingFunctions.length(),
(GLint)body.length() - 1 // null terminated
};
// Some of the sources may be zero-length. Remove them as to avoid passing lengths of
// zero to glShaderSource(). glShaderSource should work with lengths of zero, but some
// drivers instead interpret zero as a sentinel for a null-terminated string.
auto partitionPoint = std::stable_partition(
sources.begin(), sources.end(), [](std::string_view s) { return !s.empty(); });
size_t count = std::distance(sources.begin(), partitionPoint);
std::array<const char*, 5> shaderStrings;
std::array<GLint, 5> lengths;
for (size_t i = 0; i < count; i++) {
shaderStrings[i] = sources[i].data();
lengths[i] = sources[i].size();
}
GLuint const shaderId = glCreateShader(glShaderType);
if (UTILS_UNLIKELY(context.bugs.concatenate_shader_strings)) {
size_t totalSize = 0;
for (size_t i = 0; i < sources.size(); i++) {
totalSize += lengths[i];
}
std::string concatenatedShaderSource;
concatenatedShaderSource.reserve(totalSize);
for (size_t i = 0; i < sources.size(); i++) {
concatenatedShaderSource.append(sources[i], lengths[i]);
}
const GLchar* ptr = concatenatedShaderSource.c_str();
GLint length = concatenatedShaderSource.length();
glShaderSource(shaderId, 1, &ptr, &length);
} else {
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
}
glShaderSource(shaderId, count, shaderStrings.data(), lengths.data());
glCompileShader(shaderId);
+6
View File
@@ -201,6 +201,12 @@ using namespace glext;
# define GL_CLIP_DISTANCE1 0x3001
#endif
#if defined(GL_EXT_depth_clamp)
# define GL_DEPTH_CLAMP GL_DEPTH_CLAMP_EXT
#else
# define GL_DEPTH_CLAMP 0x864F
#endif
#if defined(GL_KHR_debug)
# define GL_DEBUG_OUTPUT GL_DEBUG_OUTPUT_KHR
# define GL_DEBUG_OUTPUT_SYNCHRONOUS GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR
@@ -125,6 +125,10 @@ public:
return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE;
}
inline bool isDepthClampSupported() const noexcept {
return mPhysicalDeviceFeatures.depthClamp == VK_TRUE;
}
inline bool isDebugMarkersSupported() const noexcept {
return mDebugMarkersSupported;
}
+8 -3
View File
@@ -944,6 +944,10 @@ bool VulkanDriver::isProtectedTexturesSupported() {
return false;
}
bool VulkanDriver::isDepthClampSupported() {
return mContext.isDepthClampSupported();
}
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU: {
@@ -1602,13 +1606,13 @@ void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
value);
}
void VulkanDriver::insertEventMarker(char const* string, uint32_t len) {
void VulkanDriver::insertEventMarker(char const* string) {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands.insertEventMarker(string, len);
mCommands.insertEventMarker(string, strlen(string));
#endif
}
void VulkanDriver::pushGroupMarker(char const* string, uint32_t) {
void VulkanDriver::pushGroupMarker(char const* string) {
// Turns out all the markers are 0-terminated, so we can just pass it without len.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands.pushGroupMarker(string);
@@ -1816,6 +1820,7 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
.dstAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionDstAlpha),
.colorWriteMask = (VkColorComponentFlags) (rasterState.colorWrite ? 0xf : 0x0),
.rasterizationSamples = rt->getSamples(),
.depthClamp = rasterState.depthClamp,
.colorTargetCount = rt->getColorTargetCount(mCurrentRenderPass),
.colorBlendOp = rasterState.blendEquationRGB,
.alphaBlendOp = rasterState.blendEquationAlpha,
@@ -16,7 +16,6 @@
#include "VulkanHandles.h"
#include "VulkanDriver.h"
#include "VulkanConstants.h"
#include "VulkanDriver.h"
#include "VulkanMemory.h"
@@ -184,6 +184,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
vkRaster.polygonMode = VK_POLYGON_MODE_FILL;
vkRaster.cullMode = raster.cullMode;
vkRaster.frontFace = raster.frontFace;
vkRaster.depthClampEnable = raster.depthClamp;
vkRaster.depthBiasEnable = raster.depthBiasEnable;
vkRaster.depthBiasConstantFactor = raster.depthBiasConstantFactor;
vkRaster.depthBiasClamp = 0.0f;
@@ -90,7 +90,8 @@ public:
VkBlendFactor srcAlphaBlendFactor : 5;
VkBlendFactor dstAlphaBlendFactor : 5;
VkColorComponentFlags colorWriteMask : 4;
uint8_t rasterizationSamples; // offset = 4 bytes
uint8_t rasterizationSamples : 4;// offset = 4 bytes
uint8_t depthClamp : 4;
uint8_t colorTargetCount; // offset = 5 bytes
BlendEquation colorBlendOp : 4; // offset = 6 bytes
BlendEquation alphaBlendOp : 4;
+7 -10
View File
@@ -97,13 +97,11 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.extent.depth = 1;
}
// Filament expects blit() to work with any texture, so we almost always set these usage flags.
// TODO: investigate performance implications of setting these flags.
constexpr VkImageUsageFlags blittable = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::BLIT_SRC))) {
imageInfo.usage |= blittable;
if (any(usage & TextureUsage::BLIT_SRC)) {
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(usage & TextureUsage::BLIT_DST)) {
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
if (any(usage & TextureUsage::SAMPLEABLE)) {
@@ -121,7 +119,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | blittable;
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
if (any(usage & TextureUsage::SUBPASS_INPUT)) {
imageInfo.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
}
@@ -130,10 +128,9 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
if (any(usage & TextureUsage::UPLOADABLE)) {
imageInfo.usage |= blittable;
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
imageInfo.usage |= blittable;
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
// Depth resolves uses a custom shader and therefore needs to be sampleable.
@@ -325,6 +325,7 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
// We could simply enable all supported features, but since that may have performance
// consequences let's just enable the features we need.
VkPhysicalDeviceFeatures enabledFeatures{
.depthClamp = features.depthClamp,
.samplerAnisotropy = features.samplerAnisotropy,
.textureCompressionETC2 = features.textureCompressionETC2,
.textureCompressionBC = features.textureCompressionBC,
+2 -1
View File
@@ -55,7 +55,8 @@ public:
template<typename T>
using BuilderBase = utils::PrivateImplementation<T>;
void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept;
// This needs to be public because it is used in the following template.
UTILS_PUBLIC void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept;
template <typename Builder>
class UTILS_PUBLIC BuilderNameMixin {
+25 -18
View File
@@ -16,16 +16,22 @@
#include "FrameSkipper.h"
#include <utils/Log.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <algorithm>
#include <stddef.h>
namespace filament {
using namespace utils;
using namespace backend;
FrameSkipper::FrameSkipper(size_t latency) noexcept
: mLast(latency - 1) {
: mLast(std::max(latency, MAX_FRAME_LATENCY) - 1) {
assert_invariant(latency <= MAX_FRAME_LATENCY);
}
@@ -41,31 +47,32 @@ void FrameSkipper::terminate(DriverApi& driver) noexcept {
bool FrameSkipper::beginFrame(DriverApi& driver) noexcept {
auto& fences = mDelayedFences;
auto fence = fences.front();
if (fence) {
auto status = driver.getFenceStatus(fence);
if (status == FenceStatus::TIMEOUT_EXPIRED) {
// Sync not ready, skip frame
if (fences.front()) {
// Do we have a latency old fence?
auto status = driver.getFenceStatus(fences.front());
if (UTILS_UNLIKELY(status == FenceStatus::TIMEOUT_EXPIRED)) {
// The fence hasn't signaled yet, skip this frame
return false;
}
assert_invariant(status == FenceStatus::CONDITION_SATISFIED);
driver.destroyFence(fence);
}
// shift all fences down by 1
std::move(fences.begin() + 1, fences.end(), fences.begin());
fences.back() = {};
return true;
}
void FrameSkipper::endFrame(DriverApi& driver) noexcept {
// If the user produced a new frame despite the fact that the previous one wasn't finished
// (i.e. FrameSkipper::beginFrame() returned false), we need to make sure to replace
// a fence that might be here already)
auto& fence = mDelayedFences[mLast];
if (fence) {
driver.destroyFence(fence);
auto& fences = mDelayedFences;
size_t const last = mLast;
// pop the oldest fence and advance the other ones
if (fences.front()) {
driver.destroyFence(fences.front());
}
fence = driver.createFence();
std::move(fences.begin() + 1, fences.end(), fences.begin());
// add a new fence to the end
assert_invariant(!fences[last]);
fences[last] = driver.createFence();
}
} // namespace filament
+4 -1
View File
@@ -22,6 +22,9 @@
#include <array>
#include <stddef.h>
#include <stdint.h>
namespace filament {
/*
@@ -60,7 +63,7 @@ public:
private:
using Container = std::array<backend::Handle<backend::HwFence>, MAX_FRAME_LATENCY>;
mutable Container mDelayedFences{};
size_t mLast;
uint8_t const mLast;
};
} // namespace filament
+3 -1
View File
@@ -459,10 +459,12 @@ void PerViewUniforms::bind(backend::DriverApi& driver) noexcept {
void PerViewUniforms::unbindSamplers() noexcept {
auto& samplerGroup = mSamplers;
samplerGroup.clearSampler(PerViewSib::SHADOW_MAP);
samplerGroup.clearSampler(PerViewSib::IBL_SPECULAR);
samplerGroup.clearSampler(PerViewSib::SSAO);
samplerGroup.clearSampler(PerViewSib::SSR);
samplerGroup.clearSampler(PerViewSib::STRUCTURE);
samplerGroup.clearSampler(PerViewSib::SHADOW_MAP);
samplerGroup.clearSampler(PerViewSib::FOG);
}
} // namespace filament
+13 -4
View File
@@ -2945,6 +2945,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
1.0f / outputDesc.height});
}
if (blitterNames[index] == "blitLow") {
mi->setParameter("levelOfDetail", 0.0f);
}
mi->setParameter("viewport", float4{
(float)vp.left / inputDesc.width,
(float)vp.bottom / inputDesc.height,
@@ -3003,9 +3007,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
SamplerMagFilter filterMag,
SamplerMinFilter filterMin) noexcept {
// TODO: add support for sub-resources
assert_invariant(fg.getSubResourceDescriptor(input).layer == 0);
assert_invariant(fg.getSubResourceDescriptor(input).level == 0);
uint32_t const layer = fg.getSubResourceDescriptor(input).layer;
float const levelOfDetail = fg.getSubResourceDescriptor(input).level;
struct QuadBlitData {
FrameGraphId<FrameGraphTexture> input;
@@ -3031,7 +3034,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
// --------------------------------------------------------------------------------
// set uniforms
PostProcessMaterial const& material = getPostProcessMaterial("blitLow");
PostProcessMaterial const& material =
getPostProcessMaterial(layer ? "blitArray" : "blitLow");
auto* mi = material.getMaterialInstance(mEngine);
mi->setParameter("color", color, {
.filterMag = filterMag,
@@ -3043,6 +3047,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height
});
mi->setParameter("levelOfDetail", levelOfDetail);
if (layer) {
mi->setParameter("layerIndex", layer);
}
mi->commit(driver);
mi->use(driver);
@@ -3185,6 +3193,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
assert_invariant(dst);
assert_invariant(srcDesc.format == dstDesc.format);
assert_invariant(srcDesc.width == dstDesc.width && srcDesc.height == dstDesc.height);
assert_invariant(srcDesc.samples > 1 && dstDesc.samples <= 1);
driver.resolve(
dst, dstSubDesc.level, dstSubDesc.layer,
src, srcSubDesc.level, srcSubDesc.layer);
+10 -3
View File
@@ -419,7 +419,8 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
UTILS_ALWAYS_INLINE // This function exists only to make the code more readable. we want it inlined.
inline // and we don't need it in the compilation unit
void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
FMaterialInstance const* const UTILS_RESTRICT mi, bool inverseFrontFaces) noexcept {
FMaterialInstance const* const UTILS_RESTRICT mi,
bool inverseFrontFaces, bool hasDepthClamp) noexcept {
FMaterial const * const UTILS_RESTRICT ma = mi->getMaterial();
variant = Variant::filterVariant(variant, ma->isVariantLit());
@@ -460,6 +461,7 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
cmdDraw.info.rasterState.colorWrite = mi->isColorWriteEnabled();
cmdDraw.info.rasterState.depthWrite = mi->isDepthWriteEnabled();
cmdDraw.info.rasterState.depthFunc = mi->getDepthFunc();
cmdDraw.info.rasterState.depthClamp = hasDepthClamp;
cmdDraw.info.materialVariant = variant;
// we keep "RasterState::colorWrite" to the value set by material (could be disabled)
}
@@ -558,6 +560,9 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
bool const hasInstancedStereo =
renderFlags & IS_INSTANCED_STEREOSCOPIC;
bool const hasDepthClamp =
renderFlags & HAS_DEPTH_CLAMP;
float const cameraPositionDotCameraForward = dot(cameraPosition, cameraForward);
auto const* const UTILS_RESTRICT soaWorldAABBCenter = soa.data<FScene::WORLD_AABB_CENTER>();
@@ -577,6 +582,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
cmd.info.rasterState.depthWrite = true;
cmd.info.rasterState.depthFunc = RasterState::DepthFunc::GE;
cmd.info.rasterState.alphaToCoverage = false;
cmd.info.rasterState.depthClamp = hasDepthClamp;
}
for (uint32_t i = range.first; i < range.last; ++i) {
@@ -610,7 +616,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
// Here, objects close to the camera (but behind) will be drawn first.
// An alternative that keeps the mathematical ordering is given here:
// distanceBits ^= ((int32_t(distanceBits) >> 31) | 0x80000000u);
float const distance = -dot(soaWorldAABBCenter[i], cameraForward) - cameraPositionDotCameraForward;
float const distance = -(dot(soaWorldAABBCenter[i], cameraForward) - cameraPositionDotCameraForward);
uint32_t const distanceBits = reinterpret_cast<uint32_t const&>(distance);
// calculate the per-primitive face winding order inversion
@@ -691,7 +697,8 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
cmd.info.morphingOffset = primitive.getMorphingBufferOffset();
if constexpr (isColorPass) {
RenderPass::setupColorCommand(cmd, renderableVariant, mi, inverseFrontFaces);
RenderPass::setupColorCommand(cmd, renderableVariant, mi,
inverseFrontFaces, hasDepthClamp);
const bool blendPass = Pass(cmd.key & PASS_MASK) == Pass::BLENDED;
if (blendPass) {
// TODO: at least for transparent objects, AABB should be per primitive
+5 -2
View File
@@ -284,6 +284,7 @@ public:
static constexpr RenderFlags HAS_SHADOWING = 0x01;
static constexpr RenderFlags HAS_INVERSE_FRONT_FACES = 0x02;
static constexpr RenderFlags IS_INSTANCED_STEREOSCOPIC = 0x04;
static constexpr RenderFlags HAS_DEPTH_CLAMP = 0x08;
// Arena used for commands
using Arena = utils::Arena<
@@ -444,7 +445,7 @@ private:
uint8_t instancedStereoEyeCount) noexcept;
static void setupColorCommand(Command& cmdDraw, Variant variant,
FMaterialInstance const* mi, bool inverseFrontFaces) noexcept;
FMaterialInstance const* mi, bool inverseFrontFaces, bool hasDepthClamp) noexcept;
static void updateSummedPrimitiveCounts(
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> vr) noexcept;
@@ -528,7 +529,9 @@ public:
// like above but allows to set specific flags
RenderPassBuilder& renderFlags(
RenderPass::RenderFlags mask, RenderPass::RenderFlags value) noexcept {
mFlags = (mFlags & mask) | (value & mask);
value &= mask;
mFlags &= ~mask;
mFlags |= value;
return *this;
}
+43 -45
View File
@@ -40,6 +40,7 @@
#include <utils/Panic.h>
#include <algorithm>
#include <optional>
#include <utility>
#include <stddef.h>
@@ -50,8 +51,9 @@ namespace filament {
using namespace backend;
using namespace math;
FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
RendererUtils::ColorPassOutput RendererUtils::colorPass(
FrameGraph& fg, const char* name, FEngine& engine, FView const& view,
ColorPassInput const& colorPassInput,
FrameGraphTexture::Descriptor const& colorBufferDesc,
ColorPassConfig const& config, PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass::Executor passExecutor) noexcept {
@@ -67,31 +69,28 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
FrameGraphId<FrameGraphTexture> structure;
};
Blackboard& blackboard = fg.getBlackboard();
auto& colorPass = fg.addPass<ColorPassData>(name,
[&](FrameGraph::Builder& builder, ColorPassData& data) {
TargetBufferFlags const clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
TargetBufferFlags clearDepthFlags = config.clearFlags & TargetBufferFlags::DEPTH;
TargetBufferFlags clearStencilFlags = config.clearFlags & TargetBufferFlags::STENCIL;
uint8_t layerCount = 1;
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
data.ssao = blackboard.get<FrameGraphTexture>("ssao");
data.color = blackboard.get<FrameGraphTexture>("color");
data.depth = blackboard.get<FrameGraphTexture>("depth");
data.color = colorPassInput.linearColor;
data.depth = colorPassInput.depth;
data.shadows = colorPassInput.shadows;
data.ssao = colorPassInput.ssao;
// Screen-space reflection or refractions
if (config.hasScreenSpaceReflectionsOrRefractions) {
data.ssr = blackboard.get<FrameGraphTexture>("ssr");
data.ssr = colorPassInput.ssr;
if (data.ssr) {
data.ssr = builder.sample(data.ssr);
}
}
if (config.hasContactShadows) {
data.structure = blackboard.get<FrameGraphTexture>("structure");
data.structure = colorPassInput.structure;
assert_invariant(data.structure);
data.structure = builder.sample(data.structure);
}
@@ -154,6 +153,8 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
}
if (colorGradingConfig.asSubpass) {
assert_invariant(config.msaa <= 1);
assert_invariant(colorBufferDesc.samples <= 1);
data.output = builder.createTexture("Tonemapped Buffer", {
.width = colorBufferDesc.width,
.height = colorBufferDesc.height,
@@ -172,9 +173,6 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
data.color = builder.write(data.color, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.depth = builder.write(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
if (view.hasStereo() && engine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW) {
layerCount = engine.getConfig().stereoscopicEyeCount;
}
/*
* There is a bit of magic happening here regarding the viewport used.
@@ -196,9 +194,8 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
.stencil = data.stencil },
.clearColor = config.clearColor,
.samples = config.msaa,
.layerCount = layerCount,
.layerCount = static_cast<uint8_t>(colorBufferDesc.depth),
.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags});
blackboard["depth"] = data.depth;
},
[=, passExecutor = std::move(passExecutor), &view, &engine](FrameGraphResources const& resources,
ColorPassData const& data, DriverApi& driver) {
@@ -264,24 +261,21 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
}
);
// when color grading is done as a subpass, the output of the color-pass is the ldr buffer
auto output = colorGradingConfig.asSubpass ? colorPass->output : colorPass->color;
blackboard["color"] = output;
return output;
return {
.linearColor = colorPass->color,
.tonemappedColor = colorPass->output, // can be null
.depth = colorPass->depth
};
}
std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
std::optional<RendererUtils::ColorPassOutput> RendererUtils::refractionPass(
FrameGraph& fg, FEngine& engine, FView const& view,
ColorPassInput colorPassInput,
ColorPassConfig config,
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass const& pass) noexcept {
auto& blackboard = fg.getBlackboard();
auto input = blackboard.get<FrameGraphTexture>("color");
FrameGraphId<FrameGraphTexture> output;
// find the first refractive object in channel 2
RenderPass::Command const* const refraction = std::partition_point(pass.begin(), pass.end(),
[](auto const& command) {
@@ -296,34 +290,35 @@ std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
// if there wasn't any refractive object, just skip everything below.
if (UTILS_UNLIKELY(hasScreenSpaceRefraction)) {
PostProcessManager& ppm = engine.getPostProcessManager();
// clear the color/depth buffers, which will orphan (and cull) the color pass
input.clear();
blackboard.remove("color");
blackboard.remove("depth");
assert_invariant(!colorPassInput.linearColor);
assert_invariant(!colorPassInput.depth);
config.hasScreenSpaceReflectionsOrRefractions = true;
input = RendererUtils::colorPass(fg, "Color Pass (opaque)", engine, view, {
PostProcessManager& ppm = engine.getPostProcessManager();
auto const opaquePassOutput = RendererUtils::colorPass(fg,
"Color Pass (opaque)", engine, view, colorPassInput, {
// When rendering the opaques, we need to conserve the sample buffer,
// so create a config that specifies the sample count.
.width = config.physicalViewport.width,
.height = config.physicalViewport.height,
.samples = config.msaa,
.format = config.hdrFormat
}, config, { .asSubpass = false },
},
config, { .asSubpass = false, .customResolve = false },
pass.getExecutor(pass.begin(), refraction));
// generate the mipmap chain
PostProcessManager::generateMipmapSSR(ppm, fg,
input, ssrConfig.refraction, true, ssrConfig);
opaquePassOutput.linearColor,
ssrConfig.refraction,
true, ssrConfig);
// Now we're doing the refraction pass proper.
// This uses the same framebuffer (color and depth) used by the opaque pass. This happens
// automatically because these are set in the Blackboard (they were set by the opaque
// pass). For this reason, `desc` below is only used in colorPass() for the width and
// height.
// This uses the same framebuffer (color and depth) used by the opaque pass.
// For this reason, the `colorBufferDesc` parameter of colorPass() below is only used for
// the width and height.
colorPassInput.linearColor = opaquePassOutput.linearColor;
colorPassInput.depth = opaquePassOutput.depth;
// Since we're reusing the existing target we don't want to clear any of its buffer.
// Important: if this target ended up being an imported target, then the clearFlags
@@ -331,22 +326,25 @@ std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
// and we'd end up clearing the opaque pass. This scenario never happens because it is
// prevented in Renderer.cpp's final blit.
config.clearFlags = TargetBufferFlags::NONE;
output = RendererUtils::colorPass(fg, "Color Pass (transparent)", engine, view, {
auto transparentPassOutput = RendererUtils::colorPass(fg, "Color Pass (transparent)",
engine, view, colorPassInput, {
.width = config.physicalViewport.width,
.height = config.physicalViewport.height },
config, colorGradingConfig, pass.getExecutor(refraction, pass.end()));
config, colorGradingConfig,
pass.getExecutor(refraction, pass.end()));
if (config.msaa > 1 && !colorGradingConfig.asSubpass) {
// We need to do a resolve here because later passes (such as color grading or DoF) will
// need to sample from 'output'. However, because we have MSAA, we know we're not
// sampleable. And this is because in the SSR case, we had to use a renderbuffer to
// conserve the multi-sample buffer.
output = ppm.resolve(fg, "Resolved Color Buffer", output, { .levels = 1 });
transparentPassOutput.linearColor = ppm.resolve(fg, "Resolved Color Buffer",
transparentPassOutput.linearColor, { .levels = 1 });
}
} else {
output = input;
return transparentPassOutput;
}
return { output, hasScreenSpaceRefraction };
return std::nullopt;
}
UTILS_NOINLINE
+24 -2
View File
@@ -21,14 +21,19 @@
#include "RenderPass.h"
#include "fg/FrameGraphId.h"
#include "fg/FrameGraphTexture.h"
#include <filament/Viewport.h>
#include <backend/DriverEnums.h>
#include <backend/PixelBufferDescriptor.h>
#include <math/vec2.h>
#include <math/vec4.h>
#include <stdint.h>
#include <optional>
#include <utility>
namespace filament {
@@ -71,15 +76,32 @@ public:
bool screenSpaceReflectionHistoryNotReady;
};
static FrameGraphId<FrameGraphTexture> colorPass(
struct ColorPassInput {
FrameGraphId<FrameGraphTexture> linearColor;
FrameGraphId<FrameGraphTexture> tonemappedColor;
FrameGraphId<FrameGraphTexture> depth;
FrameGraphId<FrameGraphTexture> shadows;
FrameGraphId<FrameGraphTexture> ssao;
FrameGraphId<FrameGraphTexture> ssr;
FrameGraphId<FrameGraphTexture> structure;
};
struct ColorPassOutput {
FrameGraphId<FrameGraphTexture> linearColor;
FrameGraphId<FrameGraphTexture> tonemappedColor;
FrameGraphId<FrameGraphTexture> depth;
};
static ColorPassOutput colorPass(
FrameGraph& fg, const char* name, FEngine& engine, FView const& view,
ColorPassInput const& colorPassInput,
FrameGraphTexture::Descriptor const& colorBufferDesc,
ColorPassConfig const& config,
PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass::Executor passExecutor) noexcept;
static std::pair<FrameGraphId<FrameGraphTexture>, bool> refractionPass(
static std::optional<RendererUtils::ColorPassOutput> refractionPass(
FrameGraph& fg, FEngine& engine, FView const& view,
ColorPassInput colorPassInput,
ColorPassConfig config,
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
PostProcessManager::ColorGradingConfig colorGradingConfig,
+8 -5
View File
@@ -143,12 +143,14 @@ void ResourceAllocator::terminate() noexcept {
}
}
RenderTargetHandle ResourceAllocator::createRenderTarget(const char*,
RenderTargetHandle ResourceAllocator::createRenderTarget(const char* name,
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
uint8_t samples, uint8_t layerCount, MRT color, TargetBufferInfo depth,
TargetBufferInfo stencil) noexcept {
return mBackend.createRenderTarget(targetBufferFlags,
auto handle = mBackend.createRenderTarget(targetBufferFlags,
width, height, samples ? samples : 1u, layerCount, color, depth, stencil);
mBackend.setDebugTag(handle.getId(), CString{ name });
return handle;
}
void ResourceAllocator::destroyRenderTarget(RenderTargetHandle h) noexcept {
@@ -170,9 +172,9 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name,
// do we have a suitable texture in the cache?
TextureHandle handle;
TextureKey const key{ name, target, levels, format, samples, width, height, depth, usage, swizzle };
if constexpr (mEnabled) {
auto& textureCache = mTextureCache;
const TextureKey key{ name, target, levels, format, samples, width, height, depth, usage, swizzle };
auto it = textureCache.find(key);
if (UTILS_LIKELY(it != textureCache.end())) {
// we do, move the entry to the in-use list, and remove from the cache
@@ -190,7 +192,6 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name,
swizzle[0], swizzle[1], swizzle[2], swizzle[3]);
}
}
mDisposer->checkout(handle, key);
} else {
if (swizzle == defaultSwizzle) {
handle = mBackend.createTexture(
@@ -201,12 +202,14 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name,
swizzle[0], swizzle[1], swizzle[2], swizzle[3]);
}
}
mDisposer->checkout(handle, key);
mBackend.setDebugTag(handle.getId(), CString{ name });
return handle;
}
void ResourceAllocator::destroyTexture(TextureHandle h) noexcept {
auto const key = mDisposer->checkin(h);
if constexpr (mEnabled) {
auto const key = mDisposer->checkin(h);
if (UTILS_LIKELY(key.has_value())) {
uint32_t const size = key.value().getSize();
mTextureCache.emplace(key.value(), TextureCachePayload{ h, mAge, size });
+6 -6
View File
@@ -829,13 +829,13 @@ ShadowMap::Corners ShadowMap::computeFrustumCorners(
Corners const csViewFrustumCorners = {
.vertices = {
{ -1, -1, far },
{ 1, -1, far },
{ -1, 1, far },
{ 1, 1, far },
{ 1, -1, far },
{ -1, 1, far },
{ 1, 1, far },
{ -1, -1, near },
{ 1, -1, near },
{ -1, 1, near },
{ 1, 1, near },
{ 1, -1, near },
{ -1, 1, near },
{ 1, 1, near },
}
};
+24 -5
View File
@@ -66,15 +66,15 @@ namespace filament {
using namespace backend;
using namespace math;
// do this only if depth-clamp is available
static constexpr bool USE_DEPTH_CLAMP = false;
ShadowMapManager::ShadowMapManager(FEngine& engine) {
ShadowMapManager::ShadowMapManager(FEngine& engine)
: mIsDepthClampSupported(engine.getDriverApi().isDepthClampSupported()) {
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.shadowmap.visualize_cascades",
&engine.debug.shadowmap.visualize_cascades);
debugRegistry.registerProperty("d.shadowmap.disable_light_frustum_align",
&engine.debug.shadowmap.disable_light_frustum_align);
debugRegistry.registerProperty("d.shadowmap.depth_clamp",
&engine.debug.shadowmap.depth_clamp);
}
ShadowMapManager::~ShadowMapManager() {
@@ -367,7 +367,20 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
// generate and sort the commands for rendering the shadow map
RenderPass::RenderFlags renderPassFlags{};
bool const canUseDepthClamp =
shadowMap.getShadowType() == ShadowType::DIRECTIONAL &&
!view.hasVSM() &&
mIsDepthClampSupported &&
engine.debug.shadowmap.depth_clamp;
if (canUseDepthClamp) {
renderPassFlags |= RenderPass::HAS_DEPTH_CLAMP;
}
RenderPass const pass = passBuilder
.renderFlags(RenderPass::HAS_DEPTH_CLAMP, renderPassFlags)
.camera(cameraInfo)
.visibilityMask(entry.visibilityMask)
.geometry(scene->getRenderableData(),
@@ -641,8 +654,14 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
cameraInfo.zf = -nearFarPlanes[i + 1];
updateNearFarPlanes(&cameraInfo.cullingProjection, cameraInfo.zn, cameraInfo.zf);
bool const canUseDepthClamp =
!view.hasVSM() &&
mIsDepthClampSupported &&
engine.debug.shadowmap.depth_clamp;
auto shaderParameters = shadowMap.updateDirectional(engine,
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo, USE_DEPTH_CLAMP);
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo,
canUseDepthClamp);
if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex();
+1
View File
@@ -232,6 +232,7 @@ private:
ShadowMapCacheContainer mShadowMapCache;
uint32_t mDirectionalShadowMapCount = 0;
uint32_t mSpotShadowMapCount = 0;
bool const mIsDepthClampSupported;
bool mInitialized = false;
ShadowMap& getShadowMap(size_t index) noexcept {
+8 -16
View File
@@ -28,12 +28,6 @@
#include <string_view>
#include <utility>
#ifndef NDEBUG
# define DEBUG_PROPERTIES_WRITABLE true
#else
# define DEBUG_PROPERTIES_WRITABLE false
#endif
using namespace filament::math;
using namespace utils;
@@ -79,17 +73,15 @@ bool FDebugRegistry::hasProperty(const char* name) const noexcept {
template<typename T>
bool FDebugRegistry::setProperty(const char* name, T v) noexcept {
if constexpr (DEBUG_PROPERTIES_WRITABLE) {
auto info = getPropertyInfo(name);
T* const addr = static_cast<T*>(info.first);
if (addr) {
auto old = *addr;
*addr = v;
if (info.second && old != v) {
info.second();
}
return true;
auto info = getPropertyInfo(name);
T* const addr = static_cast<T*>(info.first);
if (addr) {
auto old = *addr;
*addr = v;
if (info.second && old != v) {
info.second();
}
return true;
}
return false;
}
+1
View File
@@ -600,6 +600,7 @@ public:
bool focus_shadowcasters = true;
bool visualize_cascades = false;
bool disable_light_frustum_align = false;
bool depth_clamp = true;
float dzn = -1.0f;
float dzf = 1.0f;
float display_shadow_texture_scale = 0.25f;
+18 -10
View File
@@ -192,19 +192,27 @@ Material* Material::Builder::build(Engine& engine) {
return nullptr;
}
// Print a warning if the material's stereo type doesn't align with the engine's setting.
MaterialDomain materialDomain;
UserVariantFilterMask variantFilterMask;
materialParser->getMaterialDomain(&materialDomain);
if (materialDomain == MaterialDomain::SURFACE) {
materialParser->getMaterialVariantFilterMask(&variantFilterMask);
bool const hasStereoVariants = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::STE));
if (materialDomain == MaterialDomain::SURFACE && hasStereoVariants) {
StereoscopicType const engineStereoscopicType = engine.getConfig().stereoscopicType;
StereoscopicType materialStereoscopicType = StereoscopicType::NONE;
materialParser->getStereoscopicType(&materialStereoscopicType);
if (materialStereoscopicType != engineStereoscopicType) {
CString name;
materialParser->getName(&name);
slog.w << "The stereoscopic type in the compiled material '" << name.c_str_safe()
<< "' is " << (int)materialStereoscopicType
<< ", which is not compatiable with the engine's setting "
<< (int)engineStereoscopicType << "." << io::endl;
// Default materials are always compiled with either 'instanced' or 'multiview'.
// So, we only verify compatibility if the engine is set up for stereo.
if (engineStereoscopicType != StereoscopicType::NONE) {
StereoscopicType materialStereoscopicType = StereoscopicType::NONE;
materialParser->getStereoscopicType(&materialStereoscopicType);
if (materialStereoscopicType != engineStereoscopicType) {
CString name;
materialParser->getName(&name);
slog.w << "The stereoscopic type in the compiled material '" << name.c_str_safe()
<< "' is " << (int)materialStereoscopicType
<< ", which is not compatiable with the engine's setting "
<< (int)engineStereoscopicType << "." << io::endl;
}
}
}
+70 -37
View File
@@ -651,20 +651,25 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
const bool isProtectedContent = mSwapChain && mSwapChain->isProtected();
// Conditions to meet to be able to use the sub-pass rendering path. This is regardless of
// whether the backend supports subpasses (or if they are disabled in the debugRegistry).
const bool isSubpassPossible =
msaaSampleCount <= 1 &&
hasColorGrading &&
!bloomOptions.enabled && !dofOptions.enabled && !taaOptions.enabled;
// asSubpass is disabled with TAA (although it's supported) because performance was degraded
// on qualcomm hardware -- we might need a backend dependent toggle at some point
const PostProcessManager::ColorGradingConfig colorGradingConfig{
.asSubpass =
hasColorGrading &&
msaaSampleCount <= 1 &&
!bloomOptions.enabled && !dofOptions.enabled && !taaOptions.enabled &&
isSubpassPossible &&
driver.isFrameBufferFetchSupported() &&
!engine.debug.renderer.disable_subpasses,
.customResolve =
msaaOptions.customResolve &&
msaaSampleCount > 1 &&
hasColorGrading &&
driver.isFrameBufferFetchMultiSampleSupported() &&
msaaOptions.customResolve &&
hasColorGrading &&
!engine.debug.renderer.disable_subpasses,
.translucent = needsAlphaChannel,
.fxaa = hasFXAA,
@@ -673,6 +678,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
TextureFormat::RGBA8 : getLdrFormat(needsAlphaChannel)
};
// by construction (msaaSampleCount) both asSubpass and customResolve can't be true
assert_invariant(colorGradingConfig.asSubpass + colorGradingConfig.customResolve < 2);
// whether we're scaled at all
bool scaled = any(notEqual(scale, float2(1.0f)));
@@ -695,13 +703,12 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
CameraInfo cameraInfo = view.computeCameraInfo(engine);
// when colorgrading-as-subpass is active, we know that many other effects are disabled
// such as dof, bloom. Moreover, if fxaa and scaling are not enabled, we're essentially in
// a very fast rendering path -- in this case, we would need an extra blit to "resolve" the
// buffer padding (because there are no other pass that can do it as a side effect).
// In this case, it is better to skip the padding, which won't be helping much.
const bool noBufferPadding = (colorGradingConfig.asSubpass && !hasFXAA && !scaled)
|| engine.debug.renderer.disable_buffer_padding;
// If fxaa and scaling are not enabled, we're essentially in a very fast rendering path -- in
// this case, we would need an extra blit to "resolve" the buffer padding (because there are no
// other pass that can do it as a side effect). In this case, it is better to skip the padding,
// which won't be helping much.
const bool noBufferPadding = (isSubpassPossible &&
!hasFXAA && !scaled) || engine.debug.renderer.disable_buffer_padding;
// guardBand must be a multiple of 16 to guarantee the same exact rendering up to 4 mip levels.
float const guardBand = guardBandOptions.enabled ? 16.0f : 0.0f;
@@ -816,14 +823,14 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
blackboard["shadows"] = shadows;
}
// When we don't have a custom RenderTarget, currentRenderTarget below is nullptr and is
// When we don't have a custom RenderTarget, customRenderTarget below is nullptr and is
// recorded in the list of targets already rendered into -- this ensures that
// initializeClearFlags() is called only once for the default RenderTarget.
auto& previousRenderTargets = mPreviousRenderTargets;
FRenderTarget* const currentRenderTarget = downcast(view.getRenderTarget());
FRenderTarget* const customRenderTarget = downcast(view.getRenderTarget());
if (UTILS_LIKELY(
previousRenderTargets.find(currentRenderTarget) == previousRenderTargets.end())) {
previousRenderTargets.insert(currentRenderTarget);
previousRenderTargets.find(customRenderTarget) == previousRenderTargets.end())) {
previousRenderTargets.insert(customRenderTarget);
initializeClearFlags();
}
@@ -840,10 +847,10 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
const TargetBufferFlags keepOverrideStartFlags = TargetBufferFlags::ALL & ~discardStartFlags;
TargetBufferFlags keepOverrideEndFlags = TargetBufferFlags::NONE;
if (currentRenderTarget) {
if (customRenderTarget) {
// For custom RenderTarget, we look at each attachment flag and if they have their
// SAMPLEABLE usage bit set, we assume they must not be discarded after the render pass.
keepOverrideEndFlags |= currentRenderTarget->getSampleableAttachmentsMask();
keepOverrideEndFlags |= customRenderTarget->getSampleableAttachmentsMask();
}
// Renderer's ClearOptions apply once at the beginning of the frame (not for each View),
@@ -952,7 +959,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
.scale = aoOptions.resolution,
.picking = view.hasPicking()
});
blackboard["structure"] = structure;
const auto picking = picking_;
@@ -1000,7 +1006,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
ssReflectionsOptions.enabled ? TextureFormat::RGBA16F : TextureFormat::R11F_G11F_B10F,
view.getCameraUser().getFieldOfView(Camera::Fov::VERTICAL), config.scale);
config.ssrLodOffset = ssrConfig.lodOffset;
blackboard["ssr"] = ssrConfig.ssr;
// --------------------------------------------------------------------------------------------
// screen-space reflections pass
@@ -1014,6 +1019,10 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
{ .width = svp.width, .height = svp.height });
if (UTILS_LIKELY(reflections)) {
fg.addTrivialSideEffectPass("SSR Cleanup", [&view](DriverApi& driver) {
view.getPerViewUniforms().prepareStructure({});
view.commitUniforms(driver);
});
// generate the mipchain
PostProcessManager::generateMipmapSSR(ppm, fg,
reflections, ssrConfig.reflection, false, ssrConfig);
@@ -1104,24 +1113,45 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
);
// the color pass itself + color-grading as subpass if needed
auto colorPassOutput = RendererUtils::colorPass(fg, "Color Pass", mEngine, view,
auto colorPassOutput = RendererUtils::colorPass(fg, "Color Pass", mEngine, view, {
.shadows = blackboard.get<FrameGraphTexture>("shadows"),
.ssao = blackboard.get<FrameGraphTexture>("ssao"),
.ssr = ssrConfig.ssr,
.structure = structure
},
colorBufferDesc, config, colorGradingConfigForColor, pass.getExecutor());
if (view.isScreenSpaceRefractionEnabled() && !pass.empty()) {
// this cancels the colorPass() call above if refraction is active.
// the color pass + refraction + color-grading as subpass if needed
const auto [output, enabled] = RendererUtils::refractionPass(fg, mEngine, view,
// This cancels the colorPass() call above if refraction is active.
// The color pass + refraction + color-grading as subpass if needed
auto const output = RendererUtils::refractionPass(fg, mEngine, view, {
.shadows = blackboard.get<FrameGraphTexture>("shadows"),
.ssao = blackboard.get<FrameGraphTexture>("ssao"),
.ssr = ssrConfig.ssr,
.structure = structure
},
config, ssrConfig, colorGradingConfigForColor, pass);
colorPassOutput = output;
hasScreenSpaceRefraction = enabled;
hasScreenSpaceRefraction = output.has_value();
if (hasScreenSpaceRefraction) {
colorPassOutput = output.value();
}
}
fg.addTrivialSideEffectPass("Finish Color Passes", [&view](DriverApi& driver) {
// Unbind SSAO sampler, b/c the FrameGraph will delete the texture at the end of the pass.
view.cleanupRenderPasses();
view.commitUniforms(driver);
});
if (colorGradingConfig.customResolve) {
assert_invariant(fg.getDescriptor(colorPassOutput.linearColor).samples <= 1);
// TODO: we have to "uncompress" (i.e. detonemap) the color buffer here because it's used
// by many other passes (Bloom, TAA, DoF, etc...). We could make this more
// efficient by using ARM_shader_framebuffer_fetch. We use a load/store (i.e.
// subpass) here because it's more convenient.
colorPassOutput = ppm.customResolveUncompressPass(fg, colorPassOutput);
colorPassOutput.linearColor =
ppm.customResolveUncompressPass(fg, colorPassOutput.linearColor);
}
// export the color buffer if screen-space reflections are enabled
@@ -1137,7 +1167,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// The "output" of this pass is going to be used during the next frame as
// an "import".
builder.sideEffect();
data.history = builder.sample(colorPassOutput); // FIXME: an access must be declared for detach(), why?
// we can't use colorPassOutput here because it could be tonemapped
data.history = builder.sample(colorPassOutput.linearColor); // FIXME: an access must be declared for detach(), why?
}, [&view, projection](FrameGraphResources const& resources, auto const& data,
backend::DriverApi&) {
auto& history = view.getFrameHistory();
@@ -1147,19 +1179,20 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
});
}
FrameGraphId<FrameGraphTexture> input = colorPassOutput;
fg.addTrivialSideEffectPass("Finish Color Passes", [&view](DriverApi& driver) {
// Unbind SSAO sampler, b/c the FrameGraph will delete the texture at the end of the pass.
view.cleanupRenderPasses();
view.commitUniforms(driver);
});
// this is the output of the color pass / input to post processing,
// this is only used later for comparing it with the output after post-processing
FrameGraphId<FrameGraphTexture> const postProcessInput = colorGradingConfig.asSubpass ?
colorPassOutput.tonemappedColor :
colorPassOutput.linearColor;
// input can change below
FrameGraphId<FrameGraphTexture> input = postProcessInput;
// Resolve depth -- which might be needed because of TAA or DoF. This pass will be culled
// if the depth is not used below or if the depth is not MS (e.g. it could have been
// auto-resolved).
// In practice, this is used on Vulkan and older Metal devices.
auto depth = blackboard.get<FrameGraphTexture>("depth");
depth = ppm.resolve(fg, "Resolved Depth Buffer", depth, { .levels = 1 });
auto depth = ppm.resolve(fg, "Resolved Depth Buffer", colorPassOutput.depth, { .levels = 1 });
// Debug: CSM visualisation
if (UTILS_UNLIKELY(engine.debug.shadowmap.visualize_cascades &&
@@ -1275,7 +1308,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// The intermediate buffer is accomplished with a "fake" opaqueBlit (i.e. blit) operation.
const bool outputIsSwapChain =
(input == colorPassOutput) && (viewRenderTarget == mRenderTargetHandle);
(input == postProcessInput) && (viewRenderTarget == mRenderTargetHandle);
if (mightNeedFinalBlit) {
if (blendModeTranslucent ||
xvp != svp ||
+2
View File
@@ -245,6 +245,8 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) {
}
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mHandle.getId(), std::move(name));
} else {
driver.setDebugTag(mHandle.getId(), CString{"FTexture"});
}
}
+4 -8
View File
@@ -1040,10 +1040,8 @@ void FView::commitFrameHistory(FEngine& engine) noexcept {
auto& frameHistory = mFrameHistory;
FrameHistoryEntry& last = frameHistory.back();
disposer.destroy(last.taa.color.handle);
disposer.destroy(last.ssr.color.handle);
last.taa.color.handle.clear();
last.ssr.color.handle.clear();
disposer.destroy(std::move(last.taa.color.handle));
disposer.destroy(std::move(last.ssr.color.handle));
// and then push the new history entry to the history stack
frameHistory.commit();
@@ -1055,10 +1053,8 @@ void FView::clearFrameHistory(FEngine& engine) noexcept {
auto& frameHistory = mFrameHistory;
for (size_t i = 0; i < frameHistory.size(); ++i) {
FrameHistoryEntry& last = frameHistory[i];
disposer.destroy(last.taa.color.handle);
disposer.destroy(last.ssr.color.handle);
last.taa.color.handle.clear();
last.ssr.color.handle.clear();
disposer.destroy(std::move(last.taa.color.handle));
disposer.destroy(std::move(last.ssr.color.handle));
}
}
+3 -3
View File
@@ -190,10 +190,10 @@ void RenderPassNode::resolve() noexcept {
minHeight = std::min(minHeight, h);
maxHeight = std::max(maxHeight, h);
}
// additionally, clear implies discardStart
rt.backend.params.flags.discardStart |= (
rt.descriptor.clearFlags & rt.targetBufferFlags);
}
// additionally, clear implies discardStart
rt.backend.params.flags.discardStart |= (
rt.descriptor.clearFlags & rt.targetBufferFlags);
assert_invariant(minWidth == maxWidth);
assert_invariant(minHeight == maxHeight);
+5 -1
View File
@@ -10,6 +10,10 @@ material {
type : int,
name : layerIndex
},
{
type : float,
name : levelOfDetail,
},
{
type : float4,
name : viewport,
@@ -33,6 +37,6 @@ vertex {
fragment {
void postProcess(inout PostProcessInputs postProcess) {
postProcess.color = textureLod(materialParams_color, vec3(variable_vertex.xy, materialParams.layerIndex), 0.0);
postProcess.color = textureLod(materialParams_color, vec3(variable_vertex.xy, materialParams.layerIndex), materialParams.levelOfDetail);
}
}
+5 -1
View File
@@ -10,6 +10,10 @@ material {
type : float4,
name : viewport,
precision: high
},
{
type : float,
name : levelOfDetail,
}
],
variables : [
@@ -33,7 +37,7 @@ fragment {
#if FILAMENT_EFFECTIVE_VERSION == 100
postProcess.color = texture2D(materialParams_color, variable_vertex.xy);
#else
postProcess.color = textureLod(materialParams_color, variable_vertex.xy, 0.0);
postProcess.color = textureLod(materialParams_color, variable_vertex.xy, materialParams.levelOfDetail);
#endif
}
}
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.54.1"
spec.version = "1.54.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.54.1/filament-v1.54.1-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.54.3/filament-v1.54.3-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
+10 -1
View File
@@ -323,6 +323,9 @@ public:
//! Custom variables (all float4).
MaterialBuilder& variable(Variable v, const char* name) noexcept;
MaterialBuilder& variable(Variable v, const char* name,
ParameterPrecision precision) noexcept;
/**
* Require a specified attribute.
*
@@ -708,11 +711,17 @@ public:
ShaderStage stage;
};
struct CustomVariable {
utils::CString name;
Precision precision = Precision::DEFAULT;
bool hasPrecision = false;
};
static constexpr size_t MATERIAL_PROPERTIES_COUNT = filament::MATERIAL_PROPERTIES_COUNT;
using Property = filament::Property;
using PropertyList = bool[MATERIAL_PROPERTIES_COUNT];
using VariableList = utils::CString[MATERIAL_VARIABLES_COUNT];
using VariableList = CustomVariable[MATERIAL_VARIABLES_COUNT];
using OutputList = std::vector<Output>;
static constexpr size_t MAX_COLOR_OUTPUT = filament::backend::MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT;
+16 -2
View File
@@ -234,7 +234,21 @@ MaterialBuilder& MaterialBuilder::variable(Variable v, const char* name) noexcep
case Variable::CUSTOM2:
case Variable::CUSTOM3:
assert(size_t(v) < MATERIAL_VARIABLES_COUNT);
mVariables[size_t(v)] = CString(name);
mVariables[size_t(v)] = { CString(name), Precision::DEFAULT, false };
break;
}
return *this;
}
MaterialBuilder& MaterialBuilder::variable(Variable v,
const char* name, ParameterPrecision precision) noexcept {
switch (v) {
case Variable::CUSTOM0:
case Variable::CUSTOM1:
case Variable::CUSTOM2:
case Variable::CUSTOM3:
assert(size_t(v) < MATERIAL_VARIABLES_COUNT);
mVariables[size_t(v)] = { CString(name), precision, true };
break;
}
return *this;
@@ -1383,7 +1397,7 @@ bool MaterialBuilder::checkMaterialLevelFeatures(MaterialInfo const& info) const
bool MaterialBuilder::hasCustomVaryings() const noexcept {
for (const auto& variable : mVariables) {
if (!variable.empty()) {
if (!variable.name.empty()) {
return true;
}
}
+9 -4
View File
@@ -451,15 +451,20 @@ io::sstream& CodeGenerator::generatePostProcessMain(io::sstream& out, ShaderStag
}
io::sstream& CodeGenerator::generateVariable(io::sstream& out, ShaderStage stage,
const CString& name, size_t index) {
const MaterialBuilder::CustomVariable& variable, size_t index) {
auto const& name = variable.name;
const char* precisionString = getPrecisionQualifier(variable.precision);
if (!name.empty()) {
if (stage == ShaderStage::VERTEX) {
out << "\n#define VARIABLE_CUSTOM" << index << " " << name.c_str() << "\n";
out << "\n#define VARIABLE_CUSTOM_AT" << index << " variable_" << name.c_str() << "\n";
out << "LAYOUT_LOCATION(" << index << ") VARYING vec4 variable_" << name.c_str() << ";\n";
out << "LAYOUT_LOCATION(" << index << ") VARYING " << precisionString << " vec4 variable_" << name.c_str() << ";\n";
} else if (stage == ShaderStage::FRAGMENT) {
out << "\nLAYOUT_LOCATION(" << index << ") VARYING highp vec4 variable_" << name.c_str() << ";\n";
if (!variable.hasPrecision && variable.precision == Precision::DEFAULT) {
// for backward compatibility
precisionString = "highp";
}
out << "\nLAYOUT_LOCATION(" << index << ") VARYING " << precisionString << " vec4 variable_" << name.c_str() << ";\n";
}
}
return out;
+1 -1
View File
@@ -103,7 +103,7 @@ public:
// generate declarations for custom interpolants
static utils::io::sstream& generateVariable(utils::io::sstream& out, ShaderStage stage,
const utils::CString& name, size_t index);
const MaterialBuilder::CustomVariable& variable, size_t index);
// generate declarations for non-custom "in" variables
utils::io::sstream& generateShaderInputs(utils::io::sstream& out, ShaderStage type,
+7 -1
View File
@@ -56,7 +56,13 @@ bool operator==(const MaterialKey& k1, const MaterialKey& k2) {
(k1.volumeThicknessUV == k2.volumeThicknessUV) &&
(k1.hasSheen == k2.hasSheen) &&
(k1.hasIOR == k2.hasIOR) &&
(k1.hasVolume == k2.hasVolume);
(k1.hasVolume == k2.hasVolume) &&
(k1.hasSpecular == k2.hasSpecular) &&
(k1.hasSpecularTexture == k2.hasSpecularTexture) &&
(k1.hasSpecularColorTexture == k2.hasSpecularColorTexture) &&
(k1.specularTextureUV == k2.specularTextureUV) &&
(k1.specularColorTextureUV == k2.specularColorTextureUV)
;
}
// Filament supports up to 2 UV sets. glTF has arbitrary texcoord set indices, but it allows
+28 -2
View File
@@ -259,6 +259,24 @@ MaterialInstance* UbershaderProvider::createMaterialInstance(MaterialKey* config
mi->setParameter("occlusionUvMatrix", identity);
mi->setParameter("emissiveUvMatrix", identity);
// set to -1 all possibly optional parameters
for (auto const* param : {
"clearCoatIndex",
"clearCoatRoughnessIndex",
"clearCoatNormalIndex",
"sheenColorIndex",
"sheenRoughnessIndex",
"volumeThicknessUvMatrix",
"volumeThicknessIndex",
"transmissionIndex",
"specularIndex",
"specularColorIndex",
}) {
if (material->hasParameter(param)) {
mi->setParameter(param, -1);
}
}
if (config->hasClearCoat) {
mi->setParameter("clearCoatIndex",
getUvIndex(config->clearCoatUV, config->hasClearCoatTexture));
@@ -340,10 +358,10 @@ MaterialInstance* UbershaderProvider::createMaterialInstance(MaterialKey* config
mi->setParameter("sheenRoughnessMap", mDummyTexture, sampler);
}
if (mi->getMaterial()->hasParameter("ior")) {
if (material->hasParameter("ior")) {
mi->setParameter("ior", 1.5f);
}
if (mi->getMaterial()->hasParameter("reflectance")) {
if (material->hasParameter("reflectance")) {
mi->setParameter("reflectance", 0.5f);
}
@@ -355,6 +373,14 @@ MaterialInstance* UbershaderProvider::createMaterialInstance(MaterialKey* config
mi->setParameter("specularColorMap", mDummyTexture, sampler);
}
if (material->hasParameter("specularColorFactor")) {
mi->setParameter("specularColorFactor", float3(1.0f));
}
if (material->hasParameter("specularStrength")) {
mi->setParameter("specularStrength", 1.0f);
}
return mi;
}
@@ -527,11 +527,13 @@ bool AssetLoaderExtended::createPrimitive(Input* input, Output* out,
if (!mCgltfBuffersLoaded) {
mCgltfBuffersLoaded = utility::loadCgltfBuffers(gltf, mGltfPath.c_str(), mUriDataCache);
if (!mCgltfBuffersLoaded) return false;
if (!mCgltfBuffersLoaded) {
return false;
}
utility::decodeMeshoptCompression(gltf);
}
utility::decodeDracoMeshes(gltf, prim, input->dracoCache);
utility::decodeMeshoptCompression(gltf);
auto slots = computeGeometries(prim, jobType, attributesMap, morphTargets, out->uvmap, mEngine);
@@ -224,6 +224,9 @@ public:
*/
static void exportSettings(const Settings& settings, const char* filename);
static void exportScreenshot(View* view, Renderer* renderer, std::string filename,
bool autoclose, AutomationEngine* automationEngine);
Options getOptions() const { return mOptions; }
bool isRunning() const { return mIsRunning; }
size_t currentTest() const { return mCurrentTest; }
+3 -2
View File
@@ -56,7 +56,7 @@ static void convertRGBAtoRGB(void* buffer, uint32_t width, uint32_t height) {
}
}
static void exportScreenshot(View* view, Renderer* renderer, std::string filename,
void AutomationEngine::exportScreenshot(View* view, Renderer* renderer, std::string filename,
bool autoclose, AutomationEngine* automationEngine) {
const Viewport& vp = view->getViewport();
const size_t byteCount = vp.width * vp.height * 4;
@@ -244,7 +244,8 @@ void AutomationEngine::tick(Engine* engine, const ViewerContent& content, float
}
if (mOptions.exportScreenshots) {
exportScreenshot(content.view, content.renderer, prefix + ".ppm", isLastTest, this);
AutomationEngine::exportScreenshot(
content.view, content.renderer, prefix + ".ppm", isLastTest, this);
}
if (isLastTest) {
+23 -4
View File
@@ -61,8 +61,10 @@
#include <algorithm>
#include <array>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>
#include <string>
#include "generated/resources/gltf_demo.h"
@@ -137,6 +139,8 @@ struct App {
AutomationSpec* automationSpec = nullptr;
AutomationEngine* automationEngine = nullptr;
bool screenshot = false;
uint8_t screenshotSeq = 0;
};
static const char* DEFAULT_IBL = "assets/ibl/lightroom_14b";
@@ -877,10 +881,15 @@ int main(int argc, char** argv) {
if (ImGui::CollapsingHeader("Debug")) {
auto& debug = engine->getDebugRegistry();
if (ImGui::Button("Capture frame")) {
bool* captureFrame =
debug.getPropertyAddress<bool>("d.renderer.doFrameCapture");
*captureFrame = true;
if (engine->getBackend() == Engine::Backend::METAL) {
if (ImGui::Button("Capture frame")) {
bool* captureFrame =
debug.getPropertyAddress<bool>("d.renderer.doFrameCapture");
*captureFrame = true;
}
}
if (ImGui::Button("Screenshot")) {
app.screenshot = true;
}
ImGui::Checkbox("Disable buffer padding",
debug.getPropertyAddress<bool>("d.renderer.disable_buffer_padding"));
@@ -896,6 +905,8 @@ int main(int argc, char** argv) {
debug.getPropertyAddress<bool>("d.shadowmap.focus_shadowcasters"));
ImGui::Checkbox("Disable light frustum alignment",
debug.getPropertyAddress<bool>("d.shadowmap.disable_light_frustum_align"));
ImGui::Checkbox("Depth clamp",
debug.getPropertyAddress<bool>("d.shadowmap.depth_clamp"));
bool debugDirectionalShadowmap;
if (debug.getProperty("d.shadowmap.debug_directional_shadowmap",
@@ -1136,6 +1147,14 @@ int main(int argc, char** argv) {
};
auto postRender = [&app](Engine* engine, View* view, Scene*, Renderer* renderer) {
if (app.screenshot) {
std::ostringstream stringStream;
stringStream << "screenshot" << std::setfill('0') << std::setw(2) << +app.screenshotSeq;
AutomationEngine::exportScreenshot(
view, renderer, stringStream.str() + ".ppm", false, app.automationEngine);
++app.screenshotSeq;
app.screenshot = false;
}
if (app.automationEngine->shouldClose()) {
FilamentApp::get().close();
return;
+40 -3
View File
@@ -615,14 +615,51 @@ static bool processVariables(MaterialBuilder& builder, const JsonishValue& value
}
for (size_t i = 0; i < elements.size(); i++) {
auto elementValue = elements[i];
ParameterPrecision precision = ParameterPrecision::DEFAULT;
MaterialBuilder::Variable v = intToVariable(i);
if (elementValue->getType() != JsonishValue::Type::STRING) {
std::string nameString;
auto elementValue = elements[i];
if (elementValue->getType() == JsonishValue::Type::OBJECT) {
JsonishObject const& jsonObject = *elementValue->toJsonObject();
const JsonishValue* nameValue = jsonObject.getValue("name");
if (!nameValue) {
std::cerr << "variables: entry without 'name' key." << std::endl;
return false;
}
if (nameValue->getType() != JsonishValue::STRING) {
std::cerr << "variables: name value must be STRING." << std::endl;
return false;
}
const JsonishValue* precisionValue = jsonObject.getValue("precision");
if (precisionValue) {
if (precisionValue->getType() != JsonishValue::STRING) {
std::cerr << "variables: precision must be a STRING." << std::endl;
return false;
}
auto precisionString = precisionValue->toJsonString();
if (!Enums::isValid<ParameterPrecision>(precisionString->getString())){
return logEnumIssue("variables", *precisionString, Enums::map<ParameterPrecision>());
}
}
nameString = nameValue->toJsonString()->getString();
if (precisionValue) {
precision = Enums::toEnum<ParameterPrecision>(
precisionValue->toJsonString()->getString());
}
builder.variable(v, nameString.c_str(), precision);
} else if (elementValue->getType() == JsonishValue::Type::STRING) {
nameString = elementValue->toJsonString()->getString();
builder.variable(v, nameString.c_str());
} else {
std::cerr << "variables: array index " << i << " is not a STRING. found:" <<
JsonishValue::typeToString(elementValue->getType()) << std::endl;
return false;
}
builder.variable(v, elementValue->toJsonString()->getString().c_str());
}
return true;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.54.1",
"version": "1.54.3",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",