Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69fc1f302c |
@@ -1,17 +0,0 @@
|
||||
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
|
||||
@@ -8,35 +8,32 @@ on:
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
build-android-armv7:
|
||||
name: build-android-armv7
|
||||
build-android:
|
||||
name: build-android
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- name: Run Android Continuous
|
||||
uses: ./.github/actions/android-continuous
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
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
|
||||
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
|
||||
with:
|
||||
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
|
||||
name: filament-android
|
||||
path: out/filament-android-release.aar
|
||||
- uses: actions/upload-artifact@v1.0.0
|
||||
with:
|
||||
build-abi: x86_64
|
||||
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
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/ios && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v1.0.0
|
||||
with:
|
||||
name: filament-ios
|
||||
path: out/filament-release-ios.tgz
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/linux && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v1.0.0
|
||||
with:
|
||||
name: filament-linux
|
||||
path: out/filament-release-linux.tgz
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/mac && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v1.0.0
|
||||
with:
|
||||
name: filament-mac
|
||||
path: out/filament-release-darwin.tgz
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-14-xlarge, ubuntu-22.04-16core]
|
||||
os: [macos-14, ubuntu-22.04]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
|
||||
build-windows:
|
||||
name: build-windows
|
||||
runs-on: windows-2019-32core
|
||||
runs-on: windows-2019
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
build-android:
|
||||
name: build-android
|
||||
runs-on: ubuntu-22.04-16core
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
@@ -49,14 +49,12 @@ 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 arm64-v8a
|
||||
cd build/android && printf "y" | ./build.sh presubmit
|
||||
|
||||
build-ios:
|
||||
name: build-iOS
|
||||
runs-on: macos-14-xlarge
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
@@ -69,7 +67,7 @@ jobs:
|
||||
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: ubuntu-22.04-16core
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
|
||||
@@ -120,7 +120,7 @@ jobs:
|
||||
env:
|
||||
TAG: ${{ steps.git_ref.outputs.tag }}
|
||||
run: |
|
||||
cd build/android && printf "y" | ./build.sh release armeabi-v7a,arm64-v8a,x86,x86_64
|
||||
cd build/android && printf "y" | ./build.sh release
|
||||
cd ../..
|
||||
mv out/filament-android-release.aar out/filament-${TAG}-android.aar
|
||||
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/web && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v1.0.0
|
||||
with:
|
||||
name: filament-web
|
||||
path: out/filament-release-web.tgz
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
build-windows:
|
||||
name: build-windows
|
||||
runs-on: windows-2019-32core
|
||||
runs-on: windows-2019
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
run: |
|
||||
build\windows\build-github.bat continuous
|
||||
shell: cmd
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v1.0.0
|
||||
with:
|
||||
name: filament-windows
|
||||
path: out/filament-windows.tgz
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.54.5'
|
||||
implementation 'com.google.android.filament:filament-android:1.53.2'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,9 +51,19 @@ 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.5'
|
||||
pod 'Filament', '~> 1.53.2'
|
||||
```
|
||||
|
||||
### 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,36 +7,6 @@ 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.5
|
||||
|
||||
|
||||
## v1.54.4
|
||||
|
||||
- Add support for multi-layered render target with array textures.
|
||||
|
||||
## v1.54.3
|
||||
|
||||
|
||||
## v1.54.2
|
||||
|
||||
- Add a `name` API to Filament objects for debugging handle use-after-free assertions
|
||||
|
||||
## v1.54.1
|
||||
|
||||
|
||||
## v1.54.0
|
||||
|
||||
- materials: add a new `stereoscopicType` material parameter. [⚠️ **New Material Version**]
|
||||
- Fix a crash when compiling shaders on IMG devices
|
||||
|
||||
## v1.53.5
|
||||
|
||||
- engine: Fix bug causing certain sampler parameters to not be applied correctly in GLES 2.0 and on
|
||||
certain GLES 3.0 drivers.
|
||||
|
||||
## v1.53.4
|
||||
|
||||
|
||||
## v1.53.3
|
||||
|
||||
- Add drag and drop support for IBL files for desktop gltf_viewer.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.54.5
|
||||
VERSION_NAME=1.53.2
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
+7
-15
@@ -60,7 +60,13 @@ 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=
|
||||
@@ -68,19 +74,5 @@ 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
|
||||
build_android $2
|
||||
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION} ./build.sh -p android $ANDROID_ABIS -c $BUILD_SAMPLES $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
if [ `uname` == "Linux" ];then
|
||||
source `dirname $0`/../linux/ci-common.sh
|
||||
curl -OL https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-linux.zip
|
||||
unzip -q ninja-linux.zip
|
||||
elif [ `uname` == "Darwin" ];then
|
||||
curl -OL https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip
|
||||
unzip -q ninja-mac.zip
|
||||
@@ -12,6 +13,9 @@ fi
|
||||
chmod +x ninja
|
||||
export PATH="$PWD:$PATH"
|
||||
|
||||
# FIXME: kokoro machines have node and npm but currently they are symlinked to non-existent files
|
||||
# npm install -g typescript
|
||||
|
||||
# Install emscripten.
|
||||
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.15.zip > emsdk.zip
|
||||
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
|
||||
|
||||
+2
-11
@@ -1308,12 +1308,7 @@ 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. 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`).
|
||||
interpolant is of type `float4` (`vec4`) in the shaders.
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
|
||||
material {
|
||||
@@ -1325,11 +1320,7 @@ material {
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
eyeDirection,
|
||||
{
|
||||
name : eyeColor,
|
||||
precision : medium
|
||||
}
|
||||
eyeDirection
|
||||
],
|
||||
vertexDomain : device,
|
||||
depthWrite : false,
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -61,7 +61,6 @@ set(SRCS
|
||||
src/Engine.cpp
|
||||
src/Exposure.cpp
|
||||
src/Fence.cpp
|
||||
src/FilamentBuilder.cpp
|
||||
src/FrameInfo.cpp
|
||||
src/FrameSkipper.cpp
|
||||
src/Froxelizer.cpp
|
||||
|
||||
@@ -1058,7 +1058,7 @@ struct RasterState {
|
||||
bool inverseFrontFaces : 1; // 31
|
||||
|
||||
//! padding, must be 0
|
||||
bool depthClamp : 1; // 32
|
||||
uint8_t padding : 1; // 32
|
||||
};
|
||||
uint32_t u = 0;
|
||||
};
|
||||
@@ -1244,7 +1244,7 @@ enum class Workaround : uint16_t {
|
||||
ADRENO_UNIFORM_ARRAY_CRASH,
|
||||
// Workaround a Metal pipeline compilation error with the message:
|
||||
// "Could not statically determine the target of a texture". See light_indirect.fs
|
||||
METAL_STATIC_TEXTURE_TARGET_ERROR,
|
||||
A8X_STATIC_TEXTURE_TARGET_ERROR,
|
||||
// Adreno drivers sometimes aren't able to blit into a layer of a texture array.
|
||||
DISABLE_BLIT_INTO_TEXTURE_ARRAY,
|
||||
// Multiple workarounds needed for PowerVR GPUs
|
||||
|
||||
@@ -162,10 +162,6 @@ DECL_DRIVER_API_0(finish)
|
||||
// reset state tracking, if the driver does any state tracking (e.g. GL)
|
||||
DECL_DRIVER_API_0(resetState)
|
||||
|
||||
DECL_DRIVER_API_N(setDebugTag,
|
||||
backend::HandleBase::HandleId, handleId,
|
||||
utils::CString, tag)
|
||||
|
||||
/*
|
||||
* Creating driver objects
|
||||
* -----------------------
|
||||
@@ -309,7 +305,6 @@ 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)
|
||||
@@ -444,10 +439,12 @@ DECL_DRIVER_API_N(setPushConstant,
|
||||
backend::PushConstantVariant, value)
|
||||
|
||||
DECL_DRIVER_API_N(insertEventMarker,
|
||||
const char*, string)
|
||||
const char*, string,
|
||||
uint32_t, len = 0)
|
||||
|
||||
DECL_DRIVER_API_N(pushGroupMarker,
|
||||
const char*, string)
|
||||
const char*, string,
|
||||
uint32_t, len = 0)
|
||||
|
||||
DECL_DRIVER_API_0(popGroupMarker)
|
||||
|
||||
|
||||
@@ -20,12 +20,11 @@
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
@@ -40,7 +39,7 @@
|
||||
|
||||
#define HandleAllocatorGL HandleAllocator<32, 64, 136> // ~4520 / pool / MiB
|
||||
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
|
||||
#define HandleAllocatorMTL HandleAllocator<32, 64, 552> // ~1660 / pool / MiB
|
||||
#define HandleAllocatorMTL HandleAllocator<32, 48, 552> // ~1660 / pool / MiB
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -174,10 +173,8 @@ public:
|
||||
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
|
||||
auto const pNode = static_cast<typename Allocator::Node*>(p);
|
||||
uint8_t const expectedAge = pNode[-1].age;
|
||||
// getHandleTag() is only called if the check fails.
|
||||
FILAMENT_CHECK_POSTCONDITION(expectedAge == age)
|
||||
<< "use-after-free of Handle with id=" << handle.getId()
|
||||
<< ", tag=" << getHandleTag(handle.getId()).c_str_safe();
|
||||
FILAMENT_CHECK_POSTCONDITION(expectedAge == age) <<
|
||||
"use-after-free of Handle with id=" << handle.getId();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,29 +201,6 @@ public:
|
||||
return handle_cast<Dp>(const_cast<Handle<B>&>(handle));
|
||||
}
|
||||
|
||||
void associateTagToHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
|
||||
// TODO: for now, only pool handles check for use-after-free, so we only keep tags for
|
||||
// those
|
||||
if (isPoolHandle(id)) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
utils::CString getHandleTag(HandleBase::HandleId id) const noexcept {
|
||||
if (!isPoolHandle(id)) {
|
||||
return "(no tag)";
|
||||
}
|
||||
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)";
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template<typename D>
|
||||
@@ -344,24 +318,12 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
// 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;
|
||||
|
||||
static bool isPoolHandle(HandleBase::HandleId id) noexcept {
|
||||
return (id & HANDLE_HEAP_FLAG) == 0u;
|
||||
@@ -376,7 +338,7 @@ private:
|
||||
// a non-pool handle.
|
||||
if (UTILS_LIKELY(isPoolHandle(id))) {
|
||||
char* const base = (char*)mHandleArena.getArea().begin();
|
||||
uint32_t const tag = id & HANDLE_AGE_MASK;
|
||||
uint32_t const tag = id & HANDLE_TAG_MASK;
|
||||
size_t const offset = (id & HANDLE_INDEX_MASK) * Allocator::getAlignment();
|
||||
return { static_cast<void*>(base + offset), tag };
|
||||
}
|
||||
@@ -391,7 +353,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_AGE_MASK;
|
||||
id |= tag & HANDLE_TAG_MASK;
|
||||
assert_invariant((id & HANDLE_HEAP_FLAG) == 0);
|
||||
return id;
|
||||
}
|
||||
@@ -401,7 +363,6 @@ private:
|
||||
// Below is only used when running out of space in the HandleArena
|
||||
mutable utils::Mutex mLock;
|
||||
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
|
||||
tsl::robin_map<HandleBase::HandleId, utils::CString> mDebugTags;
|
||||
HandleBase::HandleId mId = 0;
|
||||
bool mUseAfterFreeCheckDisabled = false;
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#if !defined(WIN32) && !defined(__EMSCRIPTEN__)
|
||||
#if !defined(WIN32) && !defined(__EMSCRIPTEN__) && !defined(IOS)
|
||||
# include <sys/mman.h>
|
||||
# include <unistd.h>
|
||||
# define HAS_MMAP 1
|
||||
|
||||
@@ -101,8 +101,9 @@ void CommandBufferQueue::flush() noexcept {
|
||||
size_t const used = std::distance(
|
||||
static_cast<char const*>(begin), static_cast<char const*>(end));
|
||||
|
||||
|
||||
std::unique_lock<utils::Mutex> lock(mLock);
|
||||
mCommandBuffersToExecute.push_back({ begin, end });
|
||||
mCondition.notify_one();
|
||||
|
||||
// circular buffer is too small, we corrupted the stream
|
||||
FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) <<
|
||||
@@ -111,13 +112,11 @@ void CommandBufferQueue::flush() noexcept {
|
||||
"Space used at this time: " << used <<
|
||||
" bytes, overflow: " << used - mFreeSpace << " bytes";
|
||||
|
||||
mFreeSpace -= used;
|
||||
mCommandBuffersToExecute.push_back({ begin, end });
|
||||
mCondition.notify_one();
|
||||
|
||||
// wait until there is enough space in the buffer
|
||||
mFreeSpace -= used;
|
||||
if (UTILS_UNLIKELY(mFreeSpace < requiredSize)) {
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t const totalUsed = circularBuffer.size() - mFreeSpace;
|
||||
slog.d << "CommandStream used too much space (will block): "
|
||||
@@ -154,10 +153,8 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
|
||||
}
|
||||
|
||||
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Range const& buffer) {
|
||||
size_t const used = std::distance(
|
||||
static_cast<char const*>(buffer.begin), static_cast<char const*>(buffer.end));
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
mFreeSpace += used;
|
||||
mFreeSpace += uintptr_t(buffer.end) - uintptr_t(buffer.begin);
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,16 +20,11 @@
|
||||
#include <utils/CallStack.h>
|
||||
#endif
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Profiler.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <sys/system_properties.h>
|
||||
@@ -79,8 +74,8 @@ CommandStream::CommandStream(Driver& driver, CircularBuffer& buffer) noexcept
|
||||
}
|
||||
|
||||
void CommandStream::execute(void* buffer) {
|
||||
// NOTE: we can't use SYSTRACE_CALL() or similar here because, execute() below, also
|
||||
// uses systrace BEGIN/END and the END is not guaranteed to be happening in this scope.
|
||||
SYSTRACE_CALL();
|
||||
SYSTRACE_CONTEXT();
|
||||
|
||||
Profiler profiler;
|
||||
|
||||
@@ -105,7 +100,6 @@ void CommandStream::execute(void* buffer) {
|
||||
// we want to remove all this when tracing is completely disabled
|
||||
profiler.stop();
|
||||
UTILS_UNUSED Profiler::Counters const counters = profiler.readCounters();
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_VALUE32("GLThread (I)", counters.getInstructions());
|
||||
SYSTRACE_VALUE32("GLThread (C)", counters.getCpuCycles());
|
||||
SYSTRACE_VALUE32("GLThread (CPI x10)", counters.getCPI() * 10);
|
||||
|
||||
@@ -80,9 +80,6 @@ HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size,
|
||||
bool disableUseAfterFreeCheck) noexcept
|
||||
: mHandleArena(name, size, disableUseAfterFreeCheck),
|
||||
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) {
|
||||
// Reserve initial space for debug tags. This prevents excessive calls to malloc when the first
|
||||
// few tags are set.
|
||||
mDebugTags.reserve(512);
|
||||
}
|
||||
|
||||
template <size_t P0, size_t P1, size_t P2>
|
||||
|
||||
@@ -160,8 +160,6 @@ 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;
|
||||
|
||||
|
||||
@@ -53,8 +53,8 @@ MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType,
|
||||
mBuffer = { [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate],
|
||||
TrackedMetalBuffer::Type::GENERIC };
|
||||
}
|
||||
// mBuffer might fail to be allocated. Clients can check for this by calling
|
||||
// wasAllocationSuccessful().
|
||||
FILAMENT_CHECK_POSTCONDITION(mBuffer)
|
||||
<< "Could not allocate Metal buffer of size " << size << ".";
|
||||
}
|
||||
|
||||
MetalBuffer::~MetalBuffer() {
|
||||
|
||||
@@ -93,7 +93,7 @@ struct MetalContext {
|
||||
} highestSupportedGpuFamily;
|
||||
|
||||
struct {
|
||||
bool staticTextureTargetError;
|
||||
bool a8xStaticTextureTargetError;
|
||||
} bugs;
|
||||
|
||||
// sampleCountLookup[requestedSamples] gives a <= sample count supported by the device.
|
||||
@@ -112,7 +112,6 @@ struct MetalContext {
|
||||
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
|
||||
CullModeStateTracker cullModeState;
|
||||
WindingStateTracker windingState;
|
||||
DepthClampStateTracker depthClampState;
|
||||
Handle<HwRenderPrimitive> currentRenderPrimitive;
|
||||
|
||||
// State caches.
|
||||
|
||||
@@ -57,6 +57,7 @@ class MetalDriver final : public DriverBase {
|
||||
|
||||
public:
|
||||
static Driver* create(MetalPlatform* platform, const Platform::DriverConfig& driverConfig);
|
||||
void runAtNextTick(const std::function<void()>& fn) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
@@ -72,12 +73,10 @@ 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
|
||||
|
||||
@@ -53,14 +53,14 @@ Driver* MetalDriverFactory::create(MetalPlatform* const platform, const Platform
|
||||
// MetalRenderPrimitive : 24 many
|
||||
// MetalVertexBuffer : 32 moderate
|
||||
// -- less than or equal 32 bytes
|
||||
// MetalIndexBuffer : 40 moderate
|
||||
// MetalFence : 48 few
|
||||
// MetalIndexBuffer : 56 moderate
|
||||
// MetalBufferObject : 64 many
|
||||
// -- less than or equal 64 bytes
|
||||
// MetalBufferObject : 48 many
|
||||
// -- less than or equal 48 bytes
|
||||
// MetalSamplerGroup : 112 few
|
||||
// MetalProgram : 152 moderate
|
||||
// MetalTexture : 152 moderate
|
||||
// MetalSwapChain : 208 few
|
||||
// MetalSwapChain : 184 few
|
||||
// MetalRenderTarget : 272 few
|
||||
// MetalVertexBufferInfo : 552 moderate
|
||||
// -- less than or equal to 552 bytes
|
||||
@@ -162,10 +162,8 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
|
||||
sc[s] = [mContext->device supportsTextureSampleCount:s] ? s : sc[s - 1];
|
||||
}
|
||||
|
||||
mContext->bugs.staticTextureTargetError =
|
||||
[mContext->device.name containsString:@"Apple A8X GPU"] ||
|
||||
[mContext->device.name containsString:@"Apple A8 GPU"] ||
|
||||
[mContext->device.name containsString:@"Apple A7 GPU"];
|
||||
mContext->bugs.a8xStaticTextureTargetError =
|
||||
[mContext->device.name containsString:@"Apple A8X GPU"];
|
||||
|
||||
mContext->commandQueue = mPlatform.createCommandQueue(mContext->device);
|
||||
mContext->pipelineStateCache.setDevice(mContext->device);
|
||||
@@ -320,40 +318,17 @@ 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);
|
||||
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();
|
||||
});
|
||||
}
|
||||
auto elementSize = (uint8_t) getElementTypeSize(elementType);
|
||||
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
|
||||
}
|
||||
|
||||
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage) {
|
||||
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();
|
||||
});
|
||||
}
|
||||
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
|
||||
}
|
||||
|
||||
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
@@ -857,10 +832,6 @@ bool MetalDriver::isProtectedTexturesSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isDepthClampSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU:
|
||||
@@ -869,8 +840,8 @@ bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
return true;
|
||||
case Workaround::ADRENO_UNIFORM_ARRAY_CRASH:
|
||||
return false;
|
||||
case Workaround::METAL_STATIC_TEXTURE_TARGET_ERROR:
|
||||
return mContext->bugs.staticTextureTargetError;
|
||||
case Workaround::A8X_STATIC_TEXTURE_TARGET_ERROR:
|
||||
return mContext->bugs.a8xStaticTextureTargetError;
|
||||
case Workaround::DISABLE_BLIT_INTO_TEXTURE_ARRAY:
|
||||
return false;
|
||||
default:
|
||||
@@ -1302,11 +1273,11 @@ void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
pushConstants.setPushConstant(value, index);
|
||||
}
|
||||
|
||||
void MetalDriver::insertEventMarker(const char* string) {
|
||||
void MetalDriver::insertEventMarker(const char* string, uint32_t len) {
|
||||
|
||||
}
|
||||
|
||||
void MetalDriver::pushGroupMarker(const char* string) {
|
||||
void MetalDriver::pushGroupMarker(const char* string, uint32_t len) {
|
||||
mContext->groupMarkers.push(string);
|
||||
}
|
||||
|
||||
@@ -1778,13 +1749,6 @@ 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) {
|
||||
@@ -2095,17 +2059,16 @@ void MetalDriver::enumerateBoundBuffers(BufferObjectBinding bindingType,
|
||||
void MetalDriver::resetState(int) {
|
||||
}
|
||||
|
||||
void MetalDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
||||
mHandleAllocator.associateTagToHandle(handleId, std::move(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,6 +257,10 @@ void MetalSwapChain::present() {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD
|
||||
#define FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD 1
|
||||
#endif
|
||||
|
||||
class PresentDrawableData {
|
||||
public:
|
||||
PresentDrawableData() = delete;
|
||||
@@ -275,10 +279,14 @@ 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:
|
||||
|
||||
@@ -382,7 +382,6 @@ using SamplerStateCache = StateCache<SamplerState, id<MTLSamplerState>, SamplerS
|
||||
|
||||
using CullModeStateTracker = StateTracker<MTLCullMode>;
|
||||
using WindingStateTracker = StateTracker<MTLWinding>;
|
||||
using DepthClampStateTracker = StateTracker<MTLDepthClipMode>;
|
||||
|
||||
// Argument encoder
|
||||
|
||||
|
||||
@@ -202,10 +202,6 @@ bool NoopDriver::isProtectedTexturesSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NoopDriver::isDepthClampSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isWorkaroundNeeded(Workaround) {
|
||||
return false;
|
||||
}
|
||||
@@ -320,10 +316,10 @@ void NoopDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
backend::PushConstantVariant value) {
|
||||
}
|
||||
|
||||
void NoopDriver::insertEventMarker(char const* string) {
|
||||
void NoopDriver::insertEventMarker(char const* string, uint32_t len) {
|
||||
}
|
||||
|
||||
void NoopDriver::pushGroupMarker(char const* string) {
|
||||
void NoopDriver::pushGroupMarker(char const* string, uint32_t len) {
|
||||
}
|
||||
|
||||
void NoopDriver::popGroupMarker(int) {
|
||||
@@ -392,7 +388,4 @@ void NoopDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
void NoopDriver::resetState(int) {
|
||||
}
|
||||
|
||||
void NoopDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -676,7 +676,6 @@ 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);
|
||||
@@ -747,7 +746,6 @@ 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;
|
||||
|
||||
@@ -220,9 +220,8 @@ public:
|
||||
bool EXT_color_buffer_float;
|
||||
bool EXT_color_buffer_half_float;
|
||||
bool EXT_debug_marker;
|
||||
bool EXT_depth_clamp;
|
||||
bool EXT_discard_framebuffer;
|
||||
bool EXT_disjoint_timer_query;
|
||||
bool EXT_discard_framebuffer;
|
||||
bool EXT_multisampled_render_to_texture2;
|
||||
bool EXT_multisampled_render_to_texture;
|
||||
bool EXT_protected_textures;
|
||||
@@ -240,10 +239,10 @@ public:
|
||||
bool KHR_parallel_shader_compile;
|
||||
bool KHR_texture_compression_astc_hdr;
|
||||
bool KHR_texture_compression_astc_ldr;
|
||||
bool OES_EGL_image_external_essl3;
|
||||
bool OES_depth24;
|
||||
bool OES_depth_texture;
|
||||
bool OES_depth24;
|
||||
bool OES_packed_depth_stencil;
|
||||
bool OES_EGL_image_external_essl3;
|
||||
bool OES_rgb8_rgba8;
|
||||
bool OES_standard_derivatives;
|
||||
bool OES_texture_npot;
|
||||
@@ -628,7 +627,6 @@ 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());
|
||||
|
||||
@@ -83,20 +83,12 @@
|
||||
#define HAS_MAPBUFFERS 1
|
||||
#endif
|
||||
|
||||
#define DEBUG_GROUP_MARKER_NONE 0x00 // no debug marker
|
||||
#define DEBUG_GROUP_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
|
||||
#define DEBUG_GROUP_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
|
||||
#define DEBUG_GROUP_MARKER_ALL 0x03 // all markers
|
||||
#define DEBUG_MARKER_NONE 0x00 // no debug marker
|
||||
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
|
||||
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
|
||||
#define DEBUG_MARKER_ALL 0x03 // all markers
|
||||
|
||||
#define DEBUG_MARKER_NONE 0x00 // no debug marker
|
||||
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
|
||||
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
|
||||
#define DEBUG_MARKER_ALL 0x03 // all markers
|
||||
|
||||
// set to the desired debug marker level (for user markers [default: All])
|
||||
#define DEBUG_GROUP_MARKER_LEVEL DEBUG_GROUP_MARKER_ALL
|
||||
|
||||
// set to the desired debug level (for internal debugging [Default: None])
|
||||
// set to the desired debug marker level
|
||||
#define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE
|
||||
|
||||
#if DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
|
||||
@@ -204,37 +196,11 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
|
||||
|
||||
OpenGLDriver::DebugMarker::DebugMarker(OpenGLDriver& driver, const char* string) noexcept
|
||||
: driver(driver) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
|
||||
if (UTILS_LIKELY(driver.getContext().ext.EXT_debug_marker)) {
|
||||
glPushGroupMarkerEXT(GLsizei(strlen(string)), string);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_BEGIN(string);
|
||||
#endif
|
||||
#endif
|
||||
driver.pushGroupMarker(string, strlen(string));
|
||||
}
|
||||
|
||||
OpenGLDriver::DebugMarker::~DebugMarker() noexcept {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
|
||||
if (UTILS_LIKELY(driver.getContext().ext.EXT_debug_marker)) {
|
||||
glPopGroupMarkerEXT();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_END();
|
||||
#endif
|
||||
#endif
|
||||
driver.popGroupMarker();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -451,14 +417,6 @@ 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 {
|
||||
@@ -1148,7 +1106,6 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
if (any(t->usage & TextureUsage::SAMPLEABLE)) {
|
||||
switch (t->target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
case SamplerType::SAMPLER_3D:
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
// this could be GL_TEXTURE_2D_MULTISAMPLE or GL_TEXTURE_2D_ARRAY
|
||||
@@ -1159,9 +1116,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
target = getCubemapTarget(binfo.layer);
|
||||
// note: cubemaps can't be multi-sampled
|
||||
break;
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
// This is an error. We have asserted in debug build.
|
||||
target = t->gl.target;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1179,7 +1134,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
|
||||
if (rt->gl.samples <= 1 ||
|
||||
(rt->gl.samples > 1 && t->samples > 1 && gl.features.multisample_texture)) {
|
||||
// On GL3.2 / GLES3.1 and above multisample is handled when creating the texture.
|
||||
// on GL3.2 / GLES3.1 and above multisample is handled when creating the texture.
|
||||
// If multisampled textures are not supported and we end-up here, things should
|
||||
// still work, albeit without MSAA.
|
||||
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
|
||||
@@ -1203,7 +1158,6 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
GL_RENDERBUFFER, t->gl.id);
|
||||
}
|
||||
break;
|
||||
case GL_TEXTURE_3D:
|
||||
case GL_TEXTURE_2D_ARRAY:
|
||||
case GL_TEXTURE_CUBE_MAP_ARRAY:
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
@@ -1317,7 +1271,6 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
GL_RENDERBUFFER, t->gl.id);
|
||||
}
|
||||
break;
|
||||
case GL_TEXTURE_3D:
|
||||
case GL_TEXTURE_2D_ARRAY:
|
||||
case GL_TEXTURE_CUBE_MAP_ARRAY:
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
@@ -2132,10 +2085,6 @@ 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:
|
||||
@@ -2227,10 +2176,6 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
|
||||
// Updating driver objects
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void OpenGLDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
||||
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
|
||||
}
|
||||
|
||||
void OpenGLDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh,
|
||||
uint32_t index, Handle<HwBufferObject> boh) {
|
||||
DEBUG_MARKER()
|
||||
@@ -2384,7 +2329,9 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
|
||||
GLSamplerGroup* const sb = handle_cast<GLSamplerGroup *>(sbh);
|
||||
assert_invariant(sb->textureUnitEntries.size() == data.size / sizeof(SamplerDescriptor));
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
bool const es2 = context.isES2();
|
||||
#endif
|
||||
|
||||
auto const* const pSamplers = (SamplerDescriptor const*)data.buffer;
|
||||
for (size_t i = 0, c = sb->textureUnitEntries.size(); i < c; i++) {
|
||||
@@ -2394,15 +2341,6 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
|
||||
GLTexture const* const t = handle_cast<const GLTexture*>(th);
|
||||
assert_invariant(t);
|
||||
|
||||
if (UTILS_UNLIKELY(es2)
|
||||
#if defined(GL_EXT_texture_filter_anisotropic)
|
||||
|| UTILS_UNLIKELY(anisotropyWorkaround)
|
||||
#endif
|
||||
) {
|
||||
// We must set texture parameters on the texture itself.
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
}
|
||||
|
||||
SamplerParams params = pSamplers[i].s;
|
||||
if (UTILS_UNLIKELY(t->target == SamplerType::SAMPLER_EXTERNAL)) {
|
||||
// From OES_EGL_image_external spec:
|
||||
@@ -3199,28 +3137,28 @@ void OpenGLDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
|
||||
void OpenGLDriver::insertEventMarker(char const* string) {
|
||||
void OpenGLDriver::insertEventMarker(char const* string, uint32_t len) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
auto& gl = mContext;
|
||||
if (gl.ext.EXT_debug_marker) {
|
||||
glInsertEventMarkerEXT(GLsizei(strlen(string)), string);
|
||||
glInsertEventMarkerEXT(GLsizei(len ? len : strlen(string)), string);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDriver::pushGroupMarker(char const* string) {
|
||||
void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_OPENGL
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
|
||||
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
|
||||
glPushGroupMarkerEXT(GLsizei(strlen(string)), string);
|
||||
glPushGroupMarkerEXT(GLsizei(len ? len : strlen(string)), string);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_BEGIN(string);
|
||||
#endif
|
||||
@@ -3230,14 +3168,14 @@ void OpenGLDriver::pushGroupMarker(char const* string) {
|
||||
void OpenGLDriver::popGroupMarker(int) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_OPENGL
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
|
||||
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
|
||||
glPopGroupMarkerEXT();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_END();
|
||||
#endif
|
||||
@@ -3943,7 +3881,6 @@ void OpenGLDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
}
|
||||
|
||||
void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
DEBUG_MARKER()
|
||||
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
|
||||
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
|
||||
return;
|
||||
@@ -3965,7 +3902,6 @@ void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
|
||||
}
|
||||
|
||||
void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
DEBUG_MARKER()
|
||||
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
|
||||
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
|
||||
return;
|
||||
@@ -3986,7 +3922,6 @@ void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_
|
||||
}
|
||||
|
||||
void OpenGLDriver::scissor(Viewport scissor) {
|
||||
DEBUG_MARKER()
|
||||
setScissor(scissor);
|
||||
}
|
||||
|
||||
@@ -4006,7 +3941,6 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
|
||||
}
|
||||
|
||||
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
DEBUG_MARKER()
|
||||
getShaderCompilerService().tick();
|
||||
|
||||
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(program);
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
const Platform::DriverConfig& driverConfig) noexcept;
|
||||
|
||||
class DebugMarker {
|
||||
UTILS_UNUSED OpenGLDriver& driver;
|
||||
OpenGLDriver& driver;
|
||||
public:
|
||||
DebugMarker(OpenGLDriver& driver, const char* string) noexcept;
|
||||
~DebugMarker() noexcept;
|
||||
|
||||
@@ -143,25 +143,21 @@ void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQ
|
||||
|
||||
driver.runEveryNowAndThen([&context = mContext, weak]() -> bool {
|
||||
auto state = weak.lock();
|
||||
if (!state) {
|
||||
// The timer query state has been destroyed on the way, very likely due to the IBL
|
||||
// prefilter context destruction. We still return true to get this element removed from
|
||||
// the query list.
|
||||
return true;
|
||||
if (state) {
|
||||
GLuint available = 0;
|
||||
context.procs.getQueryObjectuiv(state->gl.query, GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
if (!available) {
|
||||
// we need to try this one again later
|
||||
return false;
|
||||
}
|
||||
GLuint64 elapsedTime = 0;
|
||||
// we won't end-up here if we're on ES and don't have GL_EXT_disjoint_timer_query
|
||||
context.procs.getQueryObjectui64v(state->gl.query, GL_QUERY_RESULT, &elapsedTime);
|
||||
state->elapsed.store((int64_t)elapsedTime, std::memory_order_relaxed);
|
||||
} else {
|
||||
state->elapsed.store(int64_t(TimerQueryResult::ERROR), std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
GLuint available = 0;
|
||||
context.procs.getQueryObjectuiv(state->gl.query, GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
if (!available) {
|
||||
// we need to try this one again later
|
||||
return false;
|
||||
}
|
||||
GLuint64 elapsedTime = 0;
|
||||
// we won't end-up here if we're on ES and don't have GL_EXT_disjoint_timer_query
|
||||
context.procs.getQueryObjectui64v(state->gl.query, GL_QUERY_RESULT, &elapsedTime);
|
||||
state->elapsed.store((int64_t)elapsedTime, std::memory_order_relaxed);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,28 +26,16 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/JobSystem.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using namespace utils;
|
||||
@@ -488,12 +476,6 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
|
||||
// check status of program linking and shader compilation, logs error and free all resources
|
||||
// in case of error.
|
||||
bool const success = checkProgramStatus(token);
|
||||
|
||||
// Unless we have matdbg, we panic if a program is invalid. Otherwise, we'd get a UB.
|
||||
// The compilation error has been logged to log.e by this point.
|
||||
FILAMENT_CHECK_POSTCONDITION(FILAMENT_ENABLE_MATDBG || success)
|
||||
<< "OpenGL program " << token->name.c_str_safe() << " failed to link or compile";
|
||||
|
||||
if (UTILS_LIKELY(success)) {
|
||||
program = token->gl.program;
|
||||
// no need to keep the shaders around
|
||||
@@ -597,31 +579,24 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
version = "#version 310 es\n";
|
||||
}
|
||||
|
||||
std::array<std::string_view, 5> sources = {
|
||||
version,
|
||||
prolog,
|
||||
specializationConstantString,
|
||||
packingFunctions,
|
||||
{ body.data(), body.size() - 1 } // null-terminated
|
||||
const std::array<const char*, 5> sources = {
|
||||
version.data(),
|
||||
prolog.data(),
|
||||
specializationConstantString.c_str(),
|
||||
packingFunctions.data(),
|
||||
body.data()
|
||||
};
|
||||
|
||||
// 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();
|
||||
}
|
||||
const std::array<GLint, 5> lengths = {
|
||||
(GLint)version.length(),
|
||||
(GLint)prolog.length(),
|
||||
(GLint)specializationConstantString.length(),
|
||||
(GLint)packingFunctions.length(),
|
||||
(GLint)body.length() - 1 // null terminated
|
||||
};
|
||||
|
||||
GLuint const shaderId = glCreateShader(glShaderType);
|
||||
glShaderSource(shaderId, count, shaderStrings.data(), lengths.data());
|
||||
|
||||
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
|
||||
glCompileShader(shaderId);
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
@@ -201,12 +201,6 @@ 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
|
||||
|
||||
@@ -59,11 +59,7 @@ VkExtent2D VulkanAttachment::getExtent2D() const {
|
||||
|
||||
VkImageView VulkanAttachment::getImageView() {
|
||||
assert_invariant(texture);
|
||||
VkImageSubresourceRange range = getSubresourceRange();
|
||||
if (range.layerCount > 1) {
|
||||
return texture->getMultiviewAttachmentView(range);
|
||||
}
|
||||
return texture->getAttachmentView(range);
|
||||
return texture->getAttachmentView(getSubresourceRange());
|
||||
}
|
||||
|
||||
bool VulkanAttachment::isDepth() const {
|
||||
@@ -77,7 +73,7 @@ VkImageSubresourceRange VulkanAttachment::getSubresourceRange() const {
|
||||
.baseMipLevel = uint32_t(level),
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = uint32_t(layer),
|
||||
.layerCount = layerCount,
|
||||
.layerCount = 1,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -43,8 +43,6 @@ struct VulkanCommandBuffer;
|
||||
struct VulkanAttachment {
|
||||
VulkanTexture* texture = nullptr;
|
||||
uint8_t level = 0;
|
||||
uint8_t baseViewIndex = 0;
|
||||
uint8_t layerCount = 1;
|
||||
uint16_t layer = 0;
|
||||
|
||||
bool isDepth() const;
|
||||
@@ -125,10 +123,6 @@ public:
|
||||
return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE;
|
||||
}
|
||||
|
||||
inline bool isDepthClampSupported() const noexcept {
|
||||
return mPhysicalDeviceFeatures.depthClamp == VK_TRUE;
|
||||
}
|
||||
|
||||
inline bool isDebugMarkersSupported() const noexcept {
|
||||
return mDebugMarkersSupported;
|
||||
}
|
||||
|
||||
@@ -595,8 +595,6 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
colorTargets[i] = {
|
||||
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(color[i].handle),
|
||||
.level = color[i].level,
|
||||
.baseViewIndex = color[i].baseViewIndex,
|
||||
.layerCount = layerCount,
|
||||
.layer = color[i].layer,
|
||||
};
|
||||
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = colorTargets[i].getExtent2D();
|
||||
@@ -611,8 +609,6 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
depthStencil[0] = {
|
||||
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(depth.handle),
|
||||
.level = depth.level,
|
||||
.baseViewIndex = depth.baseViewIndex,
|
||||
.layerCount = layerCount,
|
||||
.layer = depth.layer,
|
||||
};
|
||||
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[0].getExtent2D();
|
||||
@@ -625,8 +621,6 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
depthStencil[1] = {
|
||||
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(stencil.handle),
|
||||
.level = stencil.level,
|
||||
.baseViewIndex = stencil.baseViewIndex,
|
||||
.layerCount = layerCount,
|
||||
.layer = stencil.layer,
|
||||
};
|
||||
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[1].getExtent2D();
|
||||
@@ -643,7 +637,7 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
|
||||
auto renderTarget = mResourceAllocator.construct<VulkanRenderTarget>(rth, mPlatform->getDevice(),
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, width, height,
|
||||
samples, colorTargets, depthStencil, mStagePool, layerCount);
|
||||
samples, colorTargets, depthStencil, mStagePool);
|
||||
mResourceManager.acquire(renderTarget);
|
||||
}
|
||||
|
||||
@@ -944,10 +938,6 @@ bool VulkanDriver::isProtectedTexturesSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isDepthClampSupported() {
|
||||
return mContext.isDepthClampSupported();
|
||||
}
|
||||
|
||||
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU: {
|
||||
@@ -1277,8 +1267,6 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t const renderTargetLayerCount = rt->getLayerCount();
|
||||
|
||||
// Create the VkRenderPass or fetch it from cache.
|
||||
VulkanFboCache::RenderPassKey rpkey = {
|
||||
.initialColorLayoutMask = 0,
|
||||
@@ -1289,12 +1277,10 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
.discardEnd = discardEndVal,
|
||||
.samples = rt->getSamples(),
|
||||
.subpassMask = uint8_t(params.subpassMask),
|
||||
.viewCount = renderTargetLayerCount,
|
||||
};
|
||||
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
|
||||
const VulkanAttachment& info = rt->getColor(i);
|
||||
if (info.texture) {
|
||||
assert_invariant(info.layerCount == renderTargetLayerCount);
|
||||
rpkey.initialColorLayoutMask |= 1 << i;
|
||||
rpkey.colorFormat[i] = info.getFormat();
|
||||
if (rpkey.samples > 1 && info.texture->samples == 1) {
|
||||
@@ -1606,13 +1592,13 @@ void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
value);
|
||||
}
|
||||
|
||||
void VulkanDriver::insertEventMarker(char const* string) {
|
||||
void VulkanDriver::insertEventMarker(char const* string, uint32_t len) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
mCommands.insertEventMarker(string, strlen(string));
|
||||
mCommands.insertEventMarker(string, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
void VulkanDriver::pushGroupMarker(char const* string) {
|
||||
void VulkanDriver::pushGroupMarker(char const* string, uint32_t) {
|
||||
// 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);
|
||||
@@ -1820,7 +1806,6 @@ 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,
|
||||
@@ -2037,10 +2022,6 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
|
||||
void VulkanDriver::resetState(int) {
|
||||
}
|
||||
|
||||
void VulkanDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
||||
mResourceAllocator.associateHandle(handleId, std::move(tag));
|
||||
}
|
||||
|
||||
// explicit instantiation of the Dispatcher
|
||||
template class ConcreteDispatcher<VulkanDriver>;
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ bool VulkanFboCache::RenderPassEq::operator()(const RenderPassKey& k1,
|
||||
if (k1.samples != k2.samples) return false;
|
||||
if (k1.needsResolveMask != k2.needsResolveMask) return false;
|
||||
if (k1.subpassMask != k2.subpassMask) return false;
|
||||
if (k1.viewCount != k2.viewCount) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -187,25 +186,6 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
.pDependencies = dependencies
|
||||
};
|
||||
|
||||
VkRenderPassMultiviewCreateInfo multiviewCreateInfo = {};
|
||||
uint32_t const subpassViewMask = (1 << config.viewCount) - 1;
|
||||
// Prepare a view mask array for the maximum number of subpasses. All subpasses have all views
|
||||
// activated.
|
||||
uint32_t const viewMasks[2] = {subpassViewMask, subpassViewMask};
|
||||
if (config.viewCount > 1) {
|
||||
// Fill the multiview create info.
|
||||
multiviewCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
|
||||
multiviewCreateInfo.pNext = nullptr;
|
||||
multiviewCreateInfo.subpassCount = hasSubpasses ? 2u : 1u;
|
||||
multiviewCreateInfo.pViewMasks = viewMasks;
|
||||
multiviewCreateInfo.dependencyCount = 0;
|
||||
multiviewCreateInfo.pViewOffsets = nullptr;
|
||||
multiviewCreateInfo.correlationMaskCount = 1;
|
||||
multiviewCreateInfo.pCorrelationMasks = &subpassViewMask;
|
||||
|
||||
renderPassInfo.pNext = &multiviewCreateInfo;
|
||||
}
|
||||
|
||||
int attachmentIndex = 0;
|
||||
|
||||
// Populate the Color Attachments.
|
||||
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
uint8_t samples; // 1 byte
|
||||
uint8_t needsResolveMask; // 1 byte
|
||||
uint8_t subpassMask; // 1 byte
|
||||
uint8_t viewCount; // 1 byte
|
||||
uint8_t padding2; // 1 byte
|
||||
};
|
||||
struct RenderPassVal {
|
||||
VkRenderPass handle;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#include "VulkanDriver.h"
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanDriver.h"
|
||||
#include "VulkanMemory.h"
|
||||
@@ -322,12 +323,11 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
||||
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
|
||||
uint32_t width, uint32_t height, uint8_t samples,
|
||||
VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
|
||||
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount)
|
||||
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool)
|
||||
: HwRenderTarget(width, height),
|
||||
VulkanResource(VulkanResourceType::RENDER_TARGET),
|
||||
mOffscreen(true),
|
||||
mSamples(samples),
|
||||
mLayerCount(layerCount) {
|
||||
mSamples(samples) {
|
||||
for (int index = 0; index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; index++) {
|
||||
mColor[index] = color[index];
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
|
||||
VulkanContext const& context, VmaAllocator allocator,
|
||||
VulkanCommands* commands, uint32_t width, uint32_t height,
|
||||
uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
|
||||
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount);
|
||||
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool);
|
||||
|
||||
// Creates a special "default" render target (i.e. associated with the swap chain)
|
||||
explicit VulkanRenderTarget();
|
||||
@@ -319,7 +319,6 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
|
||||
VulkanAttachment& getMsaaDepth();
|
||||
uint8_t getColorTargetCount(const VulkanRenderPass& pass) const;
|
||||
uint8_t getSamples() const { return mSamples; }
|
||||
uint8_t getLayerCount() const { return mLayerCount; }
|
||||
bool hasDepth() const { return mDepth.texture; }
|
||||
bool isSwapChain() const { return !mOffscreen; }
|
||||
void bindToSwapChain(VulkanSwapChain& surf);
|
||||
@@ -331,7 +330,6 @@ private:
|
||||
VulkanAttachment mMsaaDepthAttachment = {};
|
||||
const bool mOffscreen : 1;
|
||||
uint8_t mSamples : 7;
|
||||
uint8_t mLayerCount = 1;
|
||||
};
|
||||
|
||||
struct VulkanBufferObject;
|
||||
|
||||
@@ -184,7 +184,6 @@ 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,8 +90,7 @@ public:
|
||||
VkBlendFactor srcAlphaBlendFactor : 5;
|
||||
VkBlendFactor dstAlphaBlendFactor : 5;
|
||||
VkColorComponentFlags colorWriteMask : 4;
|
||||
uint8_t rasterizationSamples : 4;// offset = 4 bytes
|
||||
uint8_t depthClamp : 4;
|
||||
uint8_t rasterizationSamples; // offset = 4 bytes
|
||||
uint8_t colorTargetCount; // offset = 5 bytes
|
||||
BlendEquation colorBlendOp : 4; // offset = 6 bytes
|
||||
BlendEquation alphaBlendOp : 4;
|
||||
|
||||
@@ -105,10 +105,6 @@ public:
|
||||
mHandleAllocatorImpl.deallocate(handle, obj);
|
||||
}
|
||||
|
||||
inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
|
||||
mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag));
|
||||
}
|
||||
|
||||
private:
|
||||
AllocatorImpl mHandleAllocatorImpl;
|
||||
|
||||
|
||||
@@ -97,11 +97,13 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
imageInfo.extent.depth = 1;
|
||||
}
|
||||
|
||||
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;
|
||||
// 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::SAMPLEABLE)) {
|
||||
@@ -119,7 +121,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;
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | blittable;
|
||||
if (any(usage & TextureUsage::SUBPASS_INPUT)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||
}
|
||||
@@ -128,9 +130,10 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
}
|
||||
if (any(usage & TextureUsage::UPLOADABLE)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
imageInfo.usage |= blittable;
|
||||
}
|
||||
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.
|
||||
@@ -384,15 +387,12 @@ void VulkanTexture::setPrimaryRange(uint32_t minMiplevel, uint32_t maxMiplevel)
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) {
|
||||
// Attachments should only have one mipmap level and one layer.
|
||||
range.levelCount = 1;
|
||||
range.layerCount = 1;
|
||||
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D, {});
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getMultiviewAttachmentView(VkImageSubresourceRange range) {
|
||||
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY, {});
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range, VkImageViewType type) {
|
||||
return getImageView(range, type, mSwizzle);
|
||||
}
|
||||
|
||||
@@ -73,10 +73,6 @@ struct VulkanTexture : public HwTexture, VulkanResource {
|
||||
// and the identity swizzle.
|
||||
VkImageView getAttachmentView(VkImageSubresourceRange range);
|
||||
|
||||
// Gets or creates a cached VkImageView for a single subresource that can be used as a render
|
||||
// target attachment when rendering with multiview.
|
||||
VkImageView getMultiviewAttachmentView(VkImageSubresourceRange range);
|
||||
|
||||
// This is a workaround for the first few frames where we're waiting for the texture to actually
|
||||
// be uploaded. In that case, we bind the sampler to an empty texture, but the corresponding
|
||||
// imageView needs to be of the right type. Hence, we provide an option to indicate the
|
||||
|
||||
@@ -172,7 +172,7 @@ public:
|
||||
UTILS_UNUSED VkResult result = vkAllocateDescriptorSets(mDevice, &allocInfo, &vkSet);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Failed to allocate descriptor set code=" << result << " size=" << mSize
|
||||
<< " capacity=" << mCapacity << " count=" << mUnusedCount;
|
||||
<< " capacity=" << mCapacity << " count=" << "%s";
|
||||
mSize++;
|
||||
return createSet(layout->bitmask, vkSet);
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
|
||||
}
|
||||
|
||||
// build the push constant layout key
|
||||
uint32_t const pushConstantRangeCount = program->getPushConstantRangeCount();
|
||||
auto const& pushConstantRanges = program->getPushConstantRanges();
|
||||
uint32_t pushConstantRangeCount = program->getPushConstantRangeCount();
|
||||
auto const& pushConstantRanges = program->getPushConstantRanges();
|
||||
if (pushConstantRangeCount > 0) {
|
||||
assert_invariant(pushConstantRangeCount <= Program::SHADER_TYPE_COUNT);
|
||||
for (uint8_t i = 0; i < pushConstantRangeCount; ++i) {
|
||||
@@ -52,8 +52,8 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
|
||||
}
|
||||
}
|
||||
|
||||
if (auto iter = mPipelineLayouts.find(key); iter != mPipelineLayouts.end()) {
|
||||
PipelineLayoutCacheEntry& entry = iter->second;
|
||||
if (PipelineLayoutMap::iterator iter = mPipelineLayouts.find(key); iter != mPipelineLayouts.end()) {
|
||||
PipelineLayoutCacheEntry& entry = iter.value();
|
||||
entry.lastUsed = mTimestamp++;
|
||||
return entry.handle;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -36,8 +36,8 @@ public:
|
||||
void terminate() noexcept;
|
||||
|
||||
struct PushConstantKey {
|
||||
uint8_t stage = 0;// We have one set of push constant per shader stage (fragment, vertex, etc).
|
||||
uint8_t size = 0;
|
||||
uint8_t stage;// We have one set of push constant per shader stage (fragment, vertex, etc).
|
||||
uint8_t size;
|
||||
// Note that there is also an offset parameter for push constants, but
|
||||
// we always assume our update range will have the offset 0.
|
||||
};
|
||||
@@ -73,7 +73,7 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
using PipelineLayoutMap = std::unordered_map<PipelineLayoutKey, PipelineLayoutCacheEntry,
|
||||
using PipelineLayoutMap = tsl::robin_map<PipelineLayoutKey, PipelineLayoutCacheEntry,
|
||||
PipelineLayoutKeyHashFn, PipelineLayoutKeyEqual>;
|
||||
|
||||
VkDevice mDevice;
|
||||
@@ -82,6 +82,6 @@ private:
|
||||
PipelineLayoutMap mPipelineLayouts;
|
||||
};
|
||||
|
||||
} // filament::backend
|
||||
}
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANPIPELINECACHE_H
|
||||
|
||||
@@ -325,7 +325,6 @@ 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,
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
using BufferDescriptor = backend::BufferDescriptor;
|
||||
using BindingType = backend::BufferObjectBinding;
|
||||
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
@@ -78,21 +78,6 @@ public:
|
||||
*/
|
||||
Builder& bindingType(BindingType bindingType) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this BufferObject for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this BufferObject
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the BufferObject and returns a pointer to it. After creation, the buffer
|
||||
* object is uninitialized. Use BufferObject::setBuffer() to initialize it.
|
||||
|
||||
@@ -896,29 +896,6 @@ public:
|
||||
/** Tells whether an InstanceBuffer object is valid */
|
||||
bool isValid(const InstanceBuffer* UTILS_NULLABLE p) const;
|
||||
|
||||
/**
|
||||
* Retrieve the count of each resource tracked by Engine.
|
||||
* This is intended for debugging.
|
||||
* @{
|
||||
*/
|
||||
size_t getBufferObjectCount() const noexcept;
|
||||
size_t getViewCount() const noexcept;
|
||||
size_t getSceneCount() const noexcept;
|
||||
size_t getSwapChainCount() const noexcept;
|
||||
size_t getStreamCount() const noexcept;
|
||||
size_t getIndexBufferCount() const noexcept;
|
||||
size_t getSkinningBufferCount() const noexcept;
|
||||
size_t getMorphTargetBufferCount() const noexcept;
|
||||
size_t getInstanceBufferCount() const noexcept;
|
||||
size_t getVertexBufferCount() const noexcept;
|
||||
size_t getIndirectLightCount() const noexcept;
|
||||
size_t getMaterialCount() const noexcept;
|
||||
size_t getTextureCount() const noexcept;
|
||||
size_t getSkyboxeCount() const noexcept;
|
||||
size_t getColorGradingCount() const noexcept;
|
||||
size_t getRenderTargetCount() const noexcept;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until
|
||||
* all commands to this point are executed. Note that does guarantee that the
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/PrivateImplementation.h>
|
||||
#include <utils/CString.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -55,23 +54,6 @@ public:
|
||||
template<typename T>
|
||||
using BuilderBase = utils::PrivateImplementation<T>;
|
||||
|
||||
// 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 {
|
||||
public:
|
||||
Builder& name(const char* name, size_t len) noexcept {
|
||||
builderMakeName(mName, name, len);
|
||||
return static_cast<Builder&>(*this);
|
||||
}
|
||||
|
||||
utils::CString const& getName() const noexcept { return mName; }
|
||||
|
||||
private:
|
||||
utils::CString mName;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_FILAMENTAPI_H
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
UINT = uint8_t(backend::ElementType::UINT), //!< 32-bit indices
|
||||
};
|
||||
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
@@ -83,21 +83,6 @@ public:
|
||||
*/
|
||||
Builder& bufferType(IndexType indexType) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this IndexBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this IndexBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the IndexBuffer object and returns a pointer to it. After creation, the index
|
||||
* buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer.
|
||||
|
||||
@@ -38,7 +38,7 @@ class UTILS_PUBLIC InstanceBuffer : public FilamentAPI {
|
||||
struct BuilderDetails;
|
||||
|
||||
public:
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
|
||||
public:
|
||||
@@ -70,21 +70,6 @@ public:
|
||||
*/
|
||||
Builder& localTransforms(math::mat4f const* UTILS_NULLABLE localTransforms) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this InstanceBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this InstanceBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the InstanceBuffer object and returns a pointer to it.
|
||||
*/
|
||||
|
||||
@@ -39,7 +39,7 @@ class UTILS_PUBLIC MorphTargetBuffer : public FilamentAPI {
|
||||
struct BuilderDetails;
|
||||
|
||||
public:
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
@@ -63,21 +63,6 @@ public:
|
||||
*/
|
||||
Builder& count(size_t count) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this MorphTargetBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this MorphTargetBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the MorphTargetBuffer object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -39,7 +39,7 @@ class UTILS_PUBLIC SkinningBuffer : public FilamentAPI {
|
||||
struct BuilderDetails;
|
||||
|
||||
public:
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
@@ -69,21 +69,6 @@ public:
|
||||
*/
|
||||
Builder& initialize(bool initialize = true) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this SkinningBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this SkinningBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the SkinningBuffer object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
*
|
||||
* To create a NATIVE stream, call the <pre>stream</pre> method on the builder.
|
||||
*/
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
@@ -136,21 +136,6 @@ public:
|
||||
*/
|
||||
Builder& height(uint32_t height) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this Stream for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this Stream
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the Stream object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -112,7 +112,7 @@ public:
|
||||
|
||||
|
||||
//! Use Builder to construct a Texture object instance
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
@@ -202,21 +202,6 @@ public:
|
||||
*/
|
||||
Builder& swizzle(Swizzle r, Swizzle g, Swizzle b, Swizzle a) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this Texture for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this Texture
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the Texture object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
using AttributeType = backend::ElementType;
|
||||
using BufferDescriptor = backend::BufferDescriptor;
|
||||
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
@@ -158,21 +158,6 @@ public:
|
||||
*/
|
||||
Builder& advancedSkinning(bool enabled) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this VertexBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this VertexBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the VertexBuffer object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
@@ -570,13 +569,6 @@ public:
|
||||
*/
|
||||
void setShadowType(ShadowType shadow) noexcept;
|
||||
|
||||
/**
|
||||
* Returns the shadow mapping technique used by this View.
|
||||
*
|
||||
* @return value set by setShadowType().
|
||||
*/
|
||||
ShadowType getShadowType() const noexcept;
|
||||
|
||||
/**
|
||||
* Sets VSM shadowing options that apply across the entire View.
|
||||
*
|
||||
@@ -735,7 +727,7 @@ public:
|
||||
void setDebugCamera(Camera* UTILS_NULLABLE camera) noexcept;
|
||||
|
||||
//! debugging: returns a Camera from the point of view of *the* dominant directional light used for shadowing.
|
||||
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept;
|
||||
Camera const* UTILS_NULLABLE getDirectionalShadowCamera() const noexcept;
|
||||
|
||||
|
||||
/** Result of a picking query */
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include "ResourceAllocator.h"
|
||||
|
||||
#include "details/BufferObject.h"
|
||||
#include "details/Camera.h"
|
||||
#include "details/Fence.h"
|
||||
@@ -268,71 +266,6 @@ bool Engine::isValid(const InstanceBuffer* p) const {
|
||||
return downcast(this)->isValid(downcast(p));
|
||||
}
|
||||
|
||||
size_t Engine::getBufferObjectCount() const noexcept {
|
||||
return downcast(this)->getBufferObjectCount();
|
||||
}
|
||||
|
||||
size_t Engine::getViewCount() const noexcept {
|
||||
return downcast(this)->getViewCount();
|
||||
}
|
||||
|
||||
size_t Engine::getSceneCount() const noexcept {
|
||||
return downcast(this)->getSceneCount();
|
||||
}
|
||||
|
||||
size_t Engine::getSwapChainCount() const noexcept {
|
||||
return downcast(this)->getSwapChainCount();
|
||||
}
|
||||
|
||||
size_t Engine::getStreamCount() const noexcept {
|
||||
return downcast(this)->getStreamCount();
|
||||
}
|
||||
|
||||
size_t Engine::getIndexBufferCount() const noexcept {
|
||||
return downcast(this)->getIndexBufferCount();
|
||||
}
|
||||
|
||||
size_t Engine::getSkinningBufferCount() const noexcept {
|
||||
return downcast(this)->getSkinningBufferCount();
|
||||
}
|
||||
|
||||
size_t Engine::getMorphTargetBufferCount() const noexcept {
|
||||
return downcast(this)->getMorphTargetBufferCount();
|
||||
}
|
||||
|
||||
size_t Engine::getInstanceBufferCount() const noexcept {
|
||||
return downcast(this)->getInstanceBufferCount();
|
||||
}
|
||||
|
||||
size_t Engine::getVertexBufferCount() const noexcept {
|
||||
return downcast(this)->getVertexBufferCount();
|
||||
}
|
||||
|
||||
size_t Engine::getIndirectLightCount() const noexcept {
|
||||
return downcast(this)->getIndirectLightCount();
|
||||
}
|
||||
|
||||
size_t Engine::getMaterialCount() const noexcept {
|
||||
return downcast(this)->getMaterialCount();
|
||||
}
|
||||
|
||||
size_t Engine::getTextureCount() const noexcept {
|
||||
return downcast(this)->getTextureCount();
|
||||
}
|
||||
|
||||
size_t Engine::getSkyboxeCount() const noexcept {
|
||||
return downcast(this)->getSkyboxeCount();
|
||||
}
|
||||
|
||||
size_t Engine::getColorGradingCount() const noexcept {
|
||||
return downcast(this)->getColorGradingCount();
|
||||
}
|
||||
|
||||
size_t Engine::getRenderTargetCount() const noexcept {
|
||||
return downcast(this)->getRenderTargetCount();
|
||||
}
|
||||
|
||||
|
||||
void Engine::flushAndWait() {
|
||||
downcast(this)->flushAndWait();
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <filament/FilamentAPI.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace filament {
|
||||
|
||||
void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
size_t const length = std::min(len, size_t { 128u });
|
||||
outName = utils::CString(name, length);
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
+19
-26
@@ -25,7 +25,6 @@
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
@@ -79,33 +78,25 @@ void FrameInfoManager::beginFrame(DriverApi& driver, Config const& config, uint3
|
||||
});
|
||||
|
||||
// now is a good time to check the oldest active query
|
||||
while (mLast != mIndex) {
|
||||
uint64_t elapsed = 0;
|
||||
TimerQueryResult const result = driver.getTimerQueryValue(mQueries[mLast].handle, &elapsed);
|
||||
switch (result) {
|
||||
case TimerQueryResult::NOT_READY:
|
||||
// nothing to do
|
||||
break;
|
||||
case TimerQueryResult::ERROR:
|
||||
mLast = (mLast + 1) % POOL_COUNT;
|
||||
break;
|
||||
case TimerQueryResult::AVAILABLE: {
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_VALUE32("FrameInfo::elapsed", uint32_t(elapsed));
|
||||
// conversion to our duration happens here
|
||||
pFront = mQueries[mLast].pInfo;
|
||||
pFront->frameTime = std::chrono::duration<uint64_t, std::nano>(elapsed);
|
||||
mLast = (mLast + 1) % POOL_COUNT;
|
||||
denoiseFrameTime(history, config);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result != TimerQueryResult::AVAILABLE) {
|
||||
uint64_t elapsed = 0;
|
||||
TimerQueryResult const result = driver.getTimerQueryValue(mQueries[mLast].handle, &elapsed);
|
||||
switch (result) {
|
||||
case TimerQueryResult::NOT_READY:
|
||||
// nothing to do
|
||||
break;
|
||||
case TimerQueryResult::ERROR:
|
||||
mLast = (mLast + 1) % POOL_COUNT;
|
||||
break;
|
||||
case TimerQueryResult::AVAILABLE:
|
||||
// conversion to our duration happens here
|
||||
pFront = mQueries[mLast].pInfo;
|
||||
pFront->frameTime = std::chrono::duration<uint64_t, std::nano>(elapsed);
|
||||
mLast = (mLast + 1) % POOL_COUNT;
|
||||
denoiseFrameTime(config);
|
||||
break;
|
||||
}
|
||||
// read the pending timer queries until we find one that's not ready
|
||||
}
|
||||
|
||||
|
||||
// keep this just for debugging
|
||||
if constexpr (false) {
|
||||
using namespace utils;
|
||||
@@ -138,13 +129,15 @@ void FrameInfoManager::endFrame(DriverApi& driver) noexcept {
|
||||
mIndex = (mIndex + 1) % POOL_COUNT;
|
||||
}
|
||||
|
||||
void FrameInfoManager::denoiseFrameTime(FrameHistoryQueue& history, Config const& config) noexcept {
|
||||
void FrameInfoManager::denoiseFrameTime(Config const& config) noexcept {
|
||||
auto& history = mFrameTimeHistory;
|
||||
assert_invariant(!history.empty());
|
||||
|
||||
// find the first slot that has a valid frame duration
|
||||
size_t first = history.size();
|
||||
for (size_t i = 0, c = history.size(); i < c; ++i) {
|
||||
if (history[i].frameTime != duration(0)) {
|
||||
assert_invariant(std::addressof(history[i]) == pFront);
|
||||
first = i;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -163,16 +163,15 @@ public:
|
||||
// call this immediately before "swap buffers"
|
||||
void endFrame(backend::DriverApi& driver) noexcept;
|
||||
|
||||
details::FrameInfo getLastFrameInfo() const noexcept {
|
||||
// if pFront is not set yet, return FrameInfo(). But the `valid` field will be false in this case.
|
||||
return pFront ? *pFront : details::FrameInfo{};
|
||||
details::FrameInfo const& getLastFrameInfo() const noexcept {
|
||||
// if pFront is not set yet, return front() but in this case front().valid will be false
|
||||
return pFront ? *pFront : mFrameTimeHistory.front();
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<Renderer::FrameInfo> getFrameInfoHistory(size_t historySize) const noexcept;
|
||||
|
||||
private:
|
||||
using FrameHistoryQueue = CircularQueue<FrameInfoImpl, MAX_FRAMETIME_HISTORY>;
|
||||
static void denoiseFrameTime(FrameHistoryQueue& history, Config const& config) noexcept;
|
||||
void denoiseFrameTime(Config const& config) noexcept;
|
||||
struct Query {
|
||||
backend::Handle<backend::HwTimerQuery> handle{};
|
||||
FrameInfoImpl* pInfo = nullptr;
|
||||
@@ -181,7 +180,7 @@ private:
|
||||
uint32_t mIndex = 0; // index of current query
|
||||
uint32_t mLast = 0; // index of oldest query still active
|
||||
FrameInfoImpl* pFront = nullptr; // the most recent slot with a valid frame time
|
||||
FrameHistoryQueue mFrameTimeHistory;
|
||||
CircularQueue<FrameInfoImpl, MAX_FRAMETIME_HISTORY> mFrameTimeHistory;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -16,22 +16,17 @@
|
||||
|
||||
#include "FrameSkipper.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.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(std::clamp(latency, size_t(1), MAX_FRAME_LATENCY) - 1) {
|
||||
: mLast(latency - 1) {
|
||||
assert_invariant(latency <= MAX_FRAME_LATENCY);
|
||||
}
|
||||
|
||||
FrameSkipper::~FrameSkipper() noexcept = default;
|
||||
@@ -46,32 +41,31 @@ void FrameSkipper::terminate(DriverApi& driver) noexcept {
|
||||
|
||||
bool FrameSkipper::beginFrame(DriverApi& driver) noexcept {
|
||||
auto& fences = mDelayedFences;
|
||||
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
|
||||
auto fence = fences.front();
|
||||
if (fence) {
|
||||
auto status = driver.getFenceStatus(fence);
|
||||
if (status == FenceStatus::TIMEOUT_EXPIRED) {
|
||||
// Sync not ready, skip 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 {
|
||||
auto& fences = mDelayedFences;
|
||||
size_t const last = mLast;
|
||||
|
||||
// pop the oldest fence and advance the other ones
|
||||
if (fences.front()) {
|
||||
driver.destroyFence(fences.front());
|
||||
// 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);
|
||||
}
|
||||
std::move(fences.begin() + 1, fences.end(), fences.begin());
|
||||
|
||||
// add a new fence to the end
|
||||
assert_invariant(!fences[last]);
|
||||
|
||||
fences[last] = driver.createFence();
|
||||
fence = driver.createFence();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -22,9 +22,6 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
/*
|
||||
@@ -32,18 +29,7 @@ namespace filament {
|
||||
* outrun the GPU.
|
||||
*/
|
||||
class FrameSkipper {
|
||||
/*
|
||||
* The maximum frame latency acceptable on ANDROID is 2 because higher latencies will be
|
||||
* throttled anyway in BufferQueueProducer::dequeueBuffer(), because ANDROID is generally
|
||||
* triple-buffered no more; that case is actually pretty bad because the GL thread can block
|
||||
* anywhere (usually inside the first draw command that touches the swapchain).
|
||||
*
|
||||
* A frame latency of 1 has the benefit of reducing render latency,
|
||||
* but the drawback of preventing CPU / GPU overlap.
|
||||
*
|
||||
* Generally a frame latency of 2 is the best compromise.
|
||||
*/
|
||||
static constexpr size_t MAX_FRAME_LATENCY = 2;
|
||||
static constexpr size_t MAX_FRAME_LATENCY = 3;
|
||||
public:
|
||||
/*
|
||||
* The latency parameter defines how many unfinished frames we want to accept before we start
|
||||
@@ -74,7 +60,7 @@ public:
|
||||
private:
|
||||
using Container = std::array<backend::Handle<backend::HwFence>, MAX_FRAME_LATENCY>;
|
||||
mutable Container mDelayedFences{};
|
||||
uint8_t const mLast;
|
||||
size_t mLast;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -354,12 +354,6 @@ bool MaterialParser::getSpecularAntiAliasingThreshold(float* value) const noexce
|
||||
return mImpl.getFromSimpleChunk(ChunkType::MaterialSpecularAntiAliasingThreshold, value);
|
||||
}
|
||||
|
||||
bool MaterialParser::getStereoscopicType(backend::StereoscopicType* value) const noexcept {
|
||||
static_assert(sizeof(StereoscopicType) == sizeof(uint8_t),
|
||||
"StereoscopicType expected size is wrong");
|
||||
return mImpl.getFromSimpleChunk(ChunkType::MaterialStereoscopicType, reinterpret_cast<uint8_t*>(value));
|
||||
}
|
||||
|
||||
bool MaterialParser::getRequiredAttributes(AttributeBitset* value) const noexcept {
|
||||
uint32_t rawAttributes = 0;
|
||||
if (!mImpl.getFromSimpleChunk(ChunkType::MaterialRequiredAttributes, &rawAttributes)) {
|
||||
|
||||
@@ -122,7 +122,6 @@ public:
|
||||
bool hasSpecularAntiAliasing(bool* value) const noexcept;
|
||||
bool getSpecularAntiAliasingVariance(float* value) const noexcept;
|
||||
bool getSpecularAntiAliasingThreshold(float* value) const noexcept;
|
||||
bool getStereoscopicType(backend::StereoscopicType*) const noexcept;
|
||||
|
||||
bool getShader(filaflat::ShaderContent& shader, backend::ShaderModel shaderModel,
|
||||
Variant variant, backend::ShaderStage stage) noexcept;
|
||||
|
||||
@@ -459,12 +459,10 @@ 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::FOG);
|
||||
samplerGroup.clearSampler(PerViewSib::SHADOW_MAP);
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -494,6 +494,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
|
||||
struct StructureMipmapData {
|
||||
FrameGraphId<FrameGraphTexture> depth;
|
||||
uint32_t rt[8];
|
||||
};
|
||||
|
||||
fg.addPass<StructureMipmapData>("StructureMipmap",
|
||||
@@ -504,7 +505,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
.level = uint8_t(i)
|
||||
});
|
||||
out = builder.write(out, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
builder.declareRenderPass("Structure mip target", {
|
||||
data.rt[i - 1] = builder.declareRenderPass("Structure mip target", {
|
||||
.attachments = { .depth = out }
|
||||
});
|
||||
}
|
||||
@@ -2945,10 +2946,6 @@ 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,
|
||||
@@ -3007,8 +3004,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
|
||||
SamplerMagFilter filterMag,
|
||||
SamplerMinFilter filterMin) noexcept {
|
||||
|
||||
uint32_t const layer = fg.getSubResourceDescriptor(input).layer;
|
||||
float const levelOfDetail = fg.getSubResourceDescriptor(input).level;
|
||||
// TODO: add support for sub-resources
|
||||
assert_invariant(fg.getSubResourceDescriptor(input).layer == 0);
|
||||
assert_invariant(fg.getSubResourceDescriptor(input).level == 0);
|
||||
|
||||
struct QuadBlitData {
|
||||
FrameGraphId<FrameGraphTexture> input;
|
||||
@@ -3034,8 +3032,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
|
||||
// --------------------------------------------------------------------------------
|
||||
// set uniforms
|
||||
|
||||
PostProcessMaterial const& material =
|
||||
getPostProcessMaterial(layer ? "blitArray" : "blitLow");
|
||||
PostProcessMaterial const& material = getPostProcessMaterial("blitLow");
|
||||
auto* mi = material.getMaterialInstance(mEngine);
|
||||
mi->setParameter("color", color, {
|
||||
.filterMag = filterMag,
|
||||
@@ -3047,10 +3044,6 @@ 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);
|
||||
|
||||
@@ -3193,7 +3186,6 @@ 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);
|
||||
|
||||
+31
-41
@@ -118,11 +118,10 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
|
||||
uint32_t const customCommandCount =
|
||||
builder.mCustomCommands.has_value() ? builder.mCustomCommands->size() : 0;
|
||||
|
||||
Command* const commandBegin = builder.mArena.alloc<Command>(commandCount + customCommandCount);
|
||||
Command* commandEnd = commandBegin + (commandCount + customCommandCount);
|
||||
assert_invariant(commandBegin);
|
||||
Command* const curr = builder.mArena.alloc<Command>(commandCount + customCommandCount);
|
||||
assert_invariant(curr);
|
||||
|
||||
if (UTILS_UNLIKELY(builder.mArena.getAllocator().isHeapAllocation(commandBegin))) {
|
||||
if (UTILS_UNLIKELY(builder.mArena.getAllocator().isHeapAllocation(curr))) {
|
||||
static bool sLogOnce = true;
|
||||
if (UTILS_UNLIKELY(sLogOnce)) {
|
||||
sLogOnce = false;
|
||||
@@ -131,7 +130,10 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
|
||||
}
|
||||
}
|
||||
|
||||
appendCommands(engine, { commandBegin, commandCount },
|
||||
mCommandBegin = curr;
|
||||
mCommandEnd = curr + commandCount + customCommandCount;
|
||||
|
||||
appendCommands(engine, { curr, commandCount },
|
||||
builder.mUboHandle,
|
||||
builder.mVisibleRenderables,
|
||||
builder.mCommandTypeFlags,
|
||||
@@ -142,36 +144,32 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
|
||||
builder.mCameraForwardVector);
|
||||
|
||||
if (builder.mCustomCommands.has_value()) {
|
||||
Command* p = commandBegin + commandCount;
|
||||
Command* p = curr + commandCount;
|
||||
for (auto [channel, passId, command, order, fn]: builder.mCustomCommands.value()) {
|
||||
appendCustomCommand(p++, channel, passId, command, order, fn);
|
||||
}
|
||||
}
|
||||
|
||||
// sort commands once we're done adding commands
|
||||
commandEnd = resize(builder.mArena,
|
||||
RenderPass::sortCommands(commandBegin, commandEnd));
|
||||
sortCommands(builder.mArena);
|
||||
|
||||
if (engine.isAutomaticInstancingEnabled()) {
|
||||
int32_t stereoscopicEyeCount = 1;
|
||||
if (builder.mFlags & IS_INSTANCED_STEREOSCOPIC) {
|
||||
stereoscopicEyeCount *= engine.getConfig().stereoscopicEyeCount;
|
||||
}
|
||||
commandEnd = resize(builder.mArena,
|
||||
instanceify(engine, commandBegin, commandEnd, stereoscopicEyeCount));
|
||||
instanceify(engine, builder.mArena, stereoscopicEyeCount);
|
||||
}
|
||||
|
||||
// these are `const` from this point on...
|
||||
mCommandBegin = commandBegin;
|
||||
mCommandEnd = commandEnd;
|
||||
}
|
||||
|
||||
// this destructor is actually heavy because it inlines ~vector<>
|
||||
RenderPass::~RenderPass() noexcept = default;
|
||||
|
||||
RenderPass::Command* RenderPass::resize(Arena& arena, Command* const last) noexcept {
|
||||
arena.rewind(last);
|
||||
return last;
|
||||
void RenderPass::resize(Arena& arena, size_t count) noexcept {
|
||||
if (mCommandBegin) {
|
||||
mCommandEnd = mCommandBegin + count;
|
||||
arena.rewind(mCommandEnd);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPass::appendCommands(FEngine& engine,
|
||||
@@ -262,19 +260,18 @@ void RenderPass::appendCustomCommand(Command* commands,
|
||||
commands->key = cmd;
|
||||
}
|
||||
|
||||
RenderPass::Command* RenderPass::sortCommands(
|
||||
Command* const begin, Command* const end) noexcept {
|
||||
SYSTRACE_NAME("sort commands");
|
||||
void RenderPass::sortCommands(Arena& arena) noexcept {
|
||||
SYSTRACE_NAME("sort and trim commands");
|
||||
|
||||
std::sort(begin, end);
|
||||
std::sort(mCommandBegin, mCommandEnd);
|
||||
|
||||
// find the last command
|
||||
Command* const last = std::partition_point(begin, end,
|
||||
Command const* const last = std::partition_point(mCommandBegin, mCommandEnd,
|
||||
[](Command const& c) {
|
||||
return c.key != uint64_t(Pass::SENTINEL);
|
||||
});
|
||||
|
||||
return last;
|
||||
resize(arena, uint32_t(last - mCommandBegin));
|
||||
}
|
||||
|
||||
void RenderPass::execute(RenderPass const& pass,
|
||||
@@ -287,9 +284,7 @@ void RenderPass::execute(RenderPass const& pass,
|
||||
driver.endRenderPass();
|
||||
}
|
||||
|
||||
RenderPass::Command* RenderPass::instanceify(FEngine& engine,
|
||||
Command* curr, Command* const last,
|
||||
int32_t eyeCount) const noexcept {
|
||||
void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) noexcept {
|
||||
SYSTRACE_NAME("instanceify");
|
||||
|
||||
// instanceify works by scanning the **sorted** command stream, looking for repeat draw
|
||||
@@ -302,12 +297,14 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
|
||||
|
||||
UTILS_UNUSED uint32_t drawCallsSavedCount = 0;
|
||||
|
||||
Command* curr = mCommandBegin;
|
||||
Command* const last = mCommandEnd;
|
||||
|
||||
Command* firstSentinel = nullptr;
|
||||
PerRenderableData const* uboData = nullptr;
|
||||
PerRenderableData* stagingBuffer = nullptr;
|
||||
uint32_t stagingBufferSize = 0;
|
||||
uint32_t instancedPrimitiveOffset = 0;
|
||||
size_t const count = last - curr;
|
||||
|
||||
// TODO: for the case of instancing we could actually use 128 instead of 64 instances
|
||||
constexpr size_t maxInstanceCount = CONFIG_MAX_INSTANCES;
|
||||
@@ -346,6 +343,7 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
|
||||
if (UTILS_UNLIKELY(!stagingBuffer)) {
|
||||
|
||||
// create a temporary UBO for instancing
|
||||
size_t const count = mCommandEnd - mCommandBegin;
|
||||
mInstancedUboHandle = BufferObjectSharedHandle{
|
||||
engine.getDriverApi().createBufferObject(
|
||||
count * sizeof(PerRenderableData) + sizeof(PerRenderableUib),
|
||||
@@ -387,7 +385,7 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
|
||||
|
||||
if (UTILS_UNLIKELY(firstSentinel)) {
|
||||
//slog.d << "auto-instancing, saving " << drawCallsSavedCount << " draw calls, out of "
|
||||
// << count << io::endl;
|
||||
// << mCommandEnd - mCommandBegin << io::endl;
|
||||
|
||||
// we have instanced primitives
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
@@ -403,15 +401,14 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
|
||||
stagingBuffer = nullptr;
|
||||
|
||||
// remove all the canceled commands
|
||||
auto lastCommand = std::remove_if(firstSentinel, last, [](auto const& command) {
|
||||
auto lastCommand = std::remove_if(firstSentinel, mCommandEnd, [](auto const& command) {
|
||||
return command.key == uint64_t(Pass::SENTINEL);
|
||||
});
|
||||
|
||||
return lastCommand;
|
||||
resize(arena, uint32_t(lastCommand - mCommandBegin));
|
||||
}
|
||||
|
||||
assert_invariant(stagingBuffer == nullptr);
|
||||
return last;
|
||||
}
|
||||
|
||||
|
||||
@@ -419,8 +416,7 @@ 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, bool hasDepthClamp) noexcept {
|
||||
FMaterialInstance const* const UTILS_RESTRICT mi, bool inverseFrontFaces) noexcept {
|
||||
|
||||
FMaterial const * const UTILS_RESTRICT ma = mi->getMaterial();
|
||||
variant = Variant::filterVariant(variant, ma->isVariantLit());
|
||||
@@ -461,7 +457,6 @@ 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)
|
||||
}
|
||||
@@ -560,9 +555,6 @@ 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>();
|
||||
@@ -582,7 +574,6 @@ 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) {
|
||||
@@ -616,7 +607,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
|
||||
@@ -697,8 +688,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
cmd.info.morphingOffset = primitive.getMorphingBufferOffset();
|
||||
|
||||
if constexpr (isColorPass) {
|
||||
RenderPass::setupColorCommand(cmd, renderableVariant, mi,
|
||||
inverseFrontFaces, hasDepthClamp);
|
||||
RenderPass::setupColorCommand(cmd, renderableVariant, mi, inverseFrontFaces);
|
||||
const bool blendPass = Pass(cmd.key & PASS_MASK) == Pass::BLENDED;
|
||||
if (blendPass) {
|
||||
// TODO: at least for transparent objects, AABB should be per primitive
|
||||
|
||||
@@ -284,7 +284,6 @@ 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<
|
||||
@@ -409,16 +408,13 @@ private:
|
||||
uint8_t channel, Pass pass, CustomCommand custom, uint32_t order,
|
||||
Executor::CustomCommandFn command);
|
||||
|
||||
static Command* resize(Arena& arena, Command* const last) noexcept;
|
||||
void resize(Arena& arena, size_t count) noexcept;
|
||||
|
||||
// sorts commands then trims sentinels
|
||||
static Command* sortCommands(
|
||||
Command* begin, Command* end) noexcept;
|
||||
void sortCommands(Arena& arena) noexcept;
|
||||
|
||||
// instanceify commands then trims sentinels
|
||||
RenderPass::Command* instanceify(FEngine& engine,
|
||||
Command* begin, Command* end,
|
||||
int32_t eyeCount) const noexcept;
|
||||
void instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) noexcept;
|
||||
|
||||
// We choose the command count per job to minimize JobSystem overhead.
|
||||
static constexpr size_t JOBS_PARALLEL_FOR_COMMANDS_COUNT = 128;
|
||||
@@ -445,17 +441,17 @@ private:
|
||||
uint8_t instancedStereoEyeCount) noexcept;
|
||||
|
||||
static void setupColorCommand(Command& cmdDraw, Variant variant,
|
||||
FMaterialInstance const* mi, bool inverseFrontFaces, bool hasDepthClamp) noexcept;
|
||||
FMaterialInstance const* mi, bool inverseFrontFaces) noexcept;
|
||||
|
||||
static void updateSummedPrimitiveCounts(
|
||||
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> vr) noexcept;
|
||||
|
||||
FScene::RenderableSoa const& mRenderableSoa;
|
||||
backend::Viewport const mScissorViewport;
|
||||
Command const* /* const */ mCommandBegin = nullptr; // Pointer to the first command
|
||||
Command const* /* const */ mCommandEnd = nullptr; // Pointer to one past the last command
|
||||
Command* mCommandBegin = nullptr; // Pointer to the first command
|
||||
Command* mCommandEnd = nullptr; // Pointer to one past the last command
|
||||
// a UBO for instanced primitives
|
||||
mutable BufferObjectSharedHandle mInstancedUboHandle;
|
||||
BufferObjectSharedHandle mInstancedUboHandle;
|
||||
// a vector for our custom commands
|
||||
using CustomCommandVector = std::vector<Executor::CustomCommandFn,
|
||||
utils::STLAllocator<Executor::CustomCommandFn, LinearAllocatorArena>>;
|
||||
@@ -529,9 +525,7 @@ public:
|
||||
// like above but allows to set specific flags
|
||||
RenderPassBuilder& renderFlags(
|
||||
RenderPass::RenderFlags mask, RenderPass::RenderFlags value) noexcept {
|
||||
value &= mask;
|
||||
mFlags &= ~mask;
|
||||
mFlags |= value;
|
||||
mFlags = (mFlags & mask) | (value & mask);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,9 @@
|
||||
|
||||
#include <filament/Renderer.h>
|
||||
|
||||
#include "ResourceAllocator.h"
|
||||
#include "details/Renderer.h"
|
||||
|
||||
#include "details/Engine.h"
|
||||
#include "details/Renderer.h"
|
||||
#include "details/View.h"
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -51,9 +50,8 @@ namespace filament {
|
||||
using namespace backend;
|
||||
using namespace math;
|
||||
|
||||
RendererUtils::ColorPassOutput RendererUtils::colorPass(
|
||||
FrameGraphId<FrameGraphTexture> 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 {
|
||||
@@ -69,28 +67,31 @@ RendererUtils::ColorPassOutput 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 = 0;
|
||||
|
||||
data.color = colorPassInput.linearColor;
|
||||
data.depth = colorPassInput.depth;
|
||||
data.shadows = colorPassInput.shadows;
|
||||
data.ssao = colorPassInput.ssao;
|
||||
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
|
||||
data.ssao = blackboard.get<FrameGraphTexture>("ssao");
|
||||
data.color = blackboard.get<FrameGraphTexture>("color");
|
||||
data.depth = blackboard.get<FrameGraphTexture>("depth");
|
||||
|
||||
// Screen-space reflection or refractions
|
||||
if (config.hasScreenSpaceReflectionsOrRefractions) {
|
||||
data.ssr = colorPassInput.ssr;
|
||||
data.ssr = blackboard.get<FrameGraphTexture>("ssr");
|
||||
if (data.ssr) {
|
||||
data.ssr = builder.sample(data.ssr);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.hasContactShadows) {
|
||||
data.structure = colorPassInput.structure;
|
||||
data.structure = blackboard.get<FrameGraphTexture>("structure");
|
||||
assert_invariant(data.structure);
|
||||
data.structure = builder.sample(data.structure);
|
||||
}
|
||||
@@ -153,8 +154,6 @@ RendererUtils::ColorPassOutput 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,
|
||||
@@ -173,6 +172,9 @@ RendererUtils::ColorPassOutput 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.
|
||||
@@ -194,8 +196,9 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass(
|
||||
.stencil = data.stencil },
|
||||
.clearColor = config.clearColor,
|
||||
.samples = config.msaa,
|
||||
.layerCount = static_cast<uint8_t>(colorBufferDesc.depth),
|
||||
.layerCount = layerCount,
|
||||
.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags});
|
||||
blackboard["depth"] = data.depth;
|
||||
},
|
||||
[=, passExecutor = std::move(passExecutor), &view, &engine](FrameGraphResources const& resources,
|
||||
ColorPassData const& data, DriverApi& driver) {
|
||||
@@ -261,21 +264,24 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass(
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
.linearColor = colorPass->color,
|
||||
.tonemappedColor = colorPass->output, // can be null
|
||||
.depth = colorPass->depth
|
||||
};
|
||||
// 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;
|
||||
}
|
||||
|
||||
std::optional<RendererUtils::ColorPassOutput> RendererUtils::refractionPass(
|
||||
std::pair<FrameGraphId<FrameGraphTexture>, bool> 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) {
|
||||
@@ -290,35 +296,34 @@ std::optional<RendererUtils::ColorPassOutput> RendererUtils::refractionPass(
|
||||
|
||||
// if there wasn't any refractive object, just skip everything below.
|
||||
if (UTILS_UNLIKELY(hasScreenSpaceRefraction)) {
|
||||
assert_invariant(!colorPassInput.linearColor);
|
||||
assert_invariant(!colorPassInput.depth);
|
||||
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");
|
||||
|
||||
config.hasScreenSpaceReflectionsOrRefractions = true;
|
||||
|
||||
PostProcessManager& ppm = engine.getPostProcessManager();
|
||||
auto const opaquePassOutput = RendererUtils::colorPass(fg,
|
||||
"Color Pass (opaque)", engine, view, colorPassInput, {
|
||||
input = RendererUtils::colorPass(fg, "Color Pass (opaque)", engine, view, {
|
||||
// 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, .customResolve = false },
|
||||
}, config, { .asSubpass = false },
|
||||
pass.getExecutor(pass.begin(), refraction));
|
||||
|
||||
// generate the mipmap chain
|
||||
PostProcessManager::generateMipmapSSR(ppm, fg,
|
||||
opaquePassOutput.linearColor,
|
||||
ssrConfig.refraction,
|
||||
true, ssrConfig);
|
||||
input, 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.
|
||||
// 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;
|
||||
// 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.
|
||||
|
||||
// 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
|
||||
@@ -326,25 +331,22 @@ std::optional<RendererUtils::ColorPassOutput> 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;
|
||||
auto transparentPassOutput = RendererUtils::colorPass(fg, "Color Pass (transparent)",
|
||||
engine, view, colorPassInput, {
|
||||
output = RendererUtils::colorPass(fg, "Color Pass (transparent)", engine, view, {
|
||||
.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.
|
||||
transparentPassOutput.linearColor = ppm.resolve(fg, "Resolved Color Buffer",
|
||||
transparentPassOutput.linearColor, { .levels = 1 });
|
||||
output = ppm.resolve(fg, "Resolved Color Buffer", output, { .levels = 1 });
|
||||
}
|
||||
return transparentPassOutput;
|
||||
} else {
|
||||
output = input;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
return { output, hasScreenSpaceRefraction };
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
|
||||
@@ -21,19 +21,14 @@
|
||||
#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 {
|
||||
@@ -76,32 +71,15 @@ public:
|
||||
bool screenSpaceReflectionHistoryNotReady;
|
||||
};
|
||||
|
||||
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(
|
||||
static FrameGraphId<FrameGraphTexture> 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::optional<RendererUtils::ColorPassOutput> refractionPass(
|
||||
static std::pair<FrameGraphId<FrameGraphTexture>, bool> refractionPass(
|
||||
FrameGraph& fg, FEngine& engine, FView const& view,
|
||||
ColorPassInput colorPassInput,
|
||||
ColorPassConfig config,
|
||||
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
|
||||
PostProcessManager::ColorGradingConfig colorGradingConfig,
|
||||
|
||||
@@ -143,14 +143,12 @@ void ResourceAllocator::terminate() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
RenderTargetHandle ResourceAllocator::createRenderTarget(const char* name,
|
||||
RenderTargetHandle ResourceAllocator::createRenderTarget(const char*,
|
||||
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
|
||||
uint8_t samples, uint8_t layerCount, MRT color, TargetBufferInfo depth,
|
||||
TargetBufferInfo stencil) noexcept {
|
||||
auto handle = mBackend.createRenderTarget(targetBufferFlags,
|
||||
return 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 {
|
||||
@@ -172,9 +170,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
|
||||
@@ -192,6 +190,7 @@ 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(
|
||||
@@ -202,14 +201,12 @@ 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 });
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace filament {
|
||||
class ResourceListBase {
|
||||
public:
|
||||
using iterator = typename tsl::robin_set<void*>::iterator;
|
||||
using const_iterator = typename tsl::robin_set<void*>::const_iterator;
|
||||
|
||||
explicit ResourceListBase(const char* typeName);
|
||||
ResourceListBase(ResourceListBase&& rhs) noexcept = default;
|
||||
@@ -59,14 +58,6 @@ public:
|
||||
return mList.end();
|
||||
}
|
||||
|
||||
const_iterator begin() const noexcept {
|
||||
return mList.begin();
|
||||
}
|
||||
|
||||
const_iterator end() const noexcept {
|
||||
return mList.end();
|
||||
}
|
||||
|
||||
protected:
|
||||
void forEach(void(*f)(void* user, void *p), void* user) const noexcept;
|
||||
tsl::robin_set<void*> mList;
|
||||
|
||||
+50
-68
@@ -57,6 +57,9 @@ namespace filament {
|
||||
using namespace math;
|
||||
using namespace backend;
|
||||
|
||||
// do this only if depth-clamp is available
|
||||
static constexpr bool USE_DEPTH_CLAMP = false;
|
||||
|
||||
ShadowMap::ShadowMap(FEngine& engine) noexcept
|
||||
: mPerShadowMapUniforms(engine),
|
||||
mShadowType(ShadowType::DIRECTIONAL),
|
||||
@@ -123,8 +126,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
FScene::LightSoa const& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
ShadowMapInfo const& shadowMapInfo,
|
||||
SceneInfo const& sceneInfo,
|
||||
bool useDepthClamp) noexcept {
|
||||
SceneInfo const& sceneInfo) noexcept {
|
||||
|
||||
// reset the visible shadow status
|
||||
mHasVisibleShadows = false;
|
||||
@@ -135,20 +137,13 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
|
||||
const auto direction = lightData.elementAt<FScene::SHADOW_DIRECTION>(index);
|
||||
|
||||
auto const [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] =
|
||||
computeDirectionalShadowBounds(engine, direction, params, camera, sceneInfo,
|
||||
useDepthClamp);
|
||||
auto [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] =
|
||||
computeDirectionalShadowBounds(engine, direction, params, camera, sceneInfo);
|
||||
|
||||
if (UTILS_UNLIKELY(!visibleShadows)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// if the 2D bounds of the intersection is empty seen from the light, we have no shadow
|
||||
Aabb const bounds = compute2DBounds(Mv, lsClippedShadowVolume.data(), vertexCount);
|
||||
if (UTILS_UNLIKELY(!(bounds.min.x < bounds.max.x && bounds.min.y < bounds.max.y))) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the light's projection matrix
|
||||
* (directional/point lights, i.e. projection to use, including znear/zfar clip planes)
|
||||
@@ -166,17 +161,12 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
mat4f W, Wp, L;
|
||||
// We can't use LISPSM in stable mode
|
||||
const bool useLispsm = params.options.lispsm && !params.options.stable;
|
||||
|
||||
if (useLispsm ||
|
||||
(!params.options.stable && !engine.debug.shadowmap.disable_light_frustum_align)) {
|
||||
if (useLispsm) {
|
||||
// Orient the shadow map in the direction of the view vector by constructing a
|
||||
const float3 lsCameraFwd = Mv.upperLeft() * camera.getForwardVector();
|
||||
L = computeLightRotation(lsCameraFwd);
|
||||
LMp = L * Mp;
|
||||
LMpMv = LMp * Mv;
|
||||
}
|
||||
|
||||
if (useLispsm) {
|
||||
W = applyLISPSM(Wp, camera, params,
|
||||
LMp, Mv, LMpMv, lsClippedShadowVolume, vertexCount, direction);
|
||||
}
|
||||
@@ -194,7 +184,8 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
// In LiPSM mode, we're using the warped space here.
|
||||
float4 f = computeFocusParams(LMpMv, WLMp,
|
||||
lsClippedShadowVolume, vertexCount,
|
||||
camera, params.options.shadowFar, params.options.stable);
|
||||
camera, sceneInfo.csNearFar,
|
||||
params.options.shadowFar, params.options.stable);
|
||||
|
||||
if (params.options.stable) {
|
||||
const auto lsRef = lightData.elementAt<FScene::SHADOW_REF>(index);
|
||||
@@ -395,8 +386,7 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
|
||||
float3 const direction,
|
||||
FLightManager::ShadowParams params,
|
||||
filament::CameraInfo const& camera,
|
||||
SceneInfo const& sceneInfo,
|
||||
bool useDepthClamp) noexcept {
|
||||
SceneInfo const& sceneInfo) noexcept {
|
||||
|
||||
// we use aligned_storage<> here to avoid the default initialization of std::array<>
|
||||
using Storage = std::aligned_storage<sizeof(FrustumBoxIntersection)>::type;
|
||||
@@ -458,49 +448,36 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
|
||||
|
||||
// Compute the intersection of the view volume with the intersection of receivers and casters
|
||||
// in light space. This returns a set of points on the convex-hull of the intersection.
|
||||
mat4f const projection = camera.cullingProjection *
|
||||
(camera.view * FCamera::rigidTransformInverse(MvAtOrigin));
|
||||
size_t const vertexCount = intersectFrustumWithBox(lsClippedShadowVolume,
|
||||
projection, lsLightFrustumBounds);
|
||||
camera.cullingProjection * camera.view * FCamera::rigidTransformInverse(MvAtOrigin),
|
||||
sceneInfo.csNearFar, lsLightFrustumBounds);
|
||||
|
||||
if (UTILS_UNLIKELY(vertexCount < 4)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the scene's zmax (i.e. Near plane) and zmin (i.e. Far plane) in light space.
|
||||
* (near/far correspond to max/min because the light looks down the -z axis).
|
||||
*
|
||||
* - For the Far plane, the light frustum is already set to the closest of receivers and
|
||||
* casters (i.e.: shadow casters behind the last receivers can't cast any shadows).
|
||||
* But it can be moved closer up to the farthest point in the camera frustum & light
|
||||
* frustum intersection.
|
||||
*
|
||||
* - The Near plane is already set to the shadow casters max z (i.e. closest to the light)
|
||||
*
|
||||
* If "depth clamp" is supported, we can further tighten the near plane to the
|
||||
* shadow receiver (i.e. to the closest point of camera frustum & light frustum intersection).
|
||||
*
|
||||
* Note: L has no influence here, since we're only interested in z values
|
||||
* (L is a rotation around z)
|
||||
*/
|
||||
* Adjust the scene's zmax (i.e. Near plane) and zmin (i.e. Far plane) in light space.
|
||||
* (near/far correspond to max/min because the light looks down the -z axis).
|
||||
* - The Near plane is set to the shadow casters max z (i.e. closest to the light)
|
||||
* - The Far plane is set to the closest of the farthest shadow casters and receivers
|
||||
* i.e.: shadow casters behind the last receivers can't cast any shadows
|
||||
*
|
||||
* If "depth clamp" is supported, we can further tighten the near plane to the
|
||||
* shadow receiver.
|
||||
*
|
||||
* Note: L has no influence here, since we're only interested in z values
|
||||
* (L is a rotation around z)
|
||||
*/
|
||||
|
||||
float lsClippedShadowVolumeFarthest = std::numeric_limits<float>::max();
|
||||
float lsClippedShadowVolumeNearest = std::numeric_limits<float>::lowest();
|
||||
for (size_t i = 0; i < vertexCount; ++i) {
|
||||
float3 const v = lsClippedShadowVolume[i];
|
||||
lsClippedShadowVolumeFarthest = std::min(lsClippedShadowVolumeFarthest, v.z);
|
||||
lsClippedShadowVolumeNearest = std::max(lsClippedShadowVolumeNearest, v.z);
|
||||
// far: figure out the farthest shadow receivers
|
||||
lsLightFrustumBounds.min.z = min(lsLightFrustumBounds.min.z, v.z);
|
||||
if constexpr (USE_DEPTH_CLAMP) {
|
||||
lsLightFrustumBounds.max.z = max(lsLightFrustumBounds.max.z, v.z);
|
||||
}
|
||||
}
|
||||
|
||||
lsLightFrustumBounds.min.z =
|
||||
std::max(lsLightFrustumBounds.min.z, lsClippedShadowVolumeFarthest);
|
||||
|
||||
if (useDepthClamp) {
|
||||
lsLightFrustumBounds.max.z =
|
||||
std::min(lsLightFrustumBounds.max.z, lsClippedShadowVolumeNearest);
|
||||
}
|
||||
|
||||
if (engine.debug.shadowmap.far_uses_shadowcasters) {
|
||||
// far: closest of the farthest shadow casters and receivers
|
||||
lsLightFrustumBounds.min.z =
|
||||
@@ -634,7 +611,7 @@ math::float4 ShadowMap::computeFocusParams(
|
||||
mat4f const& LMpMv,
|
||||
mat4f const& WLMp,
|
||||
FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount,
|
||||
filament::CameraInfo const& camera,
|
||||
filament::CameraInfo const& camera, float2 const& csNearFar,
|
||||
float shadowFar, bool stable) noexcept {
|
||||
float2 s, o;
|
||||
if (stable) {
|
||||
@@ -649,7 +626,7 @@ math::float4 ShadowMap::computeFocusParams(
|
||||
return wsViewVolumeBoundingSphere;
|
||||
} else {
|
||||
mat4f const viewFromClip = inverse(camera.cullingProjection);
|
||||
Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip);
|
||||
Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip, csNearFar);
|
||||
float4 const wsViewVolumeBoundingSphere =
|
||||
computeBoundingSphere(wsFrustumVertices.vertices, 8);
|
||||
return wsViewVolumeBoundingSphere;
|
||||
@@ -817,25 +794,26 @@ Aabb ShadowMap::compute2DBounds(const mat4f& lightView,
|
||||
}
|
||||
|
||||
ShadowMap::Corners ShadowMap::computeFrustumCorners(
|
||||
mat4f const& projectionInverse) noexcept {
|
||||
mat4f const& projectionInverse,
|
||||
float2 csNearFar) noexcept {
|
||||
|
||||
Corners out;
|
||||
|
||||
// compute view frustum in world space (from its NDC)
|
||||
// matrix to convert: ndc -> camera -> world
|
||||
float const near = -1.0f;
|
||||
float const far = 1.0f;
|
||||
float const near = csNearFar.x;
|
||||
float const far = csNearFar.y;
|
||||
|
||||
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 },
|
||||
}
|
||||
};
|
||||
|
||||
@@ -917,11 +895,11 @@ void ShadowMap::snapLightFrustum(float2& s, float2& o,
|
||||
|
||||
size_t ShadowMap::intersectFrustumWithBox(
|
||||
FrustumBoxIntersection& UTILS_RESTRICT outVertices,
|
||||
mat4f const& UTILS_RESTRICT projection,
|
||||
mat4f const& UTILS_RESTRICT projection, math::float2 const& UTILS_RESTRICT csNearFar,
|
||||
Aabb const& UTILS_RESTRICT box)
|
||||
{
|
||||
Frustum const frustum{ projection };
|
||||
Corners const frustumVertices{ computeFrustumCorners(inverse(projection)) };
|
||||
Corners const frustumVertices{ computeFrustumCorners(inverse(projection), csNearFar) };
|
||||
|
||||
constexpr const float EPSILON = 1.0f / 8192.0f; // ~0.012 mm
|
||||
size_t vertexCount = 0;
|
||||
@@ -1225,11 +1203,12 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
|
||||
}
|
||||
|
||||
ShadowMap::SceneInfo::SceneInfo(
|
||||
FScene const& scene, uint8_t visibleLayers) noexcept
|
||||
: visibleLayers(visibleLayers) {
|
||||
FScene const& scene, uint8_t visibleLayers, mat4f const& viewMatrix) noexcept
|
||||
: vsNearFar(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max()),
|
||||
visibleLayers(visibleLayers) {
|
||||
|
||||
// the code below only works with affine transforms
|
||||
// Filament's API requires viewMatrix to be rigid (and thus affine).
|
||||
assert_invariant(transpose(viewMatrix)[3] == float4(0, 0, 0, 1));
|
||||
|
||||
// We assume the light is at the origin to compute the SceneInfo. This is consumed later by
|
||||
// computeShadowCameraDirectional() which takes this into account.
|
||||
@@ -1245,6 +1224,9 @@ ShadowMap::SceneInfo::SceneInfo(
|
||||
[&](Aabb receiver, Culler::result_type) {
|
||||
wsShadowReceiversVolume.min = min(wsShadowReceiversVolume.min, receiver.min);
|
||||
wsShadowReceiversVolume.max = max(wsShadowReceiversVolume.max, receiver.max);
|
||||
auto r = Aabb::transform(viewMatrix.upperLeft(), viewMatrix[3].xyz, receiver);
|
||||
vsNearFar.x = std::max(vsNearFar.x, r.max.z);
|
||||
vsNearFar.y = std::min(vsNearFar.y, r.min.z);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -109,13 +109,20 @@ public:
|
||||
struct SceneInfo {
|
||||
|
||||
SceneInfo() noexcept = default;
|
||||
SceneInfo(FScene const& scene, uint8_t visibleLayers) noexcept;
|
||||
SceneInfo(FScene const& scene, uint8_t visibleLayers, math::mat4f const& viewMatrix) noexcept;
|
||||
|
||||
// scratch data: The near and far planes, in clip space, to use for this shadow map
|
||||
math::float2 csNearFar = { -1.0f, 1.0f };
|
||||
|
||||
// scratch data: light's near/far expressed in light-space, calculated from the scene's
|
||||
// content assuming the light is at the origin.
|
||||
math::float2 lsCastersNearFar;
|
||||
math::float2 lsReceiversNearFar;
|
||||
|
||||
// Viewing camera's near/far expressed in view-space, calculated from the
|
||||
// scene's content.
|
||||
math::float2 vsNearFar;
|
||||
|
||||
// World-space shadow-casters volume
|
||||
Aabb wsShadowCastersVolume;
|
||||
|
||||
@@ -147,8 +154,7 @@ public:
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
ShadowMapInfo const& shadowMapInfo,
|
||||
SceneInfo const& sceneInfo,
|
||||
bool useDepthClamp) noexcept;
|
||||
SceneInfo const& sceneInfo) noexcept;
|
||||
|
||||
ShaderParameters updateSpot(FEngine& engine,
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
@@ -238,8 +244,7 @@ private:
|
||||
math::float3 direction,
|
||||
FLightManager::ShadowParams params,
|
||||
filament::CameraInfo const& camera,
|
||||
SceneInfo const& sceneInfo,
|
||||
bool useDepthClamp) noexcept;
|
||||
SceneInfo const& sceneInfo) noexcept;
|
||||
|
||||
static math::mat4f applyLISPSM(math::mat4f& Wp,
|
||||
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
|
||||
@@ -255,7 +260,7 @@ private:
|
||||
math::mat4f const& LMpMv,
|
||||
math::mat4f const& WLMp,
|
||||
FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount,
|
||||
filament::CameraInfo const& camera,
|
||||
filament::CameraInfo const& camera, math::float2 const& csNearFar,
|
||||
float shadowFar, bool stable) noexcept;
|
||||
|
||||
static inline void snapLightFrustum(math::float2& s, math::float2& o,
|
||||
@@ -266,7 +271,8 @@ private:
|
||||
SceneInfo const& sceneInfo,
|
||||
bool stable, bool focusShadowCasters, bool farUsesShadowCasters) noexcept;
|
||||
|
||||
static Corners computeFrustumCorners(const math::mat4f& projectionInverse) noexcept;
|
||||
static Corners computeFrustumCorners(const math::mat4f& projectionInverse,
|
||||
math::float2 csNearFar = { -1.0f, 1.0f }) noexcept;
|
||||
|
||||
static inline math::float2 computeNearFar(math::mat4f const& view,
|
||||
math::float3 const* wsVertices, size_t count) noexcept;
|
||||
@@ -301,7 +307,7 @@ private:
|
||||
|
||||
static size_t intersectFrustumWithBox(
|
||||
FrustumBoxIntersection& outVertices,
|
||||
math::mat4f const& projection,
|
||||
math::mat4f const& projection, math::float2 const& csNearFar,
|
||||
Aabb const& box);
|
||||
|
||||
static math::mat4f warpFrustum(float n, float f) noexcept;
|
||||
|
||||
@@ -39,20 +39,17 @@
|
||||
#include <backend/DriverApiForward.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/Range.h>
|
||||
#include <utils/Slice.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <math/half.h>
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec4.h>
|
||||
#include <math/scalar.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <new>
|
||||
@@ -66,15 +63,12 @@ namespace filament {
|
||||
using namespace backend;
|
||||
using namespace math;
|
||||
|
||||
ShadowMapManager::ShadowMapManager(FEngine& engine)
|
||||
: mIsDepthClampSupported(engine.getDriverApi().isDepthClampSupported()) {
|
||||
ShadowMapManager::ShadowMapManager(FEngine& engine) {
|
||||
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);
|
||||
debugRegistry.registerProperty("d.shadowmap.tightly_bound_scene",
|
||||
&engine.debug.shadowmap.tightly_bound_scene);
|
||||
}
|
||||
|
||||
ShadowMapManager::~ShadowMapManager() {
|
||||
@@ -153,7 +147,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(
|
||||
calculateTextureRequirements(engine, view, lightData);
|
||||
|
||||
// Compute scene-dependent values shared across all shadow maps
|
||||
ShadowMap::SceneInfo const info{ *view.getScene(), view.getVisibleLayers() };
|
||||
ShadowMap::SceneInfo const info{ *view.getScene(), view.getVisibleLayers(), cameraInfo.view };
|
||||
|
||||
shadowTechnique |= updateCascadeShadowMaps(
|
||||
engine, view, cameraInfo, renderableData, lightData, info);
|
||||
@@ -367,20 +361,7 @@ 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(),
|
||||
@@ -562,9 +543,65 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight);
|
||||
|
||||
// Adjust the camera's projection for the light's shadowFar
|
||||
cameraInfo.zf = params.options.shadowFar > 0.0f ? params.options.shadowFar : cameraInfo.zf;
|
||||
if (UTILS_UNLIKELY(params.options.shadowFar > 0.0f)) {
|
||||
cameraInfo.zf = params.options.shadowFar;
|
||||
updateNearFarPlanes(&cameraInfo.cullingProjection, cameraInfo.zn, cameraInfo.zf);
|
||||
float const n = cameraInfo.zn;
|
||||
float const f = cameraInfo.zf;
|
||||
|
||||
/*
|
||||
* Updating a projection matrix near and far planes:
|
||||
*
|
||||
* We assume that the near and far plane equations are of the form:
|
||||
* N = { 0, 0, 1, n }
|
||||
* F = { 0, 0, -1, -f }
|
||||
*
|
||||
* We also assume that the lower-left 2x2 of the projection is all 0:
|
||||
* P = A 0 C 0
|
||||
* 0 B D 0
|
||||
* 0 0 E F
|
||||
* 0 0 G H
|
||||
*
|
||||
* It result that we need to calculate E and F while leaving all other parameter unchanged.
|
||||
*
|
||||
* We know that:
|
||||
* with N, F the near/far normalized plane equation parameters
|
||||
* sn, sf arbitrary scale factors (they don't affect the planes)
|
||||
* m is the transpose of projection (see Frustum.cpp)
|
||||
*
|
||||
* sn * N == -m[3] - m[2]
|
||||
* sf * F == -m[3] + m[2]
|
||||
*
|
||||
* sn * N + sf * F == -2 * m[3]
|
||||
* sn * N - sf * F == -2 * m[2]
|
||||
*
|
||||
* sn * N.z + sf * F.z == -2 * m[3].z
|
||||
* sn * N.w + sf * F.w == -2 * m[3].w
|
||||
* sn * N.z - sf * F.z == -2 * m[2].z
|
||||
* sn * N.w - sf * F.w == -2 * m[2].w
|
||||
*
|
||||
* sn * N.z + sf * F.z == -2 * p[2].w
|
||||
* sn * N.w + sf * F.w == -2 * p[3].w
|
||||
* sn * N.z - sf * F.z == -2 * p[2].z
|
||||
* sn * N.w - sf * F.w == -2 * p[3].z
|
||||
*
|
||||
* We now need to solve for { p[2].z, p[3].z, sn, sf } :
|
||||
*
|
||||
* sn == -2 * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z)
|
||||
* sf == -2 * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z)
|
||||
* p[2].z == (sf * F.z - sn * N.z) / 2
|
||||
* p[3].z == (sf * F.w - sn * N.w) / 2
|
||||
*/
|
||||
auto& p = cameraInfo.cullingProjection;
|
||||
float4 const N = { 0, 0, 1, n }; // near plane equation
|
||||
float4 const F = { 0, 0, -1, -f }; // far plane equation
|
||||
// near plane equation scale factor
|
||||
float const sn = -2.0f * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z);
|
||||
// far plane equation scale factor
|
||||
float const sf = -2.0f * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z);
|
||||
// New values for the projection
|
||||
p[2].z = (sf * F.z - sn * N.z) * 0.5f;
|
||||
p[3].z = (sf * F.w - sn * N.w) * 0.5f;
|
||||
}
|
||||
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
@@ -594,10 +631,8 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
// Compute scene-dependent values shared across all cascades
|
||||
ShadowMap::updateSceneInfoDirectional(MvAtOrigin, *scene, sceneInfo);
|
||||
|
||||
// we always do culling without depth clamp, because objects behind the camera
|
||||
// must be rendered regardless
|
||||
shadowMap.updateDirectional(engine,
|
||||
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo, false);
|
||||
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo);
|
||||
|
||||
hasVisibleShadows = shadowMap.hasVisibleShadows();
|
||||
|
||||
@@ -613,7 +648,15 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
uint32_t cascadeHasVisibleShadows = 0;
|
||||
|
||||
if (hasVisibleShadows) {
|
||||
uint32_t const cascadeCount = cascadedShadowMaps.size();
|
||||
// Adjust the near and far planes to tightly bound the scene.
|
||||
float vsNear = -cameraInfo.zn;
|
||||
float vsFar = -cameraInfo.zf;
|
||||
if (engine.debug.shadowmap.tightly_bound_scene && !params.options.stable) {
|
||||
vsNear = std::min(vsNear, sceneInfo.vsNearFar.x);
|
||||
vsFar = std::max(vsFar, sceneInfo.vsNearFar.y);
|
||||
}
|
||||
|
||||
const size_t cascadeCount = cascadedShadowMaps.size();
|
||||
|
||||
// We divide the camera frustum into N cascades. This gives us N + 1 split positions.
|
||||
// The first split position is the near plane; the last split position is the far plane.
|
||||
@@ -624,8 +667,9 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
}
|
||||
|
||||
const CascadeSplits splits({
|
||||
.near = -cameraInfo.zn,
|
||||
.far = -cameraInfo.zf,
|
||||
.proj = cameraInfo.cullingProjection,
|
||||
.near = vsNear,
|
||||
.far = vsFar,
|
||||
.cascadeCount = cascadeCount,
|
||||
.splitPositions = splitPercentages
|
||||
});
|
||||
@@ -635,7 +679,10 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
static_assert(CONFIG_MAX_SHADOW_CASCADES <= 5,
|
||||
"At most, a float4 can fit 4 split positions for 5 shadow cascades");
|
||||
float4 wsSplitPositionUniform{ -std::numeric_limits<float>::infinity() };
|
||||
std::copy(splits.begin() + 1, splits.end(), &wsSplitPositionUniform[0]);
|
||||
std::copy(splits.beginWs() + 1, splits.endWs(), &wsSplitPositionUniform[0]);
|
||||
|
||||
float csSplitPosition[CONFIG_MAX_SHADOW_CASCADES + 1];
|
||||
std::copy(splits.beginCs(), splits.endCs(), csSplitPosition);
|
||||
|
||||
mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform;
|
||||
|
||||
@@ -648,20 +695,10 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
ShadowMap& shadowMap = cascadedShadowMaps[i];
|
||||
assert_invariant(shadowMap.getLightIndex() == 0);
|
||||
|
||||
// update cameraInfo culling projection for the cascade
|
||||
float const* nearFarPlanes = splits.begin();
|
||||
cameraInfo.zn = -nearFarPlanes[i];
|
||||
cameraInfo.zf = -nearFarPlanes[i + 1];
|
||||
updateNearFarPlanes(&cameraInfo.cullingProjection, cameraInfo.zn, cameraInfo.zf);
|
||||
|
||||
bool const canUseDepthClamp =
|
||||
!view.hasVSM() &&
|
||||
mIsDepthClampSupported &&
|
||||
engine.debug.shadowmap.depth_clamp;
|
||||
sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] };
|
||||
|
||||
auto shaderParameters = shadowMap.updateDirectional(engine,
|
||||
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo,
|
||||
canUseDepthClamp);
|
||||
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo);
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
const size_t shadowIndex = shadowMap.getShadowIndex();
|
||||
@@ -1032,78 +1069,8 @@ ShadowMapManager::CascadeSplits::CascadeSplits(Params const& params) noexcept
|
||||
: mSplitCount(params.cascadeCount + 1) {
|
||||
for (size_t s = 0; s < mSplitCount; s++) {
|
||||
mSplitsWs[s] = params.near + (params.far - params.near) * params.splitPositions[s];
|
||||
mSplitsCs[s] = mat4f::project(params.proj, float3(0.0f, 0.0f, mSplitsWs[s])).z;
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMapManager::updateNearFarPlanes(mat4f* projection,
|
||||
float nearDistance, float farDistance) noexcept {
|
||||
float const n = nearDistance;
|
||||
float const f = farDistance;
|
||||
|
||||
/*
|
||||
* Updating a projection matrix near and far planes:
|
||||
*
|
||||
* We assume that the near and far plane equations are of the form:
|
||||
* N = { 0, 0, 1, n }
|
||||
* F = { 0, 0, -1, -f }
|
||||
*
|
||||
* We also assume that the lower-left 2x2 of the projection is all 0:
|
||||
* P = A 0 C 0
|
||||
* 0 B D 0
|
||||
* 0 0 E F
|
||||
* 0 0 G H
|
||||
*
|
||||
* It result that we need to calculate E and F while leaving all other parameter unchanged.
|
||||
*
|
||||
* We know that:
|
||||
* with N, F the near/far normalized plane equation parameters
|
||||
* sn, sf arbitrary scale factors (they don't affect the planes)
|
||||
* m is the transpose of projection (see Frustum.cpp)
|
||||
*
|
||||
* sn * N == -m[3] - m[2]
|
||||
* sf * F == -m[3] + m[2]
|
||||
*
|
||||
* sn * N + sf * F == -2 * m[3]
|
||||
* sn * N - sf * F == -2 * m[2]
|
||||
*
|
||||
* sn * N.z + sf * F.z == -2 * m[3].z
|
||||
* sn * N.w + sf * F.w == -2 * m[3].w
|
||||
* sn * N.z - sf * F.z == -2 * m[2].z
|
||||
* sn * N.w - sf * F.w == -2 * m[2].w
|
||||
*
|
||||
* sn * N.z + sf * F.z == -2 * p[2].w
|
||||
* sn * N.w + sf * F.w == -2 * p[3].w
|
||||
* sn * N.z - sf * F.z == -2 * p[2].z
|
||||
* sn * N.w - sf * F.w == -2 * p[3].z
|
||||
*
|
||||
* We now need to solve for { p[2].z, p[3].z, sn, sf } :
|
||||
*
|
||||
* sn == -2 * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z)
|
||||
* sf == -2 * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z)
|
||||
* p[2].z == (sf * F.z - sn * N.z) / 2
|
||||
* p[3].z == (sf * F.w - sn * N.w) / 2
|
||||
*/
|
||||
auto& p = *projection;
|
||||
float4 const N = { 0, 0, 1, n }; // near plane equation
|
||||
float4 const F = { 0, 0, -1, -f }; // far plane equation
|
||||
// near plane equation scale factor
|
||||
float const sn = -2.0f * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z);
|
||||
// far plane equation scale factor
|
||||
float const sf = -2.0f * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z);
|
||||
// New values for the projection
|
||||
p[2].z = (sf * F.z - sn * N.z) * 0.5f;
|
||||
p[3].z = (sf * F.w - sn * N.w) * 0.5f;
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<Camera const*>
|
||||
ShadowMapManager::getDirectionalShadowCameras() const noexcept {
|
||||
if (!mInitialized) return {};
|
||||
auto const csm = getCascadedShadowMap();
|
||||
auto result = utils::FixedCapacityVector<Camera const*>::with_capacity(csm.size());
|
||||
for (ShadowMap const& sm : csm) {
|
||||
result.push_back(sm.hasVisibleShadows() ? sm.getDebugCamera() : nullptr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Range.h>
|
||||
#include <utils/Slice.h>
|
||||
@@ -59,7 +58,6 @@
|
||||
|
||||
namespace filament {
|
||||
|
||||
class Camera;
|
||||
class FCamera;
|
||||
class FView;
|
||||
class FrameGraph;
|
||||
@@ -140,16 +138,16 @@ public:
|
||||
bool hasSpotShadows() const { return !mSpotShadowMapCount; }
|
||||
|
||||
// for debugging only
|
||||
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept;
|
||||
FCamera const* getDirectionalShadowCamera() const noexcept {
|
||||
if (!mInitialized) return nullptr;
|
||||
return getShadowMap(0).getDebugCamera();
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ShadowMapManager(FEngine& engine);
|
||||
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
static void updateNearFarPlanes(math::mat4f* projection,
|
||||
float nearDistance, float farDistance) noexcept;
|
||||
|
||||
ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
|
||||
FView& view, CameraInfo cameraInfo, FScene::RenderableSoa& renderableData,
|
||||
FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept;
|
||||
@@ -188,21 +186,27 @@ private:
|
||||
constexpr static size_t SPLIT_COUNT = CONFIG_MAX_SHADOW_CASCADES + 1;
|
||||
|
||||
struct Params {
|
||||
math::mat4f proj;
|
||||
float near = 0.0f;
|
||||
float far = 0.0f;
|
||||
uint32_t cascadeCount = 1;
|
||||
size_t cascadeCount = 1;
|
||||
std::array<float, SPLIT_COUNT> splitPositions = { 0.0f };
|
||||
};
|
||||
|
||||
explicit CascadeSplits(Params const& params) noexcept;
|
||||
|
||||
// Split positions in world-space.
|
||||
const float* begin() const { return mSplitsWs; }
|
||||
const float* end() const { return mSplitsWs + mSplitCount; }
|
||||
const float* beginWs() const { return mSplitsWs; }
|
||||
const float* endWs() const { return mSplitsWs + mSplitCount; }
|
||||
|
||||
// Split positions in clip-space.
|
||||
const float* beginCs() const { return mSplitsCs; }
|
||||
const float* endCs() const { return mSplitsCs + mSplitCount; }
|
||||
|
||||
private:
|
||||
float mSplitsWs[SPLIT_COUNT];
|
||||
uint32_t mSplitCount;
|
||||
float mSplitsCs[SPLIT_COUNT];
|
||||
size_t mSplitCount;
|
||||
};
|
||||
|
||||
// Atlas requirements, updated in ShadowMapManager::update(),
|
||||
@@ -232,7 +236,6 @@ private:
|
||||
ShadowMapCacheContainer mShadowMapCache;
|
||||
uint32_t mDirectionalShadowMapCount = 0;
|
||||
uint32_t mSpotShadowMapCount = 0;
|
||||
bool const mIsDepthClampSupported;
|
||||
bool mInitialized = false;
|
||||
|
||||
ShadowMap& getShadowMap(size_t index) noexcept {
|
||||
@@ -245,14 +248,10 @@ private:
|
||||
}
|
||||
|
||||
utils::Slice<ShadowMap> getCascadedShadowMap() noexcept {
|
||||
ShadowMap const* const p = &getShadowMap(0);
|
||||
ShadowMap* const p = &getShadowMap(0);
|
||||
return { p, mDirectionalShadowMapCount };
|
||||
}
|
||||
|
||||
utils::Slice<ShadowMap> getCascadedShadowMap() const noexcept {
|
||||
return const_cast<ShadowMapManager*>(this)->getCascadedShadowMap();
|
||||
}
|
||||
|
||||
utils::Slice<ShadowMap> getSpotShadowMaps() noexcept {
|
||||
ShadowMap* const p = &getShadowMap(CONFIG_MAX_SHADOW_CASCADES);
|
||||
return { p, mSpotShadowMapCount };
|
||||
|
||||
@@ -73,8 +73,8 @@ const char* View::getName() const noexcept {
|
||||
return downcast(this)->getName();
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<Camera const*> View::getDirectionalShadowCameras() const noexcept {
|
||||
return downcast(this)->getDirectionalShadowCameras();
|
||||
Camera const* View::getDirectionalShadowCamera() const noexcept {
|
||||
return downcast(this)->getDirectionalShadowCamera();
|
||||
}
|
||||
|
||||
void View::setShadowingEnabled(bool enabled) noexcept {
|
||||
@@ -193,10 +193,6 @@ void View::setShadowType(View::ShadowType shadow) noexcept {
|
||||
downcast(this)->setShadowType(shadow);
|
||||
}
|
||||
|
||||
View::ShadowType View::getShadowType() const noexcept {
|
||||
return downcast(this)->getShadowType();
|
||||
}
|
||||
|
||||
void View::setVsmShadowOptions(VsmShadowOptions const& options) noexcept {
|
||||
downcast(this)->setVsmShadowOptions(options);
|
||||
}
|
||||
|
||||
@@ -588,9 +588,6 @@ void FRenderableManager::create(
|
||||
// full size of the UBO.
|
||||
instances.handle = driver.createBufferObject(sizeof(PerRenderableUib),
|
||||
BufferObjectBinding::UNIFORM, backend::BufferUsage::DYNAMIC);
|
||||
if (auto name = instances.buffer->getName(); !name.empty()) {
|
||||
driver.setDebugTag(instances.handle.getId(), std::move(name));
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t boneCount = builder->mSkinningBoneCount;
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
|
||||
#include "FilamentAPI-impl.h"
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
struct BufferObject::BuilderDetails {
|
||||
@@ -58,9 +56,6 @@ FBufferObject::FBufferObject(FEngine& engine, const BufferObject::Builder& build
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
mHandle = driver.createBufferObject(builder->mByteCount, builder->mBindingType,
|
||||
backend::BufferUsage::STATIC);
|
||||
if (auto name = builder.getName(); !name.empty()) {
|
||||
driver.setDebugTag(mHandle.getId(), std::move(name));
|
||||
}
|
||||
}
|
||||
|
||||
void FBufferObject::terminate(FEngine& engine) {
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
#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;
|
||||
|
||||
@@ -73,15 +79,17 @@ bool FDebugRegistry::hasProperty(const char* name) const noexcept {
|
||||
|
||||
template<typename T>
|
||||
bool FDebugRegistry::setProperty(const char* name, T v) noexcept {
|
||||
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();
|
||||
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;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -110,32 +118,19 @@ template bool FDebugRegistry::getProperty<float2>(const char* name, float2* v) c
|
||||
template bool FDebugRegistry::getProperty<float3>(const char* name, float3* v) const noexcept;
|
||||
template bool FDebugRegistry::getProperty<float4>(const char* name, float4* v) const noexcept;
|
||||
|
||||
bool FDebugRegistry::registerDataSource(std::string_view name,
|
||||
void FDebugRegistry::registerDataSource(std::string_view name,
|
||||
void const* data, size_t count) noexcept {
|
||||
auto& dataSourceMap = mDataSourceMap;
|
||||
bool const found = dataSourceMap.find(name) == dataSourceMap.end();
|
||||
if (found) {
|
||||
if (dataSourceMap.find(name) == dataSourceMap.end()) {
|
||||
dataSourceMap[name] = { data, count };
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
bool FDebugRegistry::registerDataSource(std::string_view name,
|
||||
void FDebugRegistry::registerDataSource(std::string_view name,
|
||||
utils::Invocable<DataSource()>&& creator) noexcept {
|
||||
auto& dataSourceCreatorMap = mDataSourceCreatorMap;
|
||||
bool const found = dataSourceCreatorMap.find(name) == dataSourceCreatorMap.end();
|
||||
if (found) {
|
||||
dataSourceCreatorMap[name] = std::move(creator);
|
||||
}
|
||||
return found;
|
||||
mDataSourceCreatorMap[name] = std::move(creator);
|
||||
}
|
||||
|
||||
void FDebugRegistry::unregisterDataSource(std::string_view name) noexcept {
|
||||
mDataSourceCreatorMap.erase(name);
|
||||
mDataSourceMap.erase(name);
|
||||
}
|
||||
|
||||
|
||||
DebugRegistry::DataSource FDebugRegistry::getDataSource(const char* name) const noexcept {
|
||||
std::string_view const key{ name };
|
||||
auto& dataSourceMap = mDataSourceMap;
|
||||
|
||||
@@ -101,14 +101,12 @@ public:
|
||||
}
|
||||
|
||||
// registers a DataSource directly
|
||||
bool registerDataSource(std::string_view name, void const* data, size_t count) noexcept;
|
||||
void registerDataSource(std::string_view name, void const* data, size_t count) noexcept;
|
||||
|
||||
// registers a DataSource lazily
|
||||
bool registerDataSource(std::string_view name,
|
||||
void registerDataSource(std::string_view name,
|
||||
utils::Invocable<DataSource()>&& creator) noexcept;
|
||||
|
||||
void unregisterDataSource(std::string_view name) noexcept;
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
private:
|
||||
#endif
|
||||
|
||||
@@ -71,7 +71,7 @@ struct Engine::BuilderDetails {
|
||||
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
|
||||
void* mSharedContext = nullptr;
|
||||
bool mPaused = false;
|
||||
static Config validateConfig(Config config) noexcept;
|
||||
static Config validateConfig(const Config* pConfig) noexcept;
|
||||
};
|
||||
|
||||
Engine* FEngine::create(Engine::Builder const& builder) {
|
||||
@@ -1197,22 +1197,6 @@ bool FEngine::isValid(const FInstanceBuffer* p) const {
|
||||
return isValid(p, mInstanceBuffers);
|
||||
}
|
||||
|
||||
size_t FEngine::getBufferObjectCount() const noexcept { return mBufferObjects.size(); }
|
||||
size_t FEngine::getViewCount() const noexcept { return mViews.size(); }
|
||||
size_t FEngine::getSceneCount() const noexcept { return mScenes.size(); }
|
||||
size_t FEngine::getSwapChainCount() const noexcept { return mSwapChains.size(); }
|
||||
size_t FEngine::getStreamCount() const noexcept { return mStreams.size(); }
|
||||
size_t FEngine::getIndexBufferCount() const noexcept { return mIndexBuffers.size(); }
|
||||
size_t FEngine::getSkinningBufferCount() const noexcept { return mSkinningBuffers.size(); }
|
||||
size_t FEngine::getMorphTargetBufferCount() const noexcept { return mMorphTargetBuffers.size(); }
|
||||
size_t FEngine::getInstanceBufferCount() const noexcept { return mInstanceBuffers.size(); }
|
||||
size_t FEngine::getVertexBufferCount() const noexcept { return mVertexBuffers.size(); }
|
||||
size_t FEngine::getIndirectLightCount() const noexcept { return mIndirectLights.size(); }
|
||||
size_t FEngine::getMaterialCount() const noexcept { return mMaterials.size(); }
|
||||
size_t FEngine::getTextureCount() const noexcept { return mTextures.size(); }
|
||||
size_t FEngine::getSkyboxeCount() const noexcept { return mSkyboxes.size(); }
|
||||
size_t FEngine::getColorGradingCount() const noexcept { return mColorGradings.size(); }
|
||||
size_t FEngine::getRenderTargetCount() const noexcept { return mRenderTargets.size(); }
|
||||
|
||||
void* FEngine::streamAlloc(size_t size, size_t alignment) noexcept {
|
||||
// we allow this only for small allocations
|
||||
@@ -1302,7 +1286,7 @@ Engine::Builder& Engine::Builder::platform(Platform* platform) noexcept {
|
||||
}
|
||||
|
||||
Engine::Builder& Engine::Builder::config(Engine::Config const* config) noexcept {
|
||||
mImpl->mConfig = config ? *config : Engine::Config{};
|
||||
mImpl->mConfig = BuilderDetails::validateConfig(config);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -1330,15 +1314,22 @@ void Engine::Builder::build(Invocable<void(void*)>&& callback) const {
|
||||
#endif
|
||||
|
||||
Engine* Engine::Builder::build() const {
|
||||
mImpl->mConfig = BuilderDetails::validateConfig(mImpl->mConfig);
|
||||
return FEngine::create(*this);
|
||||
}
|
||||
|
||||
Engine::Config Engine::BuilderDetails::validateConfig(Config config) noexcept {
|
||||
Engine::Config Engine::BuilderDetails::validateConfig(const Config* const pConfig) noexcept {
|
||||
// Rule of thumb: perRenderPassArenaMB must be roughly 1 MB larger than perFrameCommandsMB
|
||||
constexpr uint32_t COMMAND_ARENA_OVERHEAD = 1;
|
||||
constexpr uint32_t CONCURRENT_FRAME_COUNT = 3;
|
||||
|
||||
Config config;
|
||||
if (!pConfig) {
|
||||
return config;
|
||||
}
|
||||
|
||||
// make sure to copy all the fields
|
||||
config = *pConfig;
|
||||
|
||||
// Use at least the defaults set by the build system
|
||||
config.minCommandBufferSizeMB = std::max(
|
||||
config.minCommandBufferSizeMB,
|
||||
|
||||
@@ -351,23 +351,6 @@ public:
|
||||
bool isValid(const FView* p) const;
|
||||
bool isValid(const FInstanceBuffer* p) const;
|
||||
|
||||
size_t getBufferObjectCount() const noexcept;
|
||||
size_t getViewCount() const noexcept;
|
||||
size_t getSceneCount() const noexcept;
|
||||
size_t getSwapChainCount() const noexcept;
|
||||
size_t getStreamCount() const noexcept;
|
||||
size_t getIndexBufferCount() const noexcept;
|
||||
size_t getSkinningBufferCount() const noexcept;
|
||||
size_t getMorphTargetBufferCount() const noexcept;
|
||||
size_t getInstanceBufferCount() const noexcept;
|
||||
size_t getVertexBufferCount() const noexcept;
|
||||
size_t getIndirectLightCount() const noexcept;
|
||||
size_t getMaterialCount() const noexcept;
|
||||
size_t getTextureCount() const noexcept;
|
||||
size_t getSkyboxeCount() const noexcept;
|
||||
size_t getColorGradingCount() const noexcept;
|
||||
size_t getRenderTargetCount() const noexcept;
|
||||
|
||||
void destroy(utils::Entity e);
|
||||
|
||||
bool isPaused() const noexcept;
|
||||
@@ -590,8 +573,7 @@ private:
|
||||
Config mConfig;
|
||||
|
||||
public:
|
||||
// These are the debug properties used by FDebug.
|
||||
// They're accessed directly by modules who need them.
|
||||
// these are the debug properties used by FDebug. They're accessed directly by modules who need them.
|
||||
struct {
|
||||
struct {
|
||||
bool debug_directional_shadowmap = false;
|
||||
@@ -599,8 +581,7 @@ public:
|
||||
bool far_uses_shadowcasters = true;
|
||||
bool focus_shadowcasters = true;
|
||||
bool visualize_cascades = false;
|
||||
bool disable_light_frustum_align = false;
|
||||
bool depth_clamp = true;
|
||||
bool tightly_bound_scene = true;
|
||||
float dzn = -1.0f;
|
||||
float dzf = 1.0f;
|
||||
float display_shadow_texture_scale = 0.25f;
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
|
||||
#include "FilamentAPI-impl.h"
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
struct IndexBuffer::BuilderDetails {
|
||||
@@ -60,9 +58,6 @@ FIndexBuffer::FIndexBuffer(FEngine& engine, const IndexBuffer::Builder& builder)
|
||||
(backend::ElementType)builder->mIndexType,
|
||||
uint32_t(builder->mIndexCount),
|
||||
backend::BufferUsage::STATIC);
|
||||
if (auto name = builder.getName(); !name.empty()) {
|
||||
driver.setDebugTag(mHandle.getId(), std::move(name));
|
||||
}
|
||||
}
|
||||
|
||||
void FIndexBuffer::terminate(FEngine& engine) {
|
||||
|
||||
@@ -61,8 +61,7 @@ InstanceBuffer* InstanceBuffer::Builder::build(Engine& engine) {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
FInstanceBuffer::FInstanceBuffer(FEngine& engine, const Builder& builder)
|
||||
: mName(builder.getName()) {
|
||||
FInstanceBuffer::FInstanceBuffer(FEngine& engine, const Builder& builder) {
|
||||
mInstanceCount = builder->mInstanceCount;
|
||||
|
||||
mLocalTransforms.reserve(mInstanceCount);
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
namespace filament {
|
||||
@@ -47,13 +46,10 @@ public:
|
||||
void prepare(FEngine& engine, math::mat4f rootTransform, const PerRenderableData& ubo,
|
||||
backend::Handle<backend::HwBufferObject> handle);
|
||||
|
||||
utils::CString const& getName() const noexcept { return mName; }
|
||||
|
||||
private:
|
||||
friend class RenderableManager;
|
||||
|
||||
utils::FixedCapacityVector<math::mat4f> mLocalTransforms;
|
||||
utils::CString mName;
|
||||
size_t mInstanceCount;
|
||||
};
|
||||
|
||||
|
||||
@@ -192,30 +192,6 @@ 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);
|
||||
materialParser->getMaterialVariantFilterMask(&variantFilterMask);
|
||||
bool const hasStereoVariants = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::STE));
|
||||
if (materialDomain == MaterialDomain::SURFACE && hasStereoVariants) {
|
||||
StereoscopicType const engineStereoscopicType = engine.getConfig().stereoscopicType;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return downcast(engine).createMaterial(*this, std::move(materialParser));
|
||||
}
|
||||
|
||||
@@ -224,7 +200,8 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
|
||||
: mIsDefaultMaterial(builder->mDefaultMaterial),
|
||||
mEngine(engine),
|
||||
mMaterialId(engine.getMaterialId()),
|
||||
mMaterialParser(std::move(materialParser)) {
|
||||
mMaterialParser(std::move(materialParser))
|
||||
{
|
||||
MaterialParser* const parser = mMaterialParser.get();
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE bool const nameOk = parser->getName(&mName);
|
||||
@@ -618,7 +595,6 @@ Program FMaterial::getProgramWithVariants(
|
||||
|
||||
void FMaterial::createAndCacheProgram(Program&& p, Variant variant) const noexcept {
|
||||
auto program = mEngine.getDriverApi().createProgram(std::move(p));
|
||||
mEngine.getDriverApi().setDebugTag(program.getId(), mName);
|
||||
assert_invariant(program);
|
||||
mCachedPrograms[variant.key] = program;
|
||||
}
|
||||
@@ -897,7 +873,7 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Material::Builde
|
||||
engine.getDriverApi().getMaxUniformBufferSize() / 16u);
|
||||
|
||||
bool const staticTextureWorkaround =
|
||||
engine.getDriverApi().isWorkaroundNeeded(Workaround::METAL_STATIC_TEXTURE_TARGET_ERROR);
|
||||
engine.getDriverApi().isWorkaroundNeeded(Workaround::A8X_STATIC_TEXTURE_TARGET_ERROR);
|
||||
|
||||
bool const powerVrShaderWorkarounds =
|
||||
engine.getDriverApi().isWorkaroundNeeded(Workaround::POWER_VR_SHADER_WORKAROUNDS);
|
||||
|
||||
@@ -51,7 +51,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material)
|
||||
mUniforms = UniformBuffer(material->getUniformInterfaceBlock().getSize());
|
||||
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
|
||||
BufferObjectBinding::UNIFORM, backend::BufferUsage::STATIC);
|
||||
driver.setDebugTag(mUbHandle.getId(), material->getName());
|
||||
}
|
||||
|
||||
if (!material->getSamplerInterfaceBlock().isEmpty()) {
|
||||
@@ -117,7 +116,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
|
||||
mUniforms.setUniforms(other->getUniformBuffer());
|
||||
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
|
||||
BufferObjectBinding::UNIFORM, backend::BufferUsage::DYNAMIC);
|
||||
driver.setDebugTag(mUbHandle.getId(), material->getName());
|
||||
}
|
||||
|
||||
if (!material->getSamplerInterfaceBlock().isEmpty()) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user