Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84cecd334b | ||
|
|
bb6ebd3aea | ||
|
|
1aab0585da | ||
|
|
1d1e15d915 | ||
|
|
ff394f7c1b | ||
|
|
161b75b9b7 | ||
|
|
bc12a56920 | ||
|
|
15bb295ec6 | ||
|
|
cea178a40c | ||
|
|
f34bb3d775 | ||
|
|
30112b5b5b | ||
|
|
9aaa1bf413 | ||
|
|
0471c5de2f | ||
|
|
65cca71abf | ||
|
|
e3384feee0 | ||
|
|
3650556de4 | ||
|
|
679b08b5db | ||
|
|
5fd5cd270c | ||
|
|
275ffb409f |
@@ -0,0 +1,23 @@
|
||||
name: 'Get and cache glTF Assets'
|
||||
description: 'Downloads and caches glTF assets by calling the get-gltf-sample-assets.sh script.'
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: ./.github/actions/dep-versions
|
||||
- name: Hash models file
|
||||
id: hash-models
|
||||
shell: bash
|
||||
run: echo "hash=$(cat test/renderdiff/tests/gltf_models.txt | md5sum | sed 's/ -//g')" >> $GITHUB_OUTPUT
|
||||
- name: Cache glTF assets
|
||||
id: cache-gltf
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: gltf
|
||||
key: gltf-assets-${{ env.GITHUB_GLTF_SAMPLE_ASSETS_COMMIT }}-${{ steps.hash-models.outputs.hash }}
|
||||
- name: Download assets via script if cache not found
|
||||
if: steps.cache-gltf.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Cache miss for commit ${{ env.GITHUB_GLTF_SAMPLE_ASSETS_COMMIT }}. Running download script..."
|
||||
xargs bash build/common/get-gltf-sample-assets.sh < test/renderdiff/tests/gltf_models.txt
|
||||
@@ -1,5 +1,6 @@
|
||||
name: 'Get Mesa'
|
||||
description: 'Caches and installs Mesa'
|
||||
name: 'Get and cache Mesa'
|
||||
description: 'Get and cache Mesa'
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
|
||||
@@ -19,5 +19,7 @@ runs:
|
||||
- name: Install Mac Prerequisites
|
||||
shell: bash
|
||||
run: |
|
||||
# Install brew prereqs
|
||||
brew install coreutils
|
||||
# Install ninja
|
||||
source ./build/common/get-ninja.sh
|
||||
|
||||
@@ -118,11 +118,12 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- uses: ./.github/actions/get-gltf-assets
|
||||
- uses: ./.github/actions/get-mesa
|
||||
- uses: ./.github/actions/get-vulkan-sdk
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- uses: ./.github/actions/get-mesa
|
||||
- uses: ./.github/actions/get-vulkan-sdk
|
||||
- name: Prerequisites
|
||||
run: |
|
||||
pip install tifffile numpy
|
||||
@@ -132,6 +133,7 @@ jobs:
|
||||
- name: Render and compare
|
||||
id: render_compare
|
||||
run: |
|
||||
ls ./gltf/Models
|
||||
TEST_DIR=test/renderdiff
|
||||
source ${TEST_DIR}/src/preamble.sh
|
||||
start_
|
||||
|
||||
@@ -7,3 +7,6 @@ We are chaning the way Vulkan buffers are handled. We need to switch over to a m
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- Rename `sampler` parameter `unfilterable` to `filterable` [⚠️ **New Material Version**]
|
||||
- materials: new mutable specialization constants feature. See the [materials documentation](https://google.github.io/filament/Materials.html) for details. [⚠️ **New Material Version**]
|
||||
|
||||
@@ -60,6 +60,14 @@ static void setParameter(JNIEnv* env, jlong nativeMaterialInstance, jstring name
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void setConstant(JNIEnv* env, jlong nativeMaterialInstance, jstring name_, T v) {
|
||||
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
|
||||
const char *name = env->GetStringUTFChars(name_, 0);
|
||||
instance->setConstant(name, v);
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetParameterBool(JNIEnv *env, jclass,
|
||||
@@ -264,6 +272,13 @@ Java_com_google_android_filament_MaterialInstance_nSetParameterTexture(
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetConstantBool(JNIEnv *env, jclass,
|
||||
jlong nativeMaterialInstance, jstring name_, jboolean x) {
|
||||
setConstant(env, nativeMaterialInstance, name_, bool(x));
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetScissor(
|
||||
|
||||
+13
@@ -402,6 +402,16 @@ public class MaterialInstance {
|
||||
nSetParameterFloat4(getNativeObject(), name, color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a bool constant.
|
||||
*
|
||||
* @param name the name of the material constant
|
||||
* @param x the value of the material constant
|
||||
*/
|
||||
public void setConstant(@NonNull String name, boolean x) {
|
||||
nSetConstantBool(getNativeObject(), name, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set-up a custom scissor rectangle; by default it is disabled.
|
||||
*
|
||||
@@ -921,6 +931,9 @@ public class MaterialInstance {
|
||||
@NonNull String name, int element, @NonNull @Size(min = 1) float[] v,
|
||||
@IntRange(from = 0) int offset, @IntRange(from = 1) int count);
|
||||
|
||||
private static native void nSetConstantBool(long nativeMaterialInstance,
|
||||
@NonNull String name, boolean x);
|
||||
|
||||
private static native void nSetParameterTexture(long nativeMaterialInstance,
|
||||
@NonNull String name, long nativeTexture, long sampler);
|
||||
|
||||
|
||||
@@ -1949,6 +1949,18 @@ public class View {
|
||||
* Ground Truth-base Ambient Occlusion (GTAO) options
|
||||
*/
|
||||
public float gtaoThicknessHeuristic = 0.004f;
|
||||
/**
|
||||
* Ground Truth-base Ambient Occlusion (GTAO) options
|
||||
*/
|
||||
public boolean gtaoUseVisibilityBitmasks = false;
|
||||
/**
|
||||
* Ground Truth-base Ambient Occlusion (GTAO) options
|
||||
*/
|
||||
public float gtaoConstThickness = 0.5f;
|
||||
/**
|
||||
* Ground Truth-base Ambient Occlusion (GTAO) options
|
||||
*/
|
||||
public boolean gtaoLinearThickness = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# Copyright (C) 2025 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.
|
||||
|
||||
#!/usr/bin/bash
|
||||
set -e
|
||||
|
||||
GLTF_SAMPLE_ASSETS_COMMIT=${GITHUB_GLTF_SAMPLE_ASSETS_COMMIT:-d441dfdb87413ff412c620849a649d61789a470f}
|
||||
COMMIT_HASH="${GLTF_SAMPLE_ASSETS_COMMIT}"
|
||||
REPO_URL="https://github.com/KhronosGroup/glTF-Sample-Assets.git"
|
||||
TARGET_DIR="gltf"
|
||||
|
||||
# The default directories to check out if none are specified
|
||||
DEFAULT_SPARSE_PATHS=(
|
||||
"Models/Box/"
|
||||
"Models/Triangle/"
|
||||
"Models/AnimatedCube/"
|
||||
)
|
||||
|
||||
# Check if command-line arguments are provided
|
||||
if [ "$#" -gt 0 ]; then
|
||||
# If arguments are provided, use them as the paths
|
||||
SPARSE_PATHS=()
|
||||
for model_name in "$@"; do
|
||||
SPARSE_PATHS+=("Models/${model_name}/")
|
||||
done
|
||||
echo "Downloading specified models: $@"
|
||||
else
|
||||
# Otherwise, use the default list
|
||||
SPARSE_PATHS=("${DEFAULT_SPARSE_PATHS[@]}")
|
||||
echo "No models specified, downloading default set."
|
||||
fi
|
||||
|
||||
echo "Removing old directory..."
|
||||
rm -rf "${TARGET_DIR}"
|
||||
|
||||
# Clone the repository using a "treeless" clone, which is highly efficient.
|
||||
# --filter=tree:0: Clones only the repository structure without file content (no historical directory listings), making the initial clone very small.
|
||||
# --no-checkout: Prevents automatically checking out the main branch. We will check out a specific commit later.
|
||||
# --sparse: Initializes the repository for sparse checkout, allowing us to fetch only specific directories.
|
||||
git clone --filter=tree:0 --no-checkout --sparse "${REPO_URL}" "${TARGET_DIR}"
|
||||
|
||||
cd "${TARGET_DIR}"
|
||||
|
||||
git sparse-checkout set "${SPARSE_PATHS[@]}"
|
||||
|
||||
echo "Checking out commit ${COMMIT_HASH}..."
|
||||
git checkout "${COMMIT_HASH}"
|
||||
|
||||
echo "Successfully checked out the specified models into the '${TARGET_DIR}' directory."
|
||||
@@ -5,4 +5,5 @@ GITHUB_MESA_VERSION=24.2.1
|
||||
GITHUB_LLVM_VERSION=16
|
||||
GITHUB_NDK_VERSION=27.0.11718014
|
||||
GITHUB_EMSDK_VERSION=3.1.60
|
||||
GITHUB_VULKANSDK_VERSION=1.4.321.0
|
||||
GITHUB_VULKANSDK_VERSION=1.4.321.0
|
||||
GITHUB_GLTF_SAMPLE_ASSETS_COMMIT=d441dfdb87413ff412c620849a649d61789a470f
|
||||
@@ -212,7 +212,7 @@ when using textures.
|
||||
This property can dramatically change the appearance of a surface. Non-metallic surfaces have
|
||||
chromatic diffuse reflection and achromatic specular reflection (reflected light does not change
|
||||
color). Metallic surfaces do not have any diffuse reflection and chromatic specular reflection
|
||||
(reflected light takes on the color of the surfaced as defined by `baseColor`).
|
||||
(reflected light takes on the color of the surfaced as defined by `baseColor`).
|
||||
|
||||
The effect of `metallic` is shown in figure [metallicProperty] (click on the image to see a
|
||||
larger version).
|
||||
@@ -248,7 +248,7 @@ The effect of `roughness` on metallic surfaces is shown in figure [roughnessCond
|
||||
When refraction through an object is enabled (using a `refractonType` of `thin` or `solid`), the
|
||||
`roughness` property will also affect the refractions, as shown in figure
|
||||
[roughnessRefractionProperty] (click on the image to see a larger version).
|
||||
|
||||
|
||||
![Figure [roughnessRefractionProperty]: Refractive sphere with `roughness` varying from 0.0
|
||||
(left) to 1.0 (right)](images/materials/refraction_roughness.png)
|
||||
|
||||
@@ -307,7 +307,7 @@ The sheen color controls the color appearance and strength of an optional sheen
|
||||
base layer described by the properties above. The sheen layer always sits below the clear coat layer
|
||||
if such a layer is present.
|
||||
|
||||
The sheen layer can be used to represent cloth and fabric materials. Please refer to
|
||||
The sheen layer can be used to represent cloth and fabric materials. Please refer to
|
||||
section [Cloth model] for more information about cloth and fabric materials.
|
||||
|
||||
The effect of `sheenColor` is shown in figure [materialSheenColor]
|
||||
@@ -520,14 +520,14 @@ light to bend further away from the initial path.
|
||||
|
||||
Table [commonMatIOR] describes acceptable refractive indices for various types of materials.
|
||||
|
||||
Material | IOR
|
||||
Material | IOR
|
||||
--------------------------:|:-----------------
|
||||
Air | 1.0
|
||||
Water | 1.33
|
||||
Common liquids | 1.33 to 1.5
|
||||
Common gemstones | 1.58 to 2.33
|
||||
Plastics, glass | 1.5 to 1.58
|
||||
Other dielectric materials | 1.33 to 1.58
|
||||
Water | 1.33
|
||||
Common liquids | 1.33 to 1.5
|
||||
Common gemstones | 1.58 to 2.33
|
||||
Plastics, glass | 1.5 to 1.58
|
||||
Other dielectric materials | 1.33 to 1.58
|
||||
[Table [commonMatIOR]: Index of refraction of common materials]
|
||||
|
||||
The appearance of a refractive material will greatly depend on the `refractionType` and
|
||||
@@ -1096,30 +1096,37 @@ Value
|
||||
`bool` or `number`, depending on the `type` of the constant. The type must be one of the types
|
||||
described in table [materialConstantsTypes].
|
||||
|
||||
Type | Description | Default
|
||||
:----------------------|:-----------------------------------------|:------------------
|
||||
int | A signed, 32 bit GLSL int | 0
|
||||
float | A single-precision GLSL float | 0.0
|
||||
bool | A GLSL bool | false
|
||||
Constants may also be specified as mutable by setting the `mutable` property to `true`. Only
|
||||
`bool` spec constants may be specified as mutable.
|
||||
|
||||
| Type | Description | Default | May be mutable? |
|
||||
|:------|:------------------------------|:--------|-----------------|
|
||||
| int | A signed, 32 bit GLSL int | | no |
|
||||
| float | A single-precision GLSL float | 0.0 | no |
|
||||
| bool | A GLSL bool | false | yes |
|
||||
[Table [materialConstantsTypes]: Material constants types]
|
||||
|
||||
Description
|
||||
: Lists the constant parameters accepted by your material. These constants can be set, or
|
||||
"specialized", at runtime when loading a material package. Multiple materials can be loaded from
|
||||
the same material package with differing constant parameter specializations. Once a material is
|
||||
loaded from a material package, its constant parameters cannot be changed. Compared to regular
|
||||
parameters, constant parameters allow the compiler to generate more efficient code. Access
|
||||
constant parameters from the shader by prefixing the name with `materialConstant_`. For example,
|
||||
a constant parameter named `myConstant` is accessed in the shader as
|
||||
`materialConstant_myConstant`. If a constant parameter is not set at runtime, the default is
|
||||
used.
|
||||
the same material package with differing constant parameter specializations. If a constant
|
||||
parameter is specialized as mutable, it may be changed at any time on a per-instance basis via
|
||||
`MaterialInstance::setConstant(name, value)`. Otherwise, once a material is loaded from a
|
||||
material package, its constant parameters cannot be changed.
|
||||
|
||||
Compared to regular parameters, constant parameters allow the compiler to generate more
|
||||
efficient code. Access constant parameters from the shader by prefixing the name with
|
||||
`materialConstant_`. For example, a constant parameter named `myConstant` is accessed in the
|
||||
shader as `materialConstant_myConstant`. If a constant parameter is not set at runtime, the
|
||||
default is used.
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
|
||||
material {
|
||||
constants : [
|
||||
{
|
||||
name : overrideAlpha,
|
||||
type : bool
|
||||
type : bool,
|
||||
mutable : true,
|
||||
},
|
||||
{
|
||||
name : customAlpha,
|
||||
@@ -2639,7 +2646,7 @@ standard skybox material. It produces a list of 2 parameters, named `showSun` an
|
||||
respectively a boolean and a cubemap texture.
|
||||
|
||||
```text
|
||||
$ matc --reflect parameters filament/src/materials/skybox.mat
|
||||
$ matc --reflect parameters filament/src/materials/skybox.mat
|
||||
{
|
||||
"parameters": [
|
||||
{
|
||||
@@ -2655,7 +2662,7 @@ $ matc --reflect parameters filament/src/materials/skybox.mat
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
### --variant-filter
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# Performance Analysis
|
||||
|
||||
## Android
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Download and install Android GPU Inspector (AGI). See https://developer.android.com/agi.
|
||||
|
||||
### Android with the Vulkan Backend
|
||||
|
||||
---
|
||||
|
||||
#### Profiling
|
||||
|
||||
1. Build a release build of Filament with the applicable backend(s) enabled
|
||||
_(+ any special flags for enabling sys strace. Nothing special is needed for Vulkan or WebGPU aside from
|
||||
building a release build with no flags)_ _(debug builds for this are useless)_
|
||||
|
||||
```shell
|
||||
# the following command assumes you are in the root filament directory
|
||||
# and ANDROID_HOME is exported (and possibly also CC and CXX on linux as needed)
|
||||
#
|
||||
# NOTE: to build with WebGPU support you need to explicitly include the -W flag
|
||||
# (it doesn't get compiled in by default), e.g.:
|
||||
# ./build.sh -W -p android,desktop -i release
|
||||
#
|
||||
# Note that you can speed this up a bit (and reduce disk space usage) by limiting the target to just
|
||||
# the ABI you plan on testing with the -q flag, e.g. -q arm64-v8a. If you do this, you
|
||||
# will need to update the android/gradle.properties file to specify the ABI(s) you are targeting
|
||||
# with the com.google.android.filament.abis property.
|
||||
# Thus, a build command that would target BOTH Vulkan AND WebGPU AND only target the ARM64 ABI would look something
|
||||
# like:
|
||||
# ./build.sh -W -q arm64-v8a -p android,desktop -i release
|
||||
./build.sh -p android,desktop -i release
|
||||
```
|
||||
1. Connect your Android device to your computer via a USB cord with USB debugging enabled and configure
|
||||
the system property to default to the desired backend, e.g.
|
||||
_(to determine how these numbers map to the backends, see the `enum class Backend`
|
||||
definition in [filament/filament/backend/include/backend/DriverEnums.h](../../../../filament/backend/include/backend/DriverEnums.h))_:
|
||||
```shell
|
||||
# to set the backend to Vulkan:
|
||||
adb shell setprop debug.filament.backend 2
|
||||
# to set the backend to WebGPU:
|
||||
adb shell setprop debug.filament.backend 4
|
||||
# to view the current property:
|
||||
adb shell getprop debug.filament.backend
|
||||
```
|
||||
1. Build and run a sample, e.g. sample-gltf-viewer, on your Android device
|
||||
_([Android Studio](https://developer.android.com/studio) recommended)_.
|
||||
1. Run [AGI](https://developer.android.com/agi) and follow instructions to profile the app/system
|
||||
with trace capture(s). See https://developer.android.com/agi/start for more details to
|
||||
get started.
|
||||
- We typically only run "Capture System Profiler trace" _(not necessarily "Capture Frame Profiler trace")_
|
||||
- When configuring the trace:
|
||||
- For both the WebGPU and Vulkan backends configure the profiler for the Vulkan API
|
||||
_(since WebGPU should be using Vulkan under the hood as well)_
|
||||
- Running for ~1 seconds should suffice
|
||||
- Hit the "Configure" button in Trace objects, select "Switch to advanced mode" and add:
|
||||
```
|
||||
data_sources {
|
||||
config {
|
||||
name: "track_event"
|
||||
track_event_config {
|
||||
disabled_categories: "*"
|
||||
enabled_categories: "filament/filament"
|
||||
enabled_categories: "filament/jobsystem"
|
||||
enabled_categories: "filament/gltfio"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
- One you open the trace, zoom into a series of frames to get a sense of generally how long they typically take
|
||||
_(use `W`, `S`, `A` and `D` keys and mouse wheel for navigation)_ and find a representative one.
|
||||
We are most interested in the performance of the
|
||||
`FEngine::loop` thread, how long it takes, overlap in activities/processes/commands, reduction in queue submissions,
|
||||
etc. Similarly, we can view GPU timeline as it relates to that. We want to see overlapping
|
||||
shader invocations and non-interrupted fragment shader runs.
|
||||
@@ -530,7 +530,7 @@ target_link_libraries(${TARGET} PRIVATE
|
||||
if (LINUX AND FILAMENT_SUPPORTS_WEBGPU)
|
||||
# Unclear why this needs to be explicit while other linux libs do not, but it is necessary for
|
||||
# WebGPU to build on linux.
|
||||
target_link_libraries(${TARGET} PRIVATE -lxcb)
|
||||
target_link_libraries(${TARGET} PRIVATE -lxcb -lX11)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -156,7 +156,7 @@ public:
|
||||
* Set to `true` to forcibly disable amortized shader compilation in the backend.
|
||||
* Currently only honored by the GL backend.
|
||||
*/
|
||||
bool disableAmortizedShaderCompile = false;
|
||||
bool disableAmortizedShaderCompile = true;
|
||||
|
||||
/**
|
||||
* Disable backend handles use-after-free checks.
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/bitset.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -66,6 +67,7 @@ public:
|
||||
using DescriptorBindingsInfo = utils::FixedCapacityVector<Descriptor>;
|
||||
using DescriptorSetInfo = std::array<DescriptorBindingsInfo, MAX_DESCRIPTOR_SET_COUNT>;
|
||||
using SpecializationConstantsInfo = utils::FixedCapacityVector<SpecializationConstant>;
|
||||
using MutableSpecConstantsInfo = utils::bitset8;
|
||||
using ShaderBlob = utils::FixedCapacityVector<uint8_t>;
|
||||
using ShaderSource = std::array<ShaderBlob, SHADER_TYPE_COUNT>;
|
||||
|
||||
@@ -103,7 +105,8 @@ public:
|
||||
Program& descriptorBindings(backend::descriptor_set_t set,
|
||||
DescriptorBindingsInfo descriptorBindings) noexcept;
|
||||
|
||||
Program& specializationConstants(SpecializationConstantsInfo specConstants) noexcept;
|
||||
Program& specializationConstants(SpecializationConstantsInfo specConstants,
|
||||
uint32_t firstMutableId, MutableSpecConstantsInfo mutableSpecConstants) noexcept;
|
||||
|
||||
struct PushConstant {
|
||||
utils::CString name;
|
||||
|
||||
@@ -82,8 +82,19 @@ Program& Program::attributes(AttributesInfo attributes) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Program& Program::specializationConstants(SpecializationConstantsInfo specConstants) noexcept {
|
||||
mSpecializationConstants = std::move(specConstants);
|
||||
Program& Program::specializationConstants(SpecializationConstantsInfo specConstants,
|
||||
uint32_t firstMutableId, MutableSpecConstantsInfo mutableSpecConstants) noexcept {
|
||||
// String the two lists together.
|
||||
mSpecializationConstants = SpecializationConstantsInfo(
|
||||
specConstants.size() + mutableSpecConstants.size());
|
||||
std::uninitialized_move(specConstants.begin(), specConstants.end(),
|
||||
mSpecializationConstants.begin());
|
||||
for (uint32_t i = 0; i < mutableSpecConstants.size(); i++) {
|
||||
mSpecializationConstants[i + specConstants.size()] = SpecializationConstant {
|
||||
.id = i + firstMutableId,
|
||||
.value = mutableSpecConstants[i],
|
||||
};
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -452,7 +452,7 @@ void VulkanDriver::updateDescriptorSetTexture(
|
||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
|
||||
|
||||
if (mExternalImageManager.isExternallySampledTexture(texture)) {
|
||||
if (UTILS_UNLIKELY(mExternalImageManager.isExternallySampledTexture(texture))) {
|
||||
mExternalImageManager.bindExternallySampledTexture(set, binding, texture, params);
|
||||
mAppState.hasBoundExternalImages = true;
|
||||
} else {
|
||||
@@ -1939,6 +1939,18 @@ void VulkanDriver::bindDescriptorSet(
|
||||
if (dsh) {
|
||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||
mDescriptorSetCache.bind(setIndex, set, std::move(offsets));
|
||||
|
||||
if (mAppState.hasExternalSamplers()) {
|
||||
auto const& bindInDrawBundle = mPipelineState.bindInDraw.second;
|
||||
// The set index being bound has already been bound or will be bound. If it's already
|
||||
// been bound and this set has external samplers, we do the doBindindraw block in
|
||||
// draw2() again. Because this set might potentially cause a new pipelineLayout
|
||||
// (therefore pipeline) to be bound.
|
||||
if (bindInDrawBundle.descriptorSetMask[setIndex] &&
|
||||
mExternalImageManager.hasExternalSampler(set)) {
|
||||
mPipelineState.bindInDraw.first = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mDescriptorSetCache.unbind(setIndex);
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ fvkutils::DescriptorSetMask VulkanExternalImageManager::prepareBindSets(LayoutAr
|
||||
}
|
||||
|
||||
bool VulkanExternalImageManager::hasExternalSampler(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set) {
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set) const {
|
||||
auto itr = std::find_if(mSetBindings.begin(), mSetBindings.end(),
|
||||
[&](SetBindingInfo const& info) { return info.set == set; });
|
||||
return itr != mSetBindings.end();
|
||||
|
||||
@@ -86,9 +86,9 @@ public:
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
private:
|
||||
bool hasExternalSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set);
|
||||
bool hasExternalSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set) const;
|
||||
|
||||
private:
|
||||
void updateSetAndLayout(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
|
||||
|
||||
@@ -46,17 +46,13 @@ fvkmemory::resource_ptr<VulkanTimerQuery> VulkanQueryManager::getNextQuery(
|
||||
return {};
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
std::pair<uint32_t, uint32_t> queryIndices;
|
||||
unused.forEachSetBit([&](size_t index) {
|
||||
if (found) {
|
||||
return;
|
||||
}
|
||||
size_t const firstUnused = unused.firstSetBit();
|
||||
{
|
||||
std::unique_lock<utils::Mutex> lock(mMutex);
|
||||
mUsed.set(index);
|
||||
found = true;
|
||||
queryIndices = std::make_pair(index * 2, index * 2 + 1);
|
||||
});
|
||||
mUsed.set(firstUnused);
|
||||
queryIndices = { firstUnused * 2, firstUnused * 2 + 1 };
|
||||
}
|
||||
return fvkmemory::resource_ptr<VulkanTimerQuery>::construct(resourceManager, queryIndices.first,
|
||||
queryIndices.second);
|
||||
}
|
||||
|
||||
@@ -49,8 +49,16 @@ constexpr size_t FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS = 4;
|
||||
// textures, samplers, and buffers,...), which would otherwise spam the logs.
|
||||
#define FWGPU_DEBUG_BIND_GROUPS 0x00000020
|
||||
|
||||
// Enables Android systrace
|
||||
#define FWGPU_DEBUG_SYSTRACE 0x00000040
|
||||
|
||||
// Enable a minimal set of traces to assess the performance of the backend.
|
||||
// All other debug features must be disabled.
|
||||
#define FWGPU_DEBUG_PROFILING 0x00000080
|
||||
|
||||
// Useful default combinations
|
||||
#define FWGPU_DEBUG_EVERYTHING 0xFFFFFFFF
|
||||
#define FWGPU_DEBUG_EVERYTHING (0xFFFFFFFF & ~FWGPU_DEBUG_PROFILING)
|
||||
#define FWGPU_DEBUG_PERFORMANCE FWGPU_DEBUG_SYSTRACE
|
||||
|
||||
#if defined(FILAMENT_BACKEND_DEBUG_FLAG)
|
||||
#define FWGPU_DEBUG_FORWARDED_FLAG (FILAMENT_BACKEND_DEBUG_FLAG & FWGPU_DEBUG_EVERYTHING)
|
||||
@@ -59,13 +67,53 @@ constexpr size_t FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS = 4;
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define FWGPU_DEBUG_FLAGS FWGPU_DEBUG_FORWARDED_FLAG
|
||||
#define FWGPU_DEBUG_FLAGS (FWGPU_DEBUG_PERFORMANCE | FWGPU_DEBUG_FORWARDED_FLAG)
|
||||
#else
|
||||
#define FWGPU_DEBUG_FLAGS 0
|
||||
#define FWGPU_DEBUG_FLAGS FWGPU_DEBUG_SYSTRACE
|
||||
#endif
|
||||
|
||||
// Override the debug flags if we are forcing profiling mode
|
||||
#if defined(FILAMENT_FORCE_PROFILING_MODE)
|
||||
#undef FWGPU_DEBUG_FLAGS
|
||||
#define FWGPU_DEBUG_FLAGS (FWGPU_DEBUG_PROFILING)
|
||||
#endif
|
||||
|
||||
#define FWGPU_ENABLED(flags) (((FWGPU_DEBUG_FLAGS) & (flags)) == (flags))
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_DEBUG_PROFILING) && FWGPU_DEBUG_FLAGS != FWGPU_DEBUG_PROFILING
|
||||
#error PROFILING is exclusive; all other debug features must be disabled.
|
||||
#endif
|
||||
|
||||
#if FWGPU_DEBUG_FLAGS == FWGPU_DEBUG_PROFILING
|
||||
|
||||
#ifndef NDEBUG
|
||||
#error PROFILING is meaningless in DEBUG mode.
|
||||
#endif
|
||||
|
||||
#define FWGPU_SYSTRACE_CONTEXT()
|
||||
#define FWGPU_SYSTRACE_START(marker)
|
||||
#define FWGPU_SYSTRACE_END()
|
||||
#define FWGPU_SYSTRACE_SCOPE()
|
||||
#define FWGPU_PROFILE_MARKER(marker) PROFILE_SCOPE(marker)
|
||||
|
||||
#elif FWGPU_ENABLED(FWGPU_DEBUG_SYSTRACE)
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#define FWGPU_SYSTRACE_CONTEXT() FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
#define FWGPU_SYSTRACE_START(marker) FILAMENT_TRACING_NAME_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, marker)
|
||||
#define FWGPU_SYSTRACE_END() FILAMENT_TRACING_NAME_END(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
#define FWGPU_SYSTRACE_SCOPE() FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
#define FWGPU_PROFILE_MARKER(marker) FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
|
||||
#else
|
||||
#define FWGPU_SYSTRACE_CONTEXT()
|
||||
#define FWGPU_SYSTRACE_START(marker)
|
||||
#define FWGPU_SYSTRACE_END()
|
||||
#define FWGPU_SYSTRACE_SCOPE()
|
||||
#define FWGPU_PROFILE_MARKER(marker)
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_DEBUG_FORCE_LOG_TO_I)
|
||||
#define FWGPU_LOGI LOG(INFO)
|
||||
#define FWGPU_LOGD FWGPU_LOGI
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include "DriverBase.h"
|
||||
#include "SystraceProfile.h"
|
||||
#include "private/backend/Dispatcher.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
@@ -148,6 +149,7 @@ void WebGPUDriver::tick(int) {
|
||||
|
||||
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||
FWGPU_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
|
||||
}
|
||||
|
||||
void WebGPUDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
|
||||
@@ -164,6 +166,7 @@ void WebGPUDriver::setPresentationTime(int64_t monotonic_clock_ns) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::endFrame(const uint32_t /* frameId */) {
|
||||
FWGPU_PROFILE_MARKER(PROFILE_NAME_ENDFRAME);
|
||||
mPipelineLayoutCache.onFrameEnd();
|
||||
mPipelineCache.onFrameEnd();
|
||||
|
||||
@@ -177,6 +180,7 @@ void WebGPUDriver::endFrame(const uint32_t /* frameId */) {
|
||||
// A new command encoder is created fresh after it.
|
||||
// If a command encoder is not in flight (nullptr), then this is a noop.
|
||||
void WebGPUDriver::flush(int /* dummy */) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
if (mCommandEncoder == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -200,6 +204,7 @@ void WebGPUDriver::flush(int /* dummy */) {
|
||||
// Submits the currently recorded commands and waits for them to complete on the GPU.
|
||||
// This is a synchronous operation and should be used sparingly.
|
||||
void WebGPUDriver::finish(int /* dummy */) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
mDevice.Tick();
|
||||
mAdapter.GetInstance().ProcessEvents();
|
||||
flush();
|
||||
@@ -224,33 +229,43 @@ void WebGPUDriver::finish(int /* dummy */) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
if (rph) {
|
||||
destructHandle<WebGPURenderPrimitive>(rph);
|
||||
if (!rph) {
|
||||
return;
|
||||
}
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
destructHandle<WebGPURenderPrimitive>(rph);
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vertexBufferInfoHandle) {
|
||||
if (vertexBufferInfoHandle) {
|
||||
destructHandle<WebGPUVertexBufferInfo>(vertexBufferInfoHandle);
|
||||
if (!vertexBufferInfoHandle) {
|
||||
return;
|
||||
}
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
destructHandle<WebGPUVertexBufferInfo>(vertexBufferInfoHandle);
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vertexBufferHandle) {
|
||||
if (vertexBufferHandle) {
|
||||
destructHandle<WebGPUVertexBuffer>(vertexBufferHandle);
|
||||
if (!vertexBufferHandle) {
|
||||
return;
|
||||
}
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
destructHandle<WebGPUVertexBuffer>(vertexBufferHandle);
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyIndexBuffer(Handle<HwIndexBuffer> indexBufferHandle) {
|
||||
if (indexBufferHandle) {
|
||||
destructHandle<WebGPUIndexBuffer>(indexBufferHandle);
|
||||
if (!indexBufferHandle) {
|
||||
return;
|
||||
}
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
destructHandle<WebGPUIndexBuffer>(indexBufferHandle);
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyBufferObject(Handle<HwBufferObject> bufferObjectHandle) {
|
||||
if (bufferObjectHandle) {
|
||||
destructHandle<WebGPUBufferObject>(bufferObjectHandle);
|
||||
if (!bufferObjectHandle) {
|
||||
return;
|
||||
}
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
destructHandle<WebGPUBufferObject>(bufferObjectHandle);
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyTexture(Handle<HwTexture> textureHandle) {
|
||||
@@ -419,7 +434,9 @@ Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
|
||||
// associated with a handle that was previously allocated by an S-method.
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
const uint64_t flags) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
mNativeWindow = nativeWindow;
|
||||
wgpu::Surface surface = mPlatform.createSurface(nativeWindow, flags);
|
||||
|
||||
@@ -455,12 +472,14 @@ void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
|
||||
|
||||
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vertexBufferInfoHandle,
|
||||
const uint8_t bufferCount, const uint8_t attributeCount, const AttributeArray attributes) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUVertexBufferInfo>(vertexBufferInfoHandle, bufferCount, attributeCount,
|
||||
attributes, mDeviceLimits);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vertexBufferHandle,
|
||||
const uint32_t vertexCount, Handle<HwVertexBufferInfo> vertexBufferInfoHandle) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
const auto vertexBufferInfo = handleCast<WebGPUVertexBufferInfo>(vertexBufferInfoHandle);
|
||||
constructHandle<WebGPUVertexBuffer>(vertexBufferHandle, vertexCount,
|
||||
vertexBufferInfo->bufferCount, vertexBufferInfoHandle);
|
||||
@@ -468,12 +487,14 @@ void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vertexBufferHandle
|
||||
|
||||
void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> indexBufferHandle,
|
||||
const ElementType elementType, const uint32_t indexCount, const BufferUsage usage) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
const auto elementSize = static_cast<uint8_t>(getElementTypeSize(elementType));
|
||||
constructHandle<WebGPUIndexBuffer>(indexBufferHandle, mDevice, elementSize, indexCount);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> bufferObjectHandle,
|
||||
const uint32_t byteCount, const BufferObjectBinding bindingType, const BufferUsage usage) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUBufferObject>(bufferObjectHandle, mDevice, bindingType, byteCount);
|
||||
}
|
||||
|
||||
@@ -481,6 +502,7 @@ void WebGPUDriver::createTextureR(Handle<HwTexture> textureHandle, const Sampler
|
||||
const uint8_t levels, const TextureFormat format, const uint8_t samples,
|
||||
const uint32_t width, const uint32_t height, const uint32_t depth,
|
||||
const TextureUsage usage) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUTexture>(textureHandle, target, levels, format, samples, width, height,
|
||||
depth, usage, mDevice);
|
||||
}
|
||||
@@ -496,13 +518,47 @@ void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> textureHandle,
|
||||
Handle<HwTexture> sourceTextureHandle, const backend::TextureSwizzle r,
|
||||
const backend::TextureSwizzle g, const backend::TextureSwizzle b,
|
||||
const backend::TextureSwizzle a) {
|
||||
PANIC_POSTCONDITION("Swizzle WebGPU Texture is not supported");
|
||||
|
||||
if (!isTextureSwizzleSupported()) {
|
||||
FWGPU_LOGW << "WebGPUDriver::createTextureViewSwizzleR called while texture swizzling is "
|
||||
"not supported by the device/driver";
|
||||
}
|
||||
|
||||
auto sourceTexture{ handleCast<WebGPUTexture>(sourceTextureHandle) };
|
||||
assert_invariant(sourceTexture);
|
||||
|
||||
wgpu::TextureComponentSwizzle swizzle
|
||||
{
|
||||
.r = toWGPUComponentSwizzle(r),
|
||||
.g = toWGPUComponentSwizzle(g),
|
||||
.b = toWGPUComponentSwizzle(b),
|
||||
.a = toWGPUComponentSwizzle(a),
|
||||
};
|
||||
|
||||
wgpu::TextureComponentSwizzleDescriptor swizzleDesc {};
|
||||
swizzleDesc.swizzle = swizzle;
|
||||
|
||||
const wgpu::TextureViewDescriptor viewDesc {
|
||||
.nextInChain = &swizzleDesc,
|
||||
.label = "swizzled_texture_view",
|
||||
.format = sourceTexture->getTexture().GetFormat(),
|
||||
.dimension = sourceTexture->getViewDimension(),
|
||||
.baseMipLevel = 0,
|
||||
.mipLevelCount = sourceTexture->getTexture().GetMipLevelCount(),
|
||||
.baseArrayLayer = 0,
|
||||
.arrayLayerCount = sourceTexture->getTexture().GetDepthOrArrayLayers(),
|
||||
};
|
||||
|
||||
wgpu::TextureView swizzledView{ sourceTexture->getTexture().CreateView(&viewDesc) };
|
||||
FILAMENT_CHECK_POSTCONDITION(swizzledView) << "Failed to create swizzled Texture view";
|
||||
constructHandle<WebGPUTexture>(textureHandle, sourceTexture, swizzledView);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> textureHandle,
|
||||
const backend::SamplerType target, const backend::TextureFormat format,
|
||||
const uint32_t width, const uint32_t height, const backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
@@ -510,6 +566,7 @@ void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> textureHandle,
|
||||
const backend::SamplerType target, const backend::TextureFormat format,
|
||||
const uint32_t width, const uint32_t height, const backend::TextureUsage usage,
|
||||
void* externalImage) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
@@ -529,6 +586,7 @@ void WebGPUDriver::importTextureR(Handle<HwTexture> textureHandle, const intptr_
|
||||
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> renderPrimitiveHandle,
|
||||
Handle<HwVertexBuffer> vertexBufferHandle, Handle<HwIndexBuffer> indexBufferHandle,
|
||||
const PrimitiveType primitiveType) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
assert_invariant(mDevice);
|
||||
const auto renderPrimitive = constructHandle<WebGPURenderPrimitive>(renderPrimitiveHandle);
|
||||
const auto vertexBuffer = handleCast<WebGPUVertexBuffer>(vertexBufferHandle);
|
||||
@@ -539,6 +597,7 @@ void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> renderPrimit
|
||||
}
|
||||
|
||||
void WebGPUDriver::createProgramR(Handle<HwProgram> programHandle, Program&& program) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUProgram>(programHandle, mDevice, program);
|
||||
}
|
||||
|
||||
@@ -556,6 +615,7 @@ void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> renderTargetHandle
|
||||
const TargetBufferFlags targetFlags, const uint32_t width, const uint32_t height,
|
||||
const uint8_t samples, const uint8_t layerCount, const MRT color,
|
||||
const TargetBufferInfo depth, const TargetBufferInfo stencil) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPURenderTarget>(
|
||||
renderTargetHandle, width, height, samples, layerCount, color, depth, stencil,
|
||||
targetFlags,
|
||||
@@ -577,11 +637,13 @@ void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
|
||||
void WebGPUDriver::createDescriptorSetLayoutR(
|
||||
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle,
|
||||
backend::DescriptorSetLayout&& info) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUDescriptorSetLayout>(descriptorSetLayoutHandle, std::move(info), mDevice);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> descriptorSetHandle,
|
||||
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
auto layout = handleCast<WebGPUDescriptorSetLayout>(descriptorSetLayoutHandle);
|
||||
constructHandle<WebGPUDescriptorSet>(descriptorSetHandle, layout->getLayout(),
|
||||
layout->getBindGroupEntries());
|
||||
@@ -641,7 +703,7 @@ bool WebGPUDriver::isTextureFormatSupported(const TextureFormat format) {
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isTextureSwizzleSupported() {
|
||||
return false;
|
||||
return mDevice.HasFeature(wgpu::FeatureName::TextureComponentSwizzle);
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isTextureFormatMipmappable(const TextureFormat format) {
|
||||
@@ -923,7 +985,7 @@ void WebGPUDriver::update3DImage(Handle<HwTexture> textureHandle, const uint32_t
|
||||
<< "Failed to create staging input texture for blit?";
|
||||
const auto copyInfo{ wgpu::TexelCopyTextureInfo{
|
||||
.texture = stagingTexture,
|
||||
.mipLevel = level,
|
||||
.mipLevel = 0,
|
||||
.origin = { .x = 0, .y = 0, .z = 0 },
|
||||
.aspect = texture->getAspect(),
|
||||
} };
|
||||
@@ -978,7 +1040,6 @@ void WebGPUDriver::update3DImage(Handle<HwTexture> textureHandle, const uint32_t
|
||||
mQueue.Submit(1, &blitCommand);
|
||||
mCommandEncoder = nullptr;
|
||||
}
|
||||
stagingTexture.Destroy();
|
||||
} else {
|
||||
// Direct copy without a blit.
|
||||
const auto copyInfo { wgpu::TexelCopyTextureInfo{
|
||||
@@ -1098,6 +1159,7 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
|
||||
// resolve targets if necessary.
|
||||
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
|
||||
RenderPassParams const& params) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
if (!mCommandEncoder) {
|
||||
wgpu::CommandEncoderDescriptor commandEncoderDescriptor{ .label = "frame_command_encoder" };
|
||||
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
||||
@@ -1251,6 +1313,7 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
|
||||
}
|
||||
|
||||
void WebGPUDriver::endRenderPass(int /* dummy */) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
mRenderPassEncoder.End();
|
||||
mRenderPassEncoder = nullptr;
|
||||
}
|
||||
@@ -1259,10 +1322,12 @@ void WebGPUDriver::nextSubpass(int) {
|
||||
//todo
|
||||
}
|
||||
|
||||
void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSch == readSch,
|
||||
void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSwapChain,
|
||||
Handle<HwSwapChain> readSwapChain) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
||||
"WebGPU driver does not support distinct draw/read swap chains.");
|
||||
auto swapChain = handleCast<WebGPUSwapChain>(drawSch);
|
||||
auto swapChain = handleCast<WebGPUSwapChain>(drawSwapChain);
|
||||
mSwapChain = swapChain;
|
||||
assert_invariant(mSwapChain);
|
||||
|
||||
@@ -1275,13 +1340,16 @@ void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
|
||||
assert_invariant(mTextureView);
|
||||
|
||||
if (!mCommandEncoder) {
|
||||
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = { .label = "frame_command_encoder" };
|
||||
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
|
||||
.label = "frame_command_encoder"
|
||||
};
|
||||
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
||||
}
|
||||
assert_invariant(mCommandEncoder);
|
||||
}
|
||||
|
||||
void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
|
||||
void WebGPUDriver::commit(Handle<HwSwapChain> swapChainHandle) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
if (UTILS_VERY_UNLIKELY(!mCommandEncoder)) {
|
||||
// nothing to submit
|
||||
if (UTILS_LIKELY(mSwapChain)) {
|
||||
@@ -1332,18 +1400,20 @@ void WebGPUDriver::insertEventMarker(char const* string) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::pushGroupMarker(char const* string) {
|
||||
FWGPU_SYSTRACE_CONTEXT();
|
||||
FWGPU_SYSTRACE_START(string);
|
||||
}
|
||||
|
||||
void WebGPUDriver::popGroupMarker(int /* dummy */) {
|
||||
FWGPU_SYSTRACE_CONTEXT();
|
||||
FWGPU_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void WebGPUDriver::startCapture(int /* dummy */) {
|
||||
//todo
|
||||
}
|
||||
|
||||
void WebGPUDriver::popGroupMarker(int) {
|
||||
//todo
|
||||
}
|
||||
|
||||
void WebGPUDriver::startCapture(int) {
|
||||
//todo
|
||||
}
|
||||
|
||||
void WebGPUDriver::stopCapture(int) {
|
||||
void WebGPUDriver::stopCapture(int /* dummy */) {
|
||||
//todo
|
||||
}
|
||||
|
||||
@@ -1559,7 +1629,7 @@ void WebGPUDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
Handle<HwRenderTarget> destinationRenderTargetHandle, const Viewport destinationViewport,
|
||||
Handle<HwRenderTarget> sourceRenderTargetHandle, const Viewport sourceViewport,
|
||||
const SamplerMagFilter filter) {
|
||||
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
auto const sourceTarget{ handleCast<WebGPURenderTarget>(sourceRenderTargetHandle) };
|
||||
auto const destinationTarget{ handleCast<WebGPURenderTarget>(destinationRenderTargetHandle) };
|
||||
assert_invariant(sourceTarget && destinationTarget);
|
||||
@@ -1605,7 +1675,7 @@ void WebGPUDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
static_cast<uint32_t>(sourceViewport.height) };
|
||||
const wgpu::Extent2D destinationSize{ static_cast<uint32_t>(destinationViewport.width),
|
||||
static_cast<uint32_t>(destinationViewport.height) };
|
||||
|
||||
|
||||
bool reusedCommandEncoder{ true };
|
||||
if (mCommandEncoder) {
|
||||
flush();
|
||||
@@ -1645,6 +1715,7 @@ void WebGPUDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
void WebGPUDriver::resolve(Handle<HwTexture> destinationTextureHandle, const uint8_t sourceLevel,
|
||||
const uint8_t sourceLayer, Handle<HwTexture> sourceTextureHandle,
|
||||
const uint8_t destinationLevel, const uint8_t destinationLayer) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
if (!mCommandEncoder) {
|
||||
const wgpu::CommandEncoderDescriptor encoderDescriptor{
|
||||
.label = "command_created_with_resolve",
|
||||
@@ -1682,6 +1753,7 @@ void WebGPUDriver::blit(Handle<HwTexture> destinationTextureHandle, const uint8_
|
||||
const uint8_t sourceLayer, const math::uint2 destinationOrigin,
|
||||
Handle<HwTexture> sourceTextureHandle, const uint8_t destinationLevel,
|
||||
const uint8_t destinationLayer, const math::uint2 sourceOrigin, const math::uint2 size) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
bool reusedCommandEncoder{ true };
|
||||
if (mCommandEncoder) {
|
||||
// make sure command elements (draws, etc.) prior to this blit are processed before the blit
|
||||
@@ -1740,6 +1812,7 @@ void WebGPUDriver::blit(Handle<HwTexture> destinationTextureHandle, const uint8_
|
||||
}
|
||||
|
||||
void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
assert_invariant(mRenderPassEncoder);
|
||||
const auto program{ handleCast<WebGPUProgram>(pipelineState.program) };
|
||||
assert_invariant(program);
|
||||
@@ -1828,6 +1901,7 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> renderPrimitiveHandle) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
const auto renderPrimitive = handleCast<WebGPURenderPrimitive>(renderPrimitiveHandle);
|
||||
const auto vertexBufferInfo = handleCast<WebGPUVertexBufferInfo>(
|
||||
renderPrimitive->vertexBuffer->getVertexBufferInfoHandle());
|
||||
@@ -1845,6 +1919,7 @@ void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> renderPrimitive
|
||||
|
||||
void WebGPUDriver::draw2(const uint32_t indexOffset, const uint32_t indexCount,
|
||||
const uint32_t instanceCount) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
// Bind groups are deferred until the actual draw call.
|
||||
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) {
|
||||
auto& binding = mCurrentDescriptorSets[i];
|
||||
@@ -1891,6 +1966,7 @@ void WebGPUDriver::resetState(int) {
|
||||
void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> descriptorSetHandle,
|
||||
const backend::descriptor_binding_t binding, Handle<HwBufferObject> bufferObjectHandle,
|
||||
const uint32_t offset, const uint32_t size) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
const auto bindGroup = handleCast<WebGPUDescriptorSet>(descriptorSetHandle);
|
||||
const auto buffer = handleCast<WebGPUBufferObject>(bufferObjectHandle);
|
||||
if (!bindGroup->getIsLocked()) {
|
||||
@@ -1911,6 +1987,7 @@ void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> descriptorS
|
||||
void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> descriptorSetHandle,
|
||||
const backend::descriptor_binding_t binding, Handle<HwTexture> textureHandle,
|
||||
const SamplerParams params) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
auto bindGroup = handleCast<WebGPUDescriptorSet>(descriptorSetHandle);
|
||||
auto texture = handleCast<WebGPUTexture>(textureHandle);
|
||||
|
||||
|
||||
@@ -372,6 +372,7 @@ bool WebGPUPipelineCache::RenderPipelineKeyEqual::operator()(RenderPipelineKey c
|
||||
}
|
||||
|
||||
void WebGPUPipelineCache::removeExpiredPipelines() {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
using Iterator = decltype(mRenderPipelines)::const_iterator;
|
||||
for (Iterator iterator{ mRenderPipelines.begin() }; iterator != mRenderPipelines.end();) {
|
||||
RenderPipelineCacheEntry const& entry{ iterator.value() };
|
||||
|
||||
@@ -89,6 +89,7 @@ bool WebGPUPipelineLayoutCache::PipelineLayoutKeyEqual::operator()(PipelineLayou
|
||||
}
|
||||
|
||||
void WebGPUPipelineLayoutCache::removeExpiredPipelineLayouts() {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
using Iterator = decltype(mPipelineLayouts)::const_iterator;
|
||||
for (Iterator iterator{ mPipelineLayouts.begin() }; iterator != mPipelineLayouts.end();) {
|
||||
PipelineLayoutCacheEntry const& entry{ iterator.value() };
|
||||
|
||||
@@ -283,6 +283,24 @@ WebGPUTexture::WebGPUTexture(WebGPUTexture const* src, const uint8_t baseLevel,
|
||||
toWebGPUTextureViewDimension(src->target)) },
|
||||
mMsaaSidecarTexture{ src->mMsaaSidecarTexture } {}
|
||||
|
||||
WebGPUTexture::WebGPUTexture(const WebGPUTexture* src, const wgpu::TextureView view) noexcept
|
||||
: HwTexture{ src->target, src->levels, src->samples, src->width, src->height, src->depth,
|
||||
src->format, src->usage},
|
||||
mViewFormat{ src->mViewFormat },
|
||||
mMipmapGenerationStrategy{ src->mMipmapGenerationStrategy },
|
||||
mWebGPUFormat{ src->mWebGPUFormat },
|
||||
mAspect{ src->mAspect },
|
||||
mWebGPUUsage{ src->mWebGPUUsage },
|
||||
mViewUsage{ src->mViewUsage },
|
||||
mBlockWidth{ src->mBlockWidth },
|
||||
mBlockHeight{ src->mBlockHeight },
|
||||
mArrayLayerCount{ src->mArrayLayerCount },
|
||||
mTexture{ src->mTexture },
|
||||
mDefaultMipLevel{ src->mDefaultMipLevel },
|
||||
mDefaultBaseArrayLayer{ 0 },
|
||||
mDefaultTextureView{ view },
|
||||
mMsaaSidecarTexture{src->mMsaaSidecarTexture}{}
|
||||
|
||||
wgpu::Texture const& WebGPUTexture::getMsaaSidecarTexture(const uint8_t sampleCount) const {
|
||||
if (mMsaaSidecarTexture == nullptr) {
|
||||
return mMsaaSidecarTexture; // nullptr (no such sidecar)
|
||||
|
||||
@@ -34,11 +34,26 @@ public:
|
||||
NONE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a Filament texture and a texture view
|
||||
*/
|
||||
WebGPUTexture(SamplerType, uint8_t levels, TextureFormat, uint8_t samples, uint32_t width,
|
||||
uint32_t height, uint32_t depth, TextureUsage, wgpu::Device const&) noexcept;
|
||||
|
||||
/**
|
||||
* Creates a "Filament Texture View", where the underlying texture is the same as the source,
|
||||
* but the view elements differ
|
||||
*/
|
||||
WebGPUTexture(WebGPUTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
|
||||
|
||||
/**
|
||||
* @param textureView texture view to use for the respective texture. e.g. it can be a
|
||||
* swizzled Texture view for that filament texture
|
||||
*
|
||||
* Associates the underlying texture with the given texture view
|
||||
*/
|
||||
WebGPUTexture(WebGPUTexture const* source, wgpu::TextureView textureView) noexcept;
|
||||
|
||||
[[nodiscard]] wgpu::TextureAspect getAspect() const { return mAspect; }
|
||||
|
||||
[[nodiscard]] size_t getBlockWidth() const { return mBlockWidth; }
|
||||
|
||||
@@ -295,6 +295,20 @@ namespace filament::backend {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr wgpu::ComponentSwizzle toWGPUComponentSwizzle(const backend::TextureSwizzle swizzle) {
|
||||
switch (swizzle) {
|
||||
case backend::TextureSwizzle::CHANNEL_0: return wgpu::ComponentSwizzle::R;
|
||||
case backend::TextureSwizzle::CHANNEL_1: return wgpu::ComponentSwizzle::G;
|
||||
case backend::TextureSwizzle::CHANNEL_2: return wgpu::ComponentSwizzle::B;
|
||||
case backend::TextureSwizzle::CHANNEL_3: return wgpu::ComponentSwizzle::A;
|
||||
case backend::TextureSwizzle::SUBSTITUTE_ZERO: return wgpu::ComponentSwizzle::Zero;
|
||||
case backend::TextureSwizzle::SUBSTITUTE_ONE: return wgpu::ComponentSwizzle::One;
|
||||
default:
|
||||
FWGPU_LOGW << "invalid swizzle component returning wgpu::ComponentSwizzle::Undefined";
|
||||
return wgpu::ComponentSwizzle::Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if https://www.w3.org/TR/webgpu/#copy-compatible which states:
|
||||
* Two GPUTextureFormats format1 and format2 are copy-compatible if:
|
||||
|
||||
@@ -67,9 +67,11 @@ constexpr std::array REQUIRED_FEATURES = {
|
||||
};
|
||||
|
||||
constexpr std::array OPTIONAL_FEATURES = {
|
||||
wgpu::FeatureName::CoreFeaturesAndLimits,
|
||||
wgpu::FeatureName::DepthClipControl,
|
||||
wgpu::FeatureName::Depth32FloatStencil8,
|
||||
wgpu::FeatureName::CoreFeaturesAndLimits };
|
||||
wgpu::FeatureName::TextureComponentSwizzle,
|
||||
};
|
||||
|
||||
enum class LimitToValidate : uint8_t {
|
||||
begin = 0,// needs to be first for iterating through all possible values in the enum
|
||||
|
||||
@@ -41,36 +41,21 @@
|
||||
uint32_t height;
|
||||
} wl;
|
||||
}// namespace
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
|
||||
// TODO: we should allow for headless on Linux explicitly. Right now this is the headless path
|
||||
// (with no FILAMENT_SUPPORTS_XCB or FILAMENT_SUPPORTS_XLIB).
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11) && defined(FILAMENT_SUPPORTS_XLIB)
|
||||
// TODO: we should allow for headless on Linux explicitly. Right now headless path doesn't work
|
||||
#include <dlfcn.h>
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
#include <xcb/xcb.h>
|
||||
namespace {
|
||||
typedef xcb_connection_t* (*XCB_CONNECT)(const char* displayname, int* screenp);
|
||||
}// namespace
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
#include <X11/Xlib.h>
|
||||
namespace {
|
||||
typedef Display* (*X11_OPEN_DISPLAY)(const char*);
|
||||
}// namespace
|
||||
#endif
|
||||
static constexpr const char* LIBRARY_X11 = "libX11.so.6";
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
static constexpr const char* LIBRARY_X11 { "libX11.so.6" };
|
||||
|
||||
namespace {
|
||||
struct XEnv {
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
XCB_CONNECT xcbConnect;
|
||||
xcb_connection_t* connection;
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
X11_OPEN_DISPLAY openDisplay;
|
||||
Display* display;
|
||||
#endif
|
||||
void* library = nullptr;
|
||||
} g_x11;
|
||||
}// namespace
|
||||
typedef Display* (*X11_OPEN_DISPLAY)(const char*);
|
||||
struct XEnv final {
|
||||
X11_OPEN_DISPLAY openDisplay;
|
||||
Display* display { nullptr };
|
||||
void* library { nullptr };
|
||||
} g_x11;
|
||||
} // namespace
|
||||
#else
|
||||
#error Not a supported Linux or FeeBSD + WebGPU platform
|
||||
#endif
|
||||
@@ -116,59 +101,26 @@ wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
surfaceExtent.height = ptrval->height;
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Unable to get window size for Linux Wayland-backed surface.";
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11) && defined (FILAMENT_SUPPORTS_XLIB)
|
||||
if (g_x11.library == nullptr) {
|
||||
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.library) << "Unable to open X11 library.";
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
g_x11.xcbConnect = (XCB_CONNECT) dlsym(g_x11.library, "xcb_connect");
|
||||
int screen = 0;
|
||||
g_x11.connection = g_x11.xcbConnect(nullptr, &screen);
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
g_x11.openDisplay = (X11_OPEN_DISPLAY) dlsym(g_x11.library, "XOpenDisplay");
|
||||
g_x11.display = g_x11.openDisplay(NULL);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.display) << "Unable to open X11 display.";
|
||||
#endif
|
||||
}
|
||||
#if defined(FILAMENT_SUPPORTS_XCB) || defined(FILAMENT_SUPPORTS_XLIB)
|
||||
bool useXcb = false;
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
useXcb = (SWAP_CHAIN_CONFIG_ENABLE_XCB) != 0;
|
||||
#else
|
||||
useXcb = true;
|
||||
#endif
|
||||
if (useXcb) {
|
||||
const xcb_setup_t* setup = xcb_get_setup(g_x11.connection);
|
||||
xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup);
|
||||
xcb_screen_t* screen = screen_iter.data;
|
||||
surfaceExtent.width = static_cast<uint32_t>(screen->width_in_pixels);
|
||||
surfaceExtent.height = static_cast<uint32_t>(screen->height_in_pixels);
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Unable to get window surface size for Linux (or FreeBSD) "
|
||||
"XCB-backed surface.";
|
||||
}
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
if (!useXcb) {
|
||||
int screenNumber = DefaultScreen(g_x11.display);
|
||||
Screen* screen = ScreenOfDisplay(g_x11.display, screenNumber);
|
||||
surfaceExtent.width = static_cast<uint32_t>(WidthOfScreen(screen));
|
||||
surfaceExtent.height = static_cast<uint32_t>(HeightOfScreen(screen));
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Unable to get window surface size for Linux (or FreeBSD) "
|
||||
"XLib-backed surface.";
|
||||
}
|
||||
#endif
|
||||
if (g_x11.openDisplay == nullptr) {
|
||||
g_x11.openDisplay = reinterpret_cast<X11_OPEN_DISPLAY>(dlsym(g_x11.library, "XOpenDisplay"));
|
||||
g_x11.display = g_x11.openDisplay(NULL);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.display) << "Unable to open X11 display.";
|
||||
}
|
||||
Window window { reinterpret_cast<Window const>(nativeWindow) };
|
||||
XWindowAttributes windowAttributes;
|
||||
Status ok { XGetWindowAttributes(g_x11.display, window, &windowAttributes) };
|
||||
FILAMENT_CHECK_PRECONDITION(ok != 0) << " XGetWindowAttributes failed";
|
||||
surfaceExtent.width = static_cast<uint32_t>(windowAttributes.width);
|
||||
surfaceExtent.height = static_cast<uint32_t>(windowAttributes.height);
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Cannot get window surface size for X11 surface for Linux (or FreeBSD) OS "
|
||||
"(not built with support for XCB or XLIB?)";
|
||||
#elif defined(__linux__)
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Cannot get window surface size for Linux (or FreeBSD) OS "
|
||||
"(not built with support for Wayland or X11?)";
|
||||
"(not built with support for XLIB?)";
|
||||
#else
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Not a supported (Linux) OS + WebGPU platform";
|
||||
@@ -176,80 +128,40 @@ wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
return surfaceExtent;
|
||||
}
|
||||
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t flags) {
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::Surface surface = nullptr;
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
wl* ptrval = reinterpret_cast<wl*>(nativeWindow);
|
||||
wgpu::SurfaceSourceWaylandSurface surfaceSourceWayland{};
|
||||
surfaceSourceWayland.display = ptrval->display;
|
||||
surfaceSourceWayland.surface = ptrval->surface;
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
wgpu::SurfaceDescriptor const surfaceDescriptor {
|
||||
.nextInChain = &surfaceSourceWayland,
|
||||
.label = "linux_wayland_surface"
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Unable to create Linux Wayland-backed surface.";
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11) && defined(FILAMENT_SUPPORTS_XLIB)
|
||||
if (g_x11.library == nullptr) {
|
||||
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.library) << "Unable to open X11 library.";
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
g_x11.xcbConnect = (XCB_CONNECT) dlsym(g_x11.library, "xcb_connect");
|
||||
int screen = 0;
|
||||
g_x11.connection = g_x11.xcbConnect(nullptr, &screen);
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
g_x11.openDisplay = (X11_OPEN_DISPLAY) dlsym(g_x11.library, "XOpenDisplay");
|
||||
g_x11.display = g_x11.openDisplay(NULL);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.display) << "Unable to open X11 display.";
|
||||
#endif
|
||||
}
|
||||
#if defined(FILAMENT_SUPPORTS_XCB) || defined(FILAMENT_SUPPORTS_XLIB)
|
||||
bool useXcb = false;
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
useXcb = (flags & SWAP_CHAIN_CONFIG_ENABLE_XCB) != 0;
|
||||
#else
|
||||
useXcb = true;
|
||||
#endif
|
||||
if (useXcb) {
|
||||
wgpu::SurfaceSourceXCBWindow surfaceSourceXcb{};
|
||||
surfaceSourceXcb.connection = g_x11.connection;
|
||||
|
||||
// TODO: this looks really wrong, please fix!!
|
||||
surfaceSourceXcb.window = *((uint32_t*) nativeWindow);
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceXcb,
|
||||
.label = "linux_xcb_surface"
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Unable to create Linux (or FreeBSD) XCB-backed surface.";
|
||||
}
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
if (!useXcb) {
|
||||
wgpu::SurfaceSourceXlibWindow surfaceSourceXlib{};
|
||||
surfaceSourceXlib.display = g_x11.display;
|
||||
surfaceSourceXlib.window = reinterpret_cast<uint64_t>(nativeWindow);
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceXlib,
|
||||
.label = "linux_xlib_surface"
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Unable to create Linux (or FreeBSD) XLib-backed surface.";
|
||||
}
|
||||
#endif
|
||||
if (g_x11.openDisplay == nullptr) {
|
||||
g_x11.openDisplay = (X11_OPEN_DISPLAY) dlsym(g_x11.library, "XOpenDisplay");
|
||||
g_x11.display = g_x11.openDisplay(NULL);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.display) << "Unable to open X11 display.";
|
||||
}
|
||||
wgpu::SurfaceSourceXlibWindow surfaceSourceXlib{};
|
||||
surfaceSourceXlib.display = g_x11.display;
|
||||
surfaceSourceXlib.window = reinterpret_cast<Window const>(nativeWindow);
|
||||
wgpu::SurfaceDescriptor const surfaceDescriptor {
|
||||
.nextInChain = &surfaceSourceXlib,
|
||||
.label = "linux_xlib_surface"
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Cannot create WebGPU X11 surface for Linux (or FreeBSD) OS "
|
||||
"(not built with support for XCB or XLIB?)";
|
||||
#elif defined(__linux__)
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Cannot create WebGPU surface for Linux (or FreeBSD) OS "
|
||||
"(not built with support for Wayland or X11?)";
|
||||
<< "Unable to create Linux (or FreeBSD) XLib-backed surface.";
|
||||
#else
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Not a supported (Linux) OS + WebGPU platform";
|
||||
|
||||
@@ -144,6 +144,7 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
|
||||
}
|
||||
|
||||
TEST_F(BlitTest, ColorMagnify) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
auto& api = getDriverApi();
|
||||
mCleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
@@ -207,6 +208,7 @@ TEST_F(BlitTest, ColorMagnify) {
|
||||
}
|
||||
|
||||
TEST_F(BlitTest, ColorMinify) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
auto& api = getDriverApi();
|
||||
mCleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
@@ -262,6 +264,7 @@ TEST_F(BlitTest, ColorMinify) {
|
||||
}
|
||||
|
||||
TEST_F(BlitTest, ColorResolve) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Nothing is drawn, see b/417229577");
|
||||
auto& api = getDriverApi();
|
||||
|
||||
@@ -104,7 +104,6 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL),
|
||||
"OpenGL image is upside down due to readPixels failing for texture with uploaded image "
|
||||
"data");
|
||||
FAIL_IF(Backend::WEBGPU, "BUG");
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
@@ -127,7 +126,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
TrianglePrimitive const triangle(getDriverApi());
|
||||
|
||||
// Create a texture.
|
||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
|
||||
Handle<HwTexture> const texture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kTexFormat, 1, kTexWidth, kTexHeight, 1, usage));
|
||||
|
||||
|
||||
@@ -231,6 +231,7 @@ public:
|
||||
};
|
||||
|
||||
TEST_F(LoadImageTest, UpdateImage2D) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
FAIL_IF(Backend::VULKAN, "Multiple test cases crash, see b/417481434");
|
||||
|
||||
// All of these test cases should result in the same rendered image, and thus the same hash.
|
||||
@@ -569,7 +570,7 @@ TEST_F(LoadImageTest, UpdateImage3D) {
|
||||
PixelDataType pixelType = PixelDataType::FLOAT;
|
||||
TextureFormat textureFormat = TextureFormat::RGBA16F;
|
||||
SamplerType samplerType = SamplerType::SAMPLER_2D_ARRAY;
|
||||
TextureUsage usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
|
||||
TextureUsage usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT;
|
||||
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
|
||||
@@ -80,6 +80,7 @@ public:
|
||||
};
|
||||
|
||||
TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Two cases fail, see b/417255941 and b/417255943");
|
||||
// These test scenarios use a known hash of the result pixel buffer to decide pass / fail,
|
||||
|
||||
@@ -46,8 +46,9 @@ Shader createShader(DriverApi& api, Cleanup& cleanup, Backend backend) {
|
||||
|
||||
// Rendering an external image without setting any data should not crash.
|
||||
TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||
SKIP_IF(Backend::METAL, "External images aren't supported in metal");
|
||||
SKIP_IF(Backend::VULKAN, "External images aren't supported in vulkan");
|
||||
SKIP_IF(Backend::METAL, "External images aren't supported in Metal");
|
||||
SKIP_IF(Backend::VULKAN, "External images aren't supported in Vulkan");
|
||||
SKIP_IF(Backend::WEBGPU, "External images aren't supported in WebGPU");
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
@@ -109,8 +110,9 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, RenderExternalImage) {
|
||||
SKIP_IF(Backend::METAL, "External images aren't supported in metal");
|
||||
SKIP_IF(Backend::VULKAN, "External images aren't supported in vulkan");
|
||||
SKIP_IF(Backend::METAL, "External images aren't supported in Metal");
|
||||
SKIP_IF(Backend::VULKAN, "External images aren't supported in Vulkan");
|
||||
SKIP_IF(Backend::WEBGPU, "External images aren't supported in WebGPU");
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Affected area in wrong corner, see b/417229118");
|
||||
auto& api = getDriverApi();
|
||||
|
||||
@@ -126,6 +126,7 @@ public:
|
||||
};
|
||||
|
||||
TEST_F(BasicStencilBufferTest, StencilBuffer) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Stencil not supported, see b/417230776");
|
||||
auto& api = getDriverApi();
|
||||
@@ -152,6 +153,7 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
|
||||
}
|
||||
|
||||
TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Stencil not supported, see b/417230776");
|
||||
auto& api = getDriverApi();
|
||||
@@ -178,6 +180,7 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
|
||||
}
|
||||
|
||||
TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Stencil not supported, see b/417230776");
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL), "Stencil isn't applied");
|
||||
|
||||
@@ -87,6 +87,9 @@ public:
|
||||
std::is_same_v<math::mat3f, T>
|
||||
>;
|
||||
|
||||
template<typename T>
|
||||
using is_supported_constant_parameter_t = std::enable_if_t<std::is_same_v<bool, T>>;
|
||||
|
||||
/**
|
||||
* Creates a new MaterialInstance using another MaterialInstance as a template for initialization.
|
||||
* The new MaterialInstance is an instance of the same Material of the template instance and
|
||||
@@ -238,13 +241,13 @@ public:
|
||||
|
||||
/**
|
||||
* Gets the value of a parameter by name.
|
||||
*
|
||||
*
|
||||
* Note: Only supports non-texture parameters such as numeric and math types.
|
||||
*
|
||||
*
|
||||
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
|
||||
* @param nameLength Length in `char` of the name parameter.
|
||||
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
|
||||
*
|
||||
*
|
||||
* @see Material::hasParameter
|
||||
*/
|
||||
template<typename T>
|
||||
@@ -262,6 +265,53 @@ public:
|
||||
return getParameter<T>(name, strlen(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a mutable constant parameter by name.
|
||||
*
|
||||
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
|
||||
* @param nameLength Length in `char` of the name parameter.
|
||||
* @param value Value of the parameter to set.
|
||||
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
|
||||
*/
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
void setConstant(const char* UTILS_NONNULL name, size_t nameLength, T const& value);
|
||||
|
||||
/** inline helper to provide the name as a null-terminated string literal */
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
void setConstant(StringLiteral const name, T const& value) {
|
||||
setConstant<T>(name.data, name.size, value);
|
||||
}
|
||||
|
||||
/** inline helper to provide the name as a null-terminated C string */
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
void setConstant(const char* UTILS_NONNULL name, T const& value) {
|
||||
setConstant<T>(name, strlen(name), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a mutable constant parameter by name.
|
||||
*
|
||||
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
|
||||
* @param nameLength Length in `char` of the name parameter.
|
||||
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
|
||||
*
|
||||
* @see Material::hasConstant
|
||||
*/
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
T getConstant(const char* UTILS_NONNULL name, size_t nameLength) const;
|
||||
|
||||
/** inline helper to provide the name as a null-terminated C string */
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
T getConstant(StringLiteral const name) const {
|
||||
return getConstant<T>(name.data, name.size);
|
||||
}
|
||||
|
||||
/** inline helper to provide the name as a null-terminated C string */
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
T getConstant(const char* UTILS_NONNULL name) const {
|
||||
return getConstant<T>(name, strlen(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set-up a custom scissor rectangle; by default it is disabled.
|
||||
*
|
||||
|
||||
@@ -437,7 +437,20 @@ struct AmbientOcclusionOptions {
|
||||
struct Gtao {
|
||||
uint8_t sampleSliceCount = 4; //!< # of slices. Higher value makes less noise.
|
||||
uint8_t sampleStepsPerSlice = 3; //!< # of steps the radius is divided into for integration. Higher value makes less bias.
|
||||
float thicknessHeuristic = 0.004f; //!< thickness heuristic, should be closed to 0
|
||||
float thicknessHeuristic = 0.004f; //!< thickness heuristic, should be closed to 0. No effect when useVisibilityBitmasks sets to true.
|
||||
|
||||
/**
|
||||
* Enables or disables visibility bitmasks mode. Notes that bent normal doesn't work under this mode.
|
||||
* Caution: Changing this option at runtime is very expensive as it may trigger a shader re-compilation.
|
||||
*/
|
||||
bool useVisibilityBitmasks = false;
|
||||
float constThickness = 0.5f; //!< constant thickness value of objects on the screen in world space. Only take effect when useVisibilityBitmasks is set to true.
|
||||
|
||||
/**
|
||||
* Increase thickness with distance to maintain detail on distant surfaces.
|
||||
* Caution: Changing this option at runtime is very expensive as it may trigger a shader re-compilation.
|
||||
*/
|
||||
bool linearThickness = false;
|
||||
};
|
||||
Gtao gtao; // %codegen_skip_javascript% %codegen_java_flatten%
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
@@ -757,6 +758,29 @@ public:
|
||||
//! debugging: returns a Camera from the point of view of *the* dominant directional light used for shadowing.
|
||||
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept;
|
||||
|
||||
//! debugging: enable or disable froxel visualisation for this view.
|
||||
void setFroxelVizEnabled(bool enabled) noexcept;
|
||||
|
||||
//! debugging: returns information about the froxel configuration
|
||||
struct FroxelConfigurationInfo {
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
uint8_t depth;
|
||||
uint32_t viewportWidth;
|
||||
uint32_t viewportHeight;
|
||||
math::uint2 froxelDimension;
|
||||
float zLightFar;
|
||||
float linearizer;
|
||||
math::mat4f p;
|
||||
math::float4 clipTransform;
|
||||
};
|
||||
|
||||
struct FroxelConfigurationInfoWithAge {
|
||||
FroxelConfigurationInfo info;
|
||||
uint32_t age;
|
||||
};
|
||||
|
||||
FroxelConfigurationInfoWithAge getFroxelConfigurationInfo() const noexcept;
|
||||
|
||||
/** Result of a picking query */
|
||||
struct PickingQueryResult {
|
||||
|
||||
@@ -124,6 +124,20 @@ size_t Froxelizer::getFroxelBufferByteCount(FEngine::DriverApi& driverApi) noexc
|
||||
return std::min(FROXEL_BUFFER_MAX_ENTRY_COUNT * sizeof(FroxelEntry), targetSize);
|
||||
}
|
||||
|
||||
View::FroxelConfigurationInfo Froxelizer::getFroxelConfigurationInfo() const noexcept {
|
||||
return { uint8_t(getFroxelCountX()),
|
||||
uint8_t(getFroxelCountY()),
|
||||
uint8_t(getFroxelCountZ()),
|
||||
mViewport.width,
|
||||
mViewport.height,
|
||||
mFroxelDimension,
|
||||
mZLightFar,
|
||||
mLinearizer[0],
|
||||
mProjection,
|
||||
mClipTransform
|
||||
};
|
||||
}
|
||||
|
||||
Froxelizer::Froxelizer(FEngine& engine)
|
||||
: mArena("froxel", PER_FROXELDATA_ARENA_SIZE),
|
||||
mZLightNear(FROXEL_FIRST_SLICE_DEPTH),
|
||||
@@ -198,10 +212,14 @@ void Froxelizer::setProjection(const mat4f& projection,
|
||||
bool Froxelizer::prepare(
|
||||
FEngine::DriverApi& driverApi, RootArenaScope& rootArenaScope,
|
||||
filament::Viewport const& viewport,
|
||||
const mat4f& projection, float const projectionNear, float const projectionFar) noexcept {
|
||||
const mat4f& projection, float const projectionNear, float const projectionFar,
|
||||
float4 const& clipTransform) noexcept {
|
||||
setViewport(viewport);
|
||||
setProjection(projection, projectionNear, projectionFar);
|
||||
|
||||
// Only for debugging
|
||||
mClipTransform = clipTransform;
|
||||
|
||||
bool uniformsNeedUpdating = false;
|
||||
if (UTILS_UNLIKELY(mDirtyFlags)) {
|
||||
uniformsNeedUpdating = update();
|
||||
@@ -361,8 +379,10 @@ bool Froxelizer::update() noexcept {
|
||||
getFroxelBufferEntryCount(), viewport);
|
||||
|
||||
mFroxelDimension = froxelDimension;
|
||||
mClipToFroxelX = (0.5f * float(viewport.width)) / float(froxelDimension.x);
|
||||
mClipToFroxelY = (0.5f * float(viewport.height)) / float(froxelDimension.y);
|
||||
// note: because froxelDimension is a power-of-two and viewport is an integer, mClipFroxel
|
||||
// is an exact value (which is not true for 1/mClipToFroxelX, btw)
|
||||
mClipToFroxelX = float(viewport.width) / float(2 * froxelDimension.x);
|
||||
mClipToFroxelY = float(viewport.height) / float(2 * froxelDimension.y);
|
||||
|
||||
uniformsNeedUpdating = true;
|
||||
|
||||
@@ -408,8 +428,7 @@ bool Froxelizer::update() noexcept {
|
||||
}
|
||||
|
||||
// for the inverse-transformation (view-space z to z-slice)
|
||||
mLinearizer = 1.0f / linearizer;
|
||||
mZLightFar = zLightFar;
|
||||
mLinearizer = { linearizer, 1.0f / linearizer };
|
||||
|
||||
mParamsZ[0] = 0; // updated when camera changes
|
||||
mParamsZ[1] = 0; // updated when camera changes
|
||||
@@ -466,7 +485,7 @@ bool Froxelizer::update() noexcept {
|
||||
// ==> i = log2(z_screen * (far/near)) * (-1/linearizer) + zcount
|
||||
mParamsZ[0] = mZLightFar / Pw;
|
||||
mParamsZ[1] = 0.0f;
|
||||
mParamsZ[2] = -mLinearizer;
|
||||
mParamsZ[2] = -mLinearizer[1];
|
||||
} else {
|
||||
// orthographic projection
|
||||
// z_view = (1 - z_screen) * (near - far) - near
|
||||
@@ -476,7 +495,7 @@ bool Froxelizer::update() noexcept {
|
||||
// Pw = far / (far - near)
|
||||
mParamsZ[0] = -1.0f / (Pz * mZLightFar); // -(far-near) / mZLightFar
|
||||
mParamsZ[1] = Pw / (Pz * mZLightFar); // far / mZLightFar
|
||||
mParamsZ[2] = mLinearizer;
|
||||
mParamsZ[2] = mLinearizer[1];
|
||||
}
|
||||
uniformsNeedUpdating = true;
|
||||
}
|
||||
@@ -507,7 +526,7 @@ size_t Froxelizer::findSliceZ(float const z) const noexcept {
|
||||
|
||||
// This whole function is now branch-less.
|
||||
|
||||
int s = int( fast::log2(-z / mZLightFar) * mLinearizer + float(mFroxelCountZ) );
|
||||
int s = int( fast::log2(-z / mZLightFar) * mLinearizer[1] + float(mFroxelCountZ) );
|
||||
|
||||
// there are cases where z can be negative here, e.g.:
|
||||
// - the light is visible, but its center is behind the camera
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "details/Scene.h"
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include <filament/View.h>
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
#include <backend/Handle.h>
|
||||
@@ -31,6 +32,7 @@
|
||||
#include <utils/Slice.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
namespace filament {
|
||||
@@ -112,7 +114,8 @@ public:
|
||||
* return true if updateUniforms() needs to be called
|
||||
*/
|
||||
bool prepare(backend::DriverApi& driverApi, RootArenaScope& rootArenaScope, Viewport const& viewport,
|
||||
const math::mat4f& projection, float projectionNear, float projectionFar) noexcept;
|
||||
const math::mat4f& projection, float projectionNear, float projectionFar,
|
||||
math::float4 const& clipTransform) noexcept;
|
||||
|
||||
Froxel getFroxelAt(size_t x, size_t y, size_t z) const noexcept;
|
||||
size_t getFroxelCountX() const noexcept { return mFroxelCountX; }
|
||||
@@ -161,6 +164,8 @@ public:
|
||||
|
||||
static size_t getFroxelBufferByteCount(FEngine::DriverApi& driverApi) noexcept;
|
||||
|
||||
View::FroxelConfigurationInfo getFroxelConfigurationInfo() const noexcept;
|
||||
|
||||
private:
|
||||
size_t getFroxelBufferEntryCount() const noexcept {
|
||||
return mFroxelBufferEntryCount;
|
||||
@@ -260,9 +265,10 @@ private:
|
||||
uint16_t mFroxelCountZ = 0;
|
||||
uint32_t mFroxelCount = 0;
|
||||
math::uint2 mFroxelDimension = {};
|
||||
math::float4 mClipTransform = { 1, 1, 0, 0 };
|
||||
|
||||
math::mat4f mProjection;
|
||||
float mLinearizer = 0.0f;
|
||||
math::float2 mLinearizer{};
|
||||
float mClipToFroxelX = 0.0f;
|
||||
float mClipToFroxelY = 0.0f;
|
||||
backend::BufferObjectHandle mRecordsBuffer;
|
||||
|
||||
@@ -233,6 +233,46 @@ template UTILS_PUBLIC mat3f MaterialInstance::getParameter<mat3f> (const ch
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<>
|
||||
inline void FMaterialInstance::setConstantImpl<bool>(std::string_view const name, bool const& value) {
|
||||
std::optional<uint32_t> id = mMaterial->getMutableConstantId(name);
|
||||
FILAMENT_CHECK_PRECONDITION(id.has_value()) << "No mutable constant with name " << name;
|
||||
mConstants.set(*id, value);
|
||||
}
|
||||
|
||||
template<typename T, typename>
|
||||
void MaterialInstance::setConstant(const char* name, size_t nameLength, T const& value) {
|
||||
downcast(this)->setConstantImpl({ name, nameLength }, value);
|
||||
}
|
||||
|
||||
// Explicit template instantiation of our supported types.
|
||||
//
|
||||
// Mutable spec constants will probably only ever allow bools, but it's nice to keep the API
|
||||
// forwards-compatible.
|
||||
template UTILS_PUBLIC void MaterialInstance::setConstant<bool>(const char* name, size_t nameLength, bool const& v);
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<>
|
||||
inline bool FMaterialInstance::getConstantImpl<bool>(std::string_view const name) const {
|
||||
std::optional<uint32_t> id = mMaterial->getMutableConstantId(name);
|
||||
FILAMENT_CHECK_PRECONDITION(id.has_value()) << "No mutable constant with name " << name;
|
||||
return mConstants[*id];
|
||||
}
|
||||
|
||||
template<typename T, typename>
|
||||
T MaterialInstance::getConstant(const char* name, size_t nameLength) const {
|
||||
return downcast(this)->getConstantImpl<T>({ name, nameLength });
|
||||
}
|
||||
|
||||
// Explicit template instantiation of our supported types.
|
||||
//
|
||||
// Mutable spec constants will probably only ever allow bools, but it's nice to keep the API
|
||||
// forwards-compatible.
|
||||
template UTILS_PUBLIC bool MaterialInstance::getConstant<bool>(const char* name, size_t nameLength) const;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
Material const* MaterialInstance::getMaterial() const noexcept {
|
||||
return downcast(this)->getMaterial();
|
||||
}
|
||||
|
||||
@@ -229,6 +229,11 @@ bool MaterialParser::getConstants(FixedCapacityVector<MaterialConstant>* contain
|
||||
return get<ChunkMaterialConstants>(container);
|
||||
}
|
||||
|
||||
bool MaterialParser::getMutableConstants(
|
||||
FixedCapacityVector<MaterialMutableConstant>* container) const noexcept {
|
||||
return get<ChunkMaterialMutableConstants>(container);
|
||||
}
|
||||
|
||||
bool MaterialParser::getPushConstants(CString* structVarName,
|
||||
FixedCapacityVector<MaterialPushConstant>* value) const noexcept {
|
||||
auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialPushConstants);
|
||||
@@ -761,6 +766,38 @@ bool ChunkMaterialConstants::unflatten(Unflattener& unflattener,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChunkMaterialMutableConstants::unflatten(Unflattener& unflattener,
|
||||
FixedCapacityVector<MaterialMutableConstant>* materialConstants) {
|
||||
assert_invariant(materialConstants);
|
||||
|
||||
// Read number of constants.
|
||||
uint64_t numConstants = 0;
|
||||
if (!unflattener.read(&numConstants)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
materialConstants->reserve(numConstants);
|
||||
materialConstants->resize(numConstants);
|
||||
|
||||
for (uint64_t i = 0; i < numConstants; i++) {
|
||||
CString constantName;
|
||||
bool defaultValue;
|
||||
|
||||
if (!unflattener.read(&constantName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!unflattener.read(&defaultValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
(*materialConstants)[i].name = std::move(constantName);
|
||||
(*materialConstants)[i].defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChunkMaterialPushConstants::unflatten(Unflattener& unflattener,
|
||||
CString* structVarName,
|
||||
FixedCapacityVector<MaterialPushConstant>* materialPushConstants) {
|
||||
|
||||
@@ -49,6 +49,7 @@ class BufferInterfaceBlock;
|
||||
class SamplerInterfaceBlock;
|
||||
struct SubpassInfo;
|
||||
struct MaterialConstant;
|
||||
struct MaterialMutableConstant;
|
||||
struct MaterialPushConstant;
|
||||
|
||||
class MaterialParser {
|
||||
@@ -79,6 +80,8 @@ public:
|
||||
bool getShaderModels(uint32_t* value) const noexcept;
|
||||
bool getMaterialProperties(uint64_t* value) const noexcept;
|
||||
bool getConstants(utils::FixedCapacityVector<MaterialConstant>* value) const noexcept;
|
||||
bool getMutableConstants(utils::FixedCapacityVector<MaterialMutableConstant>* value)
|
||||
const noexcept;
|
||||
bool getPushConstants(utils::CString* structVarName,
|
||||
utils::FixedCapacityVector<MaterialPushConstant>* value) const noexcept;
|
||||
|
||||
@@ -238,6 +241,13 @@ struct ChunkMaterialConstants {
|
||||
static filamat::ChunkType const tag = filamat::MaterialConstants;
|
||||
};
|
||||
|
||||
struct ChunkMaterialMutableConstants {
|
||||
static bool unflatten(filaflat::Unflattener& unflattener,
|
||||
utils::FixedCapacityVector<MaterialMutableConstant>* materialConstants);
|
||||
using Container = utils::FixedCapacityVector<MaterialMutableConstant>;
|
||||
static filamat::ChunkType const tag = filamat::MaterialMutableConstants;
|
||||
};
|
||||
|
||||
struct ChunkMaterialPushConstants {
|
||||
static bool unflatten(filaflat::Unflattener& unflattener, utils::CString* structVarName,
|
||||
utils::FixedCapacityVector<MaterialPushConstant>* materialPushConstants);
|
||||
|
||||
@@ -199,6 +199,8 @@ FMaterial* PostProcessManager::PostProcessMaterial::getMaterial(FEngine& engine,
|
||||
loadMaterial(engine);
|
||||
}
|
||||
mMaterial->prepareProgram(Variant{ Variant::type_t(variant) },
|
||||
// TODO(exv): allow post process materials to use mutable spec constants?
|
||||
Program::MutableSpecConstantsInfo(0),
|
||||
CompilerPriorityQueue::CRITICAL);
|
||||
return mMaterial;
|
||||
}
|
||||
@@ -465,7 +467,10 @@ UTILS_NOINLINE
|
||||
PipelineState PostProcessManager::getPipelineState(
|
||||
FMaterial const* const ma, PostProcessVariant variant) const noexcept {
|
||||
return {
|
||||
.program = ma->getProgram(Variant{ Variant::type_t(variant) }),
|
||||
.program = ma->getProgram(Variant{ Variant::type_t(variant) },
|
||||
// TODO(exv): allow post process materials to use mutable spec constants?
|
||||
Program::MutableSpecConstantsInfo(0)
|
||||
),
|
||||
.vertexBufferInfo = mFullScreenQuadVbih,
|
||||
.pipelineLayout = {
|
||||
.setLayout = {
|
||||
@@ -993,7 +998,17 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
#endif
|
||||
auto& material = getPostProcessMaterial(materialName);
|
||||
|
||||
FMaterial const * const ma = material.getMaterial(mEngine);
|
||||
FMaterial* ma = material.getMaterial(mEngine);
|
||||
bool dirty = false;
|
||||
setConstantParameter(ma, "useVisibilityBitmasks", options.gtao.useVisibilityBitmasks, dirty);
|
||||
setConstantParameter(ma, "linearThickness", options.gtao.linearThickness, dirty);
|
||||
if (dirty) {
|
||||
ma->invalidate();
|
||||
// TODO: call Material::compile(), we can't do that now because it works only
|
||||
// with surface materials
|
||||
}
|
||||
|
||||
ma = material.getMaterial(mEngine);
|
||||
FMaterialInstance* const mi = getMaterialInstance(ma);
|
||||
|
||||
// Set AO type specific material parameters
|
||||
@@ -1028,6 +1043,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
mi->setParameter("radius", options.radius);
|
||||
mi->setParameter("intensity", options.intensity);
|
||||
mi->setParameter("thicknessHeuristic", options.gtao.thicknessHeuristic);
|
||||
mi->setParameter("constThickness", options.gtao.constThickness);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -243,7 +243,9 @@ void RenderPass::appendCommands(FEngine const& engine,
|
||||
for (Command const* first = curr, *last = curr + commandCount ; first != last ; ++first) {
|
||||
if (UTILS_LIKELY((first->key & CUSTOM_MASK) == uint64_t(CustomCommand::PASS))) {
|
||||
auto ma = first->info.mi->getMaterial();
|
||||
ma->prepareProgram(first->info.materialVariant, CompilerPriorityQueue::CRITICAL);
|
||||
ma->prepareProgram(first->info.materialVariant,
|
||||
first->info.mi->getMutableSpecConstants(),
|
||||
CompilerPriorityQueue::CRITICAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1096,7 +1098,8 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
|
||||
}
|
||||
|
||||
assert_invariant(ma);
|
||||
pipeline.program = ma->getProgram(info.materialVariant);
|
||||
pipeline.program = ma->getProgram(info.materialVariant,
|
||||
info.mi->getMutableSpecConstants());
|
||||
|
||||
if (UTILS_UNLIKELY(memcmp(&pipeline, ¤tPipeline, sizeof(PipelineState)) != 0)) {
|
||||
currentPipeline = pipeline;
|
||||
|
||||
@@ -77,6 +77,14 @@ utils::FixedCapacityVector<Camera const*> View::getDirectionalShadowCameras() co
|
||||
return downcast(this)->getDirectionalShadowCameras();
|
||||
}
|
||||
|
||||
void View::setFroxelVizEnabled(bool const enabled) noexcept {
|
||||
downcast(this)->setFroxelVizEnabled(enabled);
|
||||
}
|
||||
|
||||
View::FroxelConfigurationInfoWithAge View::getFroxelConfigurationInfo() const noexcept {
|
||||
return downcast(this)->getFroxelConfigurationInfo();
|
||||
}
|
||||
|
||||
void View::setShadowingEnabled(bool const enabled) noexcept {
|
||||
downcast(this)->setShadowingEnabled(enabled);
|
||||
}
|
||||
|
||||
@@ -748,7 +748,7 @@ public:
|
||||
bool enable_staging_buffer_bypass = false;
|
||||
} vulkan;
|
||||
bool disable_parallel_shader_compile = false;
|
||||
bool disable_amortized_shader_compile = false;
|
||||
bool disable_amortized_shader_compile = true;
|
||||
bool disable_handle_use_after_free_check = false;
|
||||
bool disable_heap_handle_tags = true; // FIXME: this should be false
|
||||
} backend;
|
||||
|
||||
@@ -364,8 +364,24 @@ FMaterial::FMaterial(FEngine& engine, const Builder& builder,
|
||||
|
||||
processBlendingMode(parser);
|
||||
processSpecializationConstants(engine, builder, parser);
|
||||
processMutableSpecializationConstants(engine, builder, parser);
|
||||
processPushConstants(engine, parser);
|
||||
processDescriptorSets(engine, parser);
|
||||
|
||||
switch (mMaterialDomain) {
|
||||
case filament::MaterialDomain::SURFACE:
|
||||
mVariantBits = VARIANT_BITS;
|
||||
break;
|
||||
case filament::MaterialDomain::POST_PROCESS:
|
||||
mVariantBits = POST_PROCESS_VARIANT_BITS;
|
||||
break;
|
||||
case filament::MaterialDomain::COMPUTE:
|
||||
mVariantBits = 0;
|
||||
break;
|
||||
}
|
||||
mCachedPrograms = FixedCapacityVector<backend::Handle<backend::HwProgram>>(
|
||||
1 << (mVariantBits + mMaterialMutableConstants.size()));
|
||||
|
||||
precacheDepthVariants(engine);
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
@@ -466,13 +482,17 @@ void FMaterial::compile(CompilerPriorityQueue const priority,
|
||||
UserVariantFilterMask const variantFilter =
|
||||
~variantSpec & UserVariantFilterMask(UserVariantFilterBit::ALL);
|
||||
|
||||
int mutableConstantVariations = 1 << mMaterialMutableConstants.size();
|
||||
|
||||
if (UTILS_LIKELY(mEngine.getDriverApi().isParallelShaderCompileSupported())) {
|
||||
auto const& variants = isVariantLit() ?
|
||||
VariantUtils::getLitVariants() : VariantUtils::getUnlitVariants();
|
||||
for (auto const variant: variants) {
|
||||
if (!variantFilter || variant == Variant::filterUserVariant(variant, variantFilter)) {
|
||||
if (hasVariant(variant)) {
|
||||
prepareProgram(variant, priority);
|
||||
for (int spec = 0; spec < mutableConstantVariations; spec++) {
|
||||
prepareProgram(variant, Program::MutableSpecConstantsInfo(spec), priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -553,14 +573,15 @@ bool FMaterial::hasVariant(Variant const variant) const noexcept {
|
||||
}
|
||||
|
||||
void FMaterial::prepareProgramSlow(Variant const variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
backend::CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
assert_invariant(mEngine.hasFeatureLevel(mFeatureLevel));
|
||||
switch (getMaterialDomain()) {
|
||||
case MaterialDomain::SURFACE:
|
||||
getSurfaceProgramSlow(variant, priorityQueue);
|
||||
getSurfaceProgramSlow(variant, mutableSpecConstants, priorityQueue);
|
||||
break;
|
||||
case MaterialDomain::POST_PROCESS:
|
||||
getPostProcessProgramSlow(variant, priorityQueue);
|
||||
getPostProcessProgramSlow(variant, mutableSpecConstants, priorityQueue);
|
||||
break;
|
||||
case MaterialDomain::COMPUTE:
|
||||
// TODO: implement MaterialDomain::COMPUTE
|
||||
@@ -569,6 +590,7 @@ void FMaterial::prepareProgramSlow(Variant const variant,
|
||||
}
|
||||
|
||||
void FMaterial::getSurfaceProgramSlow(Variant const variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
// filterVariant() has already been applied in generateCommands(), shouldn't be needed here
|
||||
// if we're unlit, we don't have any bits that correspond to lit materials
|
||||
@@ -579,25 +601,25 @@ void FMaterial::getSurfaceProgramSlow(Variant const variant,
|
||||
Variant const vertexVariant = Variant::filterVariantVertex(variant);
|
||||
Variant const fragmentVariant = Variant::filterVariantFragment(variant);
|
||||
|
||||
Program pb{ getProgramWithVariants(variant, vertexVariant, fragmentVariant) };
|
||||
Program pb{ getProgramWithVariants(variant, vertexVariant, fragmentVariant, mutableSpecConstants) };
|
||||
pb.priorityQueue(priorityQueue);
|
||||
pb.multiview(
|
||||
mEngine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW &&
|
||||
Variant::isStereoVariant(variant));
|
||||
createAndCacheProgram(std::move(pb), variant);
|
||||
createAndCacheProgram(std::move(pb), variant, mutableSpecConstants);
|
||||
}
|
||||
|
||||
void FMaterial::getPostProcessProgramSlow(Variant const variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
Program pb{ getProgramWithVariants(variant, variant, variant) };
|
||||
Program pb{ getProgramWithVariants(variant, variant, variant, mutableSpecConstants) };
|
||||
pb.priorityQueue(priorityQueue);
|
||||
createAndCacheProgram(std::move(pb), variant);
|
||||
createAndCacheProgram(std::move(pb), variant, mutableSpecConstants);
|
||||
}
|
||||
|
||||
Program FMaterial::getProgramWithVariants(
|
||||
Variant variant,
|
||||
Variant vertexVariant,
|
||||
Variant fragmentVariant) const {
|
||||
Program FMaterial::getProgramWithVariants(Variant variant,
|
||||
Variant vertexVariant, Variant fragmentVariant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const {
|
||||
FEngine const& engine = mEngine;
|
||||
const ShaderModel sm = engine.getShaderModel();
|
||||
const bool isNoop = engine.getBackend() == Backend::NOOP;
|
||||
@@ -658,29 +680,34 @@ Program FMaterial::getProgramWithVariants(
|
||||
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_RENDERABLE]);
|
||||
program.descriptorBindings(+DescriptorSetBindingPoints::PER_MATERIAL,
|
||||
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_MATERIAL]);
|
||||
program.specializationConstants(mSpecializationConstants);
|
||||
program.specializationConstants(mSpecializationConstants,
|
||||
CONFIG_MAX_RESERVED_SPEC_CONSTANTS + mMaterialConstants.size(), mutableSpecConstants);
|
||||
|
||||
program.pushConstants(ShaderStage::VERTEX, mPushConstants[uint8_t(ShaderStage::VERTEX)]);
|
||||
program.pushConstants(ShaderStage::FRAGMENT, mPushConstants[uint8_t(ShaderStage::FRAGMENT)]);
|
||||
|
||||
program.cacheId(hash::combine(size_t(mCacheId), variant.key));
|
||||
program.cacheId(
|
||||
hash::combine(size_t(mCacheId),
|
||||
hash::combine(variant.key, mutableSpecConstants.getValue())));
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
void FMaterial::createAndCacheProgram(Program&& p, Variant const variant) const noexcept {
|
||||
void FMaterial::createAndCacheProgram(Program&& p, Variant const variant,
|
||||
Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept {
|
||||
FEngine const& engine = mEngine;
|
||||
DriverApi& driverApi = mEngine.getDriverApi();
|
||||
|
||||
bool const isShared = isSharedVariant(variant);
|
||||
size_t const cacheIndex = getCachedProgramIndex(variant, mutableSpecConstants);
|
||||
|
||||
// Check if the default material has this program cached
|
||||
if (isShared) {
|
||||
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
|
||||
if (pDefaultMaterial) {
|
||||
auto const program = pDefaultMaterial->mCachedPrograms[variant.key];
|
||||
auto const program = pDefaultMaterial->mCachedPrograms[cacheIndex];
|
||||
if (program) {
|
||||
mCachedPrograms[variant.key] = program;
|
||||
mCachedPrograms[cacheIndex] = program;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -689,17 +716,17 @@ void FMaterial::createAndCacheProgram(Program&& p, Variant const variant) const
|
||||
auto const program = driverApi.createProgram(std::move(p));
|
||||
driverApi.setDebugTag(program.getId(), mName);
|
||||
assert_invariant(program);
|
||||
mCachedPrograms[variant.key] = program;
|
||||
mCachedPrograms[cacheIndex] = program;
|
||||
|
||||
// If the default material doesn't already have this program cached, and all caching conditions
|
||||
// are met (Surface Domain and no custom depth shader), cache it now.
|
||||
// New Materials will inherit these program automatically.
|
||||
if (isShared) {
|
||||
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
|
||||
if (pDefaultMaterial && !pDefaultMaterial->mCachedPrograms[variant.key]) {
|
||||
if (pDefaultMaterial && !pDefaultMaterial->mCachedPrograms[cacheIndex]) {
|
||||
// set the tag to the default material name
|
||||
driverApi.setDebugTag(program.getId(), mName);
|
||||
pDefaultMaterial->mCachedPrograms[variant.key] = program;
|
||||
pDefaultMaterial->mCachedPrograms[cacheIndex] = program;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -747,6 +774,14 @@ size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const n
|
||||
return count;
|
||||
}
|
||||
|
||||
std::optional<uint32_t> FMaterial::getMutableConstantId(std::string_view name) const noexcept {
|
||||
auto const pos = mMutableSpecializationConstantsNameToIndex.find(name);
|
||||
if (pos != mMutableSpecializationConstantsNameToIndex.end()) {
|
||||
return pos->second;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
|
||||
// Swaps in an edited version of the original package that was used to create the material. The
|
||||
@@ -805,7 +840,9 @@ void FMaterial::onQueryCallback(void* userdata, VariantList* pActiveVariants) {
|
||||
|
||||
#endif // FILAMENT_ENABLE_MATDBG
|
||||
|
||||
[[nodiscard]] Handle<HwProgram> FMaterial::getProgramWithMATDBG(Variant const variant) const noexcept {
|
||||
[[nodiscard]] Handle<HwProgram> FMaterial::getProgramWithMATDBG(Variant const variant,
|
||||
Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept {
|
||||
size_t cacheIndex = getCachedProgramIndex(variant, mutableSpecConstants);
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
assert_invariant((size_t)variant.key < VARIANT_COUNT);
|
||||
std::unique_lock lock(mActiveProgramsLock);
|
||||
@@ -820,13 +857,13 @@ void FMaterial::onQueryCallback(void* userdata, VariantList* pActiveVariants) {
|
||||
lock.unlock();
|
||||
if (isSharedVariant(variant)) {
|
||||
FMaterial const* const pDefaultMaterial = mEngine.getDefaultMaterial();
|
||||
if (pDefaultMaterial && pDefaultMaterial->mCachedPrograms[variant.key]) {
|
||||
return pDefaultMaterial->getProgram(variant);
|
||||
if (pDefaultMaterial && pDefaultMaterial->mCachedPrograms[cacheIndex]) {
|
||||
return pDefaultMaterial->getProgram(variant, mutableSpecConstants);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
assert_invariant(mCachedPrograms[variant.key]);
|
||||
return mCachedPrograms[variant.key];
|
||||
assert_invariant(mCachedPrograms[cacheIndex]);
|
||||
return mCachedPrograms[cacheIndex];
|
||||
}
|
||||
|
||||
void FMaterial::destroyPrograms(FEngine& engine,
|
||||
@@ -839,7 +876,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
case MaterialDomain::SURFACE: {
|
||||
if (mIsDefaultMaterial || mHasCustomDepthShader) {
|
||||
// default material, or we have custom depth shaders, we destroy all variants
|
||||
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
|
||||
for (size_t k = 0, n = cachedPrograms.size(); k < n; ++k) {
|
||||
if ((k & variantMask) == variantValue) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variants, and this generates traffic in the command queue.
|
||||
@@ -857,7 +894,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
UTILS_UNUSED_IN_RELEASE
|
||||
auto const UTILS_NULLABLE pDefaultMaterial = engine.getDefaultMaterial();
|
||||
|
||||
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
|
||||
for (size_t k = 0, n = cachedPrograms.size(); k < n; ++k) {
|
||||
if ((k & variantMask) == variantValue) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variant, and this generates traffic in the command queue.
|
||||
@@ -869,7 +906,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
// During Engine::shutdown, auto-cleanup destroys the
|
||||
// default material first
|
||||
assert_invariant(!pDefaultMaterial ||
|
||||
pDefaultMaterial->mCachedPrograms[k]);
|
||||
pDefaultMaterial->mCachedPrograms[k & (VARIANT_COUNT - 1)]);
|
||||
// we don't own this variant, skip, but clear the entry.
|
||||
cachedPrograms[k].clear();
|
||||
continue;
|
||||
@@ -883,7 +920,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
break;
|
||||
}
|
||||
case MaterialDomain::POST_PROCESS: {
|
||||
for (size_t k = 0, n = POST_PROCESS_VARIANT_COUNT; k < n; ++k) {
|
||||
for (size_t k = 0, n = cachedPrograms.size(); k < n; ++k) {
|
||||
if ((k & variantMask) == variantValue) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variant, and this generates traffic in the command queue.
|
||||
@@ -895,8 +932,13 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
break;
|
||||
}
|
||||
case MaterialDomain::COMPUTE: {
|
||||
// Compute programs don't have variants
|
||||
driverApi.destroyProgram(std::move(cachedPrograms[0]));
|
||||
for (size_t k = 0, n = cachedPrograms.size(); k < n; ++k) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variant, and this generates traffic in the command queue.
|
||||
if (cachedPrograms[k]) {
|
||||
driverApi.destroyProgram(std::move(cachedPrograms[k]));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1131,6 +1173,19 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Builder const& b
|
||||
}
|
||||
}
|
||||
|
||||
void FMaterial::processMutableSpecializationConstants(FEngine& engine, Builder const& builder,
|
||||
MaterialParser const* const parser) {
|
||||
// Older materials won't have a mutable constants chunk, but that's okay.
|
||||
parser->getMutableConstants(&mMaterialMutableConstants);
|
||||
for (size_t i = 0, c = mMaterialMutableConstants.size(); i < c; i++) {
|
||||
auto& item = mMaterialMutableConstants[i];
|
||||
// the key can be a string_view because mMaterialMutableConstants owns the CString
|
||||
std::string_view const key{ item.name.data(), item.name.size() };
|
||||
mMutableSpecializationConstantsNameToIndex[key] = i;
|
||||
mDefaultMutableSpecializationConstants.set(i, item.defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
void FMaterial::processPushConstants(FEngine&, MaterialParser const* parser) {
|
||||
FixedCapacityVector<Program::PushConstant>& vertexConstants =
|
||||
mPushConstants[uint8_t(ShaderStage::VERTEX)];
|
||||
@@ -1186,7 +1241,9 @@ void FMaterial::precacheDepthVariants(FEngine& engine) {
|
||||
}
|
||||
assert_invariant(Variant::isValidDepthVariant(variant));
|
||||
if (hasVariant(variant)) {
|
||||
prepareProgram(variant, CompilerPriorityQueue::HIGH);
|
||||
// The default material shouldn't ever have mutable spec constants.
|
||||
prepareProgram(variant, Program::MutableSpecConstantsInfo(0),
|
||||
CompilerPriorityQueue::HIGH);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -1199,9 +1256,16 @@ void FMaterial::precacheDepthVariants(FEngine& engine) {
|
||||
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
|
||||
assert_invariant(pDefaultMaterial);
|
||||
auto const allDepthVariants = VariantUtils::getDepthVariants();
|
||||
int mutableConstantVariations = 1 << mMaterialMutableConstants.size();
|
||||
for (auto const variant: allDepthVariants) {
|
||||
assert_invariant(Variant::isValidDepthVariant(variant));
|
||||
mCachedPrograms[variant.key] = pDefaultMaterial->mCachedPrograms[variant.key];
|
||||
size_t defaultCacheIndex = pDefaultMaterial->getCachedProgramIndex(variant,
|
||||
Program::MutableSpecConstantsInfo(0));
|
||||
for (int spec = 0; spec < mutableConstantVariations; spec++) {
|
||||
size_t cacheIndex = getCachedProgramIndex(variant,
|
||||
Program::MutableSpecConstantsInfo(spec));
|
||||
mCachedPrograms[cacheIndex] = pDefaultMaterial->mCachedPrograms[defaultCacheIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,8 +132,9 @@ public:
|
||||
|
||||
FEngine& getEngine() const noexcept { return mEngine; }
|
||||
|
||||
bool isCached(Variant const variant) const noexcept {
|
||||
return bool(mCachedPrograms[variant.key]);
|
||||
bool isCached(Variant const variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const noexcept {
|
||||
return bool(mCachedPrograms[getCachedProgramIndex(variant, mutableSpecConstants)]);
|
||||
}
|
||||
|
||||
void invalidate(Variant::type_t variantMask = 0, Variant::type_t variantValue = 0) noexcept;
|
||||
@@ -142,22 +143,26 @@ public:
|
||||
// Must be called outside of backend render pass.
|
||||
// Must be called before getProgram() below.
|
||||
void prepareProgram(Variant const variant,
|
||||
backend::Program::MutableSpecConstantsInfo const mutableSpecConstants,
|
||||
backend::CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
// prepareProgram() is called for each RenderPrimitive in the scene, so it must be efficient.
|
||||
if (UTILS_UNLIKELY(!isCached(variant))) {
|
||||
prepareProgramSlow(variant, priorityQueue);
|
||||
if (UTILS_UNLIKELY(!isCached(variant, mutableSpecConstants))) {
|
||||
prepareProgramSlow(variant, mutableSpecConstants, priorityQueue);
|
||||
}
|
||||
}
|
||||
|
||||
// getProgram returns the backend program for the material's given variant.
|
||||
// getProgram returns the backend program for the material's given variant and set of mutable
|
||||
// spec constants.
|
||||
// Must be called after prepareProgram().
|
||||
[[nodiscard]]
|
||||
backend::Handle<backend::HwProgram> getProgram(Variant const variant) const noexcept {
|
||||
backend::Handle<backend::HwProgram> getProgram(Variant const variant,
|
||||
backend::Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept {
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
return getProgramWithMATDBG(variant);
|
||||
return getProgramWithMATDBG(variant, mutableSpecConstants);
|
||||
#endif
|
||||
assert_invariant(mCachedPrograms[variant.key]);
|
||||
return mCachedPrograms[variant.key];
|
||||
size_t index = getCachedProgramIndex(variant, mutableSpecConstants);
|
||||
assert_invariant(mCachedPrograms[index]);
|
||||
return mCachedPrograms[index];
|
||||
}
|
||||
|
||||
// MaterialInstance::use() binds descriptor sets before drawing. For shared variants,
|
||||
@@ -178,7 +183,8 @@ public:
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
backend::Handle<backend::HwProgram> getProgramWithMATDBG(Variant variant) const noexcept;
|
||||
backend::Handle<backend::HwProgram> getProgramWithMATDBG(Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept;
|
||||
|
||||
bool isVariantLit() const noexcept { return mIsVariantLit; }
|
||||
|
||||
@@ -235,6 +241,12 @@ public:
|
||||
}
|
||||
size_t getParameters(ParameterInfo* parameters, size_t count) const noexcept;
|
||||
|
||||
uint32_t getMutableConstantCount() const noexcept { return mMaterialMutableConstants.size(); }
|
||||
std::optional<uint32_t> getMutableConstantId(std::string_view name) const noexcept;
|
||||
backend::Program::MutableSpecConstantsInfo getDefaultMutableConstants() const noexcept {
|
||||
return mDefaultMutableSpecializationConstants;
|
||||
}
|
||||
|
||||
uint32_t generateMaterialInstanceId() const noexcept { return mMaterialInstanceId++; }
|
||||
|
||||
void destroyPrograms(FEngine& engine,
|
||||
@@ -288,34 +300,48 @@ public:
|
||||
private:
|
||||
bool hasVariant(Variant variant) const noexcept;
|
||||
void prepareProgramSlow(Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue priorityQueue) const noexcept;
|
||||
void getSurfaceProgramSlow(Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue priorityQueue) const noexcept;
|
||||
void getPostProcessProgramSlow(Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue priorityQueue) const noexcept;
|
||||
backend::Program getProgramWithVariants(Variant variant,
|
||||
Variant vertexVariant, Variant fragmentVariant) const;
|
||||
Variant vertexVariant, Variant fragmentVariant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const;
|
||||
|
||||
void processBlendingMode(MaterialParser const* parser);
|
||||
|
||||
void processSpecializationConstants(FEngine& engine, Builder const& builder,
|
||||
MaterialParser const* parser);
|
||||
|
||||
void processMutableSpecializationConstants(FEngine& engine, Builder const& builder,
|
||||
MaterialParser const* parser);
|
||||
|
||||
void processPushConstants(FEngine& engine, MaterialParser const* parser);
|
||||
|
||||
void precacheDepthVariants(FEngine& engine);
|
||||
|
||||
void processDescriptorSets(FEngine& engine, MaterialParser const* parser);
|
||||
|
||||
void createAndCacheProgram(backend::Program&& p, Variant variant) const noexcept;
|
||||
void createAndCacheProgram(backend::Program&& p, Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const noexcept;
|
||||
|
||||
inline bool isSharedVariant(Variant const variant) const {
|
||||
return (mMaterialDomain == MaterialDomain::SURFACE) && !mIsDefaultMaterial &&
|
||||
!mHasCustomDepthShader && Variant::isValidDepthVariant(variant);
|
||||
}
|
||||
|
||||
inline size_t getCachedProgramIndex(const Variant variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const noexcept {
|
||||
return static_cast<size_t>(variant.key) |
|
||||
(static_cast<size_t>(mutableSpecConstants.getValue()) << mVariantBits);
|
||||
}
|
||||
|
||||
// try to order by frequency of use
|
||||
mutable std::array<backend::Handle<backend::HwProgram>, VARIANT_COUNT> mCachedPrograms;
|
||||
mutable utils::FixedCapacityVector<backend::Handle<backend::HwProgram>> mCachedPrograms;
|
||||
DescriptorSetLayout mPerViewDescriptorSetLayout;
|
||||
DescriptorSetLayout mPerViewDescriptorSetLayoutVsm;
|
||||
DescriptorSetLayout mDescriptorSetLayout;
|
||||
@@ -351,6 +377,7 @@ private:
|
||||
bool mHasCustomDepthShader = false;
|
||||
bool mIsDefaultMaterial = false;
|
||||
bool mSpecularAntiAliasing = false;
|
||||
uint8_t mVariantBits;
|
||||
|
||||
// reserve some space to construct the default material instance
|
||||
mutable FMaterialInstance* mDefaultMaterialInstance = nullptr;
|
||||
@@ -375,6 +402,13 @@ private:
|
||||
// current specialization constants for the HwProgram
|
||||
utils::FixedCapacityVector<backend::Program::SpecializationConstant> mSpecializationConstants;
|
||||
|
||||
// Mutable constants defined by this Material.
|
||||
utils::FixedCapacityVector<MaterialMutableConstant> mMaterialMutableConstants;
|
||||
// A map from the Constant name to the mMutableMaterialConstant index
|
||||
std::unordered_map<std::string_view, uint32_t> mMutableSpecializationConstantsNameToIndex;
|
||||
// Default values for the mutable constants.
|
||||
backend::Program::MutableSpecConstantsInfo mDefaultMutableSpecializationConstants;
|
||||
|
||||
// current push constants for the HwProgram
|
||||
std::array<utils::FixedCapacityVector<backend::Program::PushConstant>,
|
||||
backend::Program::SHADER_TYPE_COUNT>
|
||||
|
||||
@@ -63,6 +63,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
|
||||
const char* name) noexcept
|
||||
: mMaterial(material),
|
||||
mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()),
|
||||
mConstants(material->getDefaultMutableConstants()),
|
||||
mCulling(CullingMode::BACK),
|
||||
mShadowCulling(CullingMode::BACK),
|
||||
mDepthFunc(RasterState::DepthFunc::LE),
|
||||
@@ -128,6 +129,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
|
||||
mTextureParameters(other->mTextureParameters),
|
||||
mDescriptorSet(other->mDescriptorSet.duplicate(
|
||||
"MaterialInstance", mMaterial->getDescriptorSetLayout())),
|
||||
mConstants(other->mConstants),
|
||||
mPolygonOffset(other->mPolygonOffset),
|
||||
mStencilState(other->mStencilState),
|
||||
mMaskThreshold(other->mMaskThreshold),
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/bitset.h>
|
||||
@@ -68,7 +69,7 @@ public:
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
void commitStreamUniformAssociations(FEngine::DriverApi& driver);
|
||||
|
||||
|
||||
void commit(FEngine& engine) const;
|
||||
|
||||
void commit(FEngine::DriverApi& driver) const;
|
||||
@@ -244,6 +245,10 @@ public:
|
||||
|
||||
using MaterialInstance::setParameter;
|
||||
|
||||
const backend::Program::MutableSpecConstantsInfo getMutableSpecConstants() const noexcept {
|
||||
return mConstants;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class FMaterial;
|
||||
friend class MaterialInstance;
|
||||
@@ -266,6 +271,12 @@ private:
|
||||
template<typename T>
|
||||
T getParameterImpl(std::string_view name) const;
|
||||
|
||||
template<typename T>
|
||||
void setConstantImpl(std::string_view name, T const& value);
|
||||
|
||||
template<typename T>
|
||||
T getConstantImpl(std::string_view name) const;
|
||||
|
||||
// keep these grouped, they're accessed together in the render-loop
|
||||
FMaterial const* mMaterial = nullptr;
|
||||
|
||||
@@ -279,6 +290,7 @@ private:
|
||||
mutable DescriptorSet mDescriptorSet;
|
||||
UniformBuffer mUniforms;
|
||||
bool mHasStreamUniformAssociations = false;
|
||||
backend::Program::MutableSpecConstantsInfo mConstants;
|
||||
|
||||
backend::PolygonOffset mPolygonOffset{};
|
||||
backend::StencilState mStencilState{};
|
||||
|
||||
@@ -659,10 +659,12 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
|
||||
if (hasDynamicLighting()) {
|
||||
auto& froxelizer = mFroxelizer;
|
||||
if (froxelizer.prepare(driver, rootArenaScope, viewport,
|
||||
cameraInfo.projection, cameraInfo.zn, cameraInfo.zf)) {
|
||||
cameraInfo.projection, cameraInfo.zn, cameraInfo.zf,
|
||||
cameraInfo.clipTransform)) {
|
||||
// TODO: might be more consistent to do this in prepareLighting(), but it's not
|
||||
// strictly necessary
|
||||
getColorPassDescriptorSet().prepareDynamicLights(mFroxelizer);
|
||||
getColorPassDescriptorSet().prepareDynamicLights(mFroxelizer, mFroxelVizEnabled);
|
||||
mFroxelConfigurationAge++;
|
||||
}
|
||||
// We need to pass viewMatrix by value here because it extends the scope of this
|
||||
// function.
|
||||
@@ -1434,6 +1436,10 @@ void FView::setStereoscopicOptions(const StereoscopicOptions& options) noexcept
|
||||
mStereoscopicOptions = options;
|
||||
}
|
||||
|
||||
View::FroxelConfigurationInfoWithAge FView::getFroxelConfigurationInfo() const noexcept {
|
||||
return { mFroxelizer.getFroxelConfigurationInfo(), mFroxelConfigurationAge };
|
||||
}
|
||||
|
||||
void FView::setMaterialGlobal(uint32_t const index, float4 const& value) {
|
||||
FILAMENT_CHECK_PRECONDITION(index < 4)
|
||||
<< "material global variable index (" << +index << ") out of range";
|
||||
|
||||
@@ -240,6 +240,12 @@ public:
|
||||
return mShadowMapManager->getDirectionalShadowCameras();
|
||||
}
|
||||
|
||||
void setFroxelVizEnabled(bool const enabled) noexcept {
|
||||
mFroxelVizEnabled = enabled;
|
||||
}
|
||||
|
||||
FroxelConfigurationInfoWithAge getFroxelConfigurationInfo() const noexcept;
|
||||
|
||||
void setRenderTarget(FRenderTarget* renderTarget) noexcept {
|
||||
assert_invariant(!renderTarget || !mMultiSampleAntiAliasingOptions.enabled ||
|
||||
!renderTarget->hasSampleableDepth());
|
||||
@@ -552,6 +558,8 @@ private:
|
||||
|
||||
mutable Froxelizer mFroxelizer;
|
||||
utils::JobSystem::Job* mFroxelizerSync = nullptr;
|
||||
bool mFroxelVizEnabled = false;
|
||||
uint32_t mFroxelConfigurationAge = 0;
|
||||
|
||||
Viewport mViewport;
|
||||
bool mCulling = true;
|
||||
|
||||
@@ -372,12 +372,13 @@ void ColorPassDescriptorSet::prepareAmbientLight(FEngine const& engine, FIndirec
|
||||
});
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::prepareDynamicLights(Froxelizer& froxelizer) noexcept {
|
||||
void ColorPassDescriptorSet::prepareDynamicLights(Froxelizer& froxelizer, bool const enableFroxelViz) noexcept {
|
||||
auto& s = mUniforms.edit();
|
||||
froxelizer.updateUniforms(s);
|
||||
float const f = froxelizer.getLightFar();
|
||||
// TODO: make the falloff rate a parameter
|
||||
s.lightFarAttenuationParams = 0.5f * float2{ 10.0f, 10.0f / (f * f) };
|
||||
s.enableFroxelViz = enableFroxelViz;
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::prepareShadowMapping(BufferObjectHandle shadowUniforms) noexcept {
|
||||
|
||||
@@ -118,7 +118,7 @@ public:
|
||||
void prepareAmbientLight(FEngine const& engine,
|
||||
FIndirectLight const& ibl, float intensity, float exposure) noexcept;
|
||||
|
||||
void prepareDynamicLights(Froxelizer& froxelizer) noexcept;
|
||||
void prepareDynamicLights(Froxelizer& froxelizer, bool enableFroxelViz) noexcept;
|
||||
|
||||
void prepareShadowVSM(TextureHandle texture,
|
||||
VsmShadowOptions const& options) noexcept;
|
||||
|
||||
@@ -65,6 +65,10 @@ material {
|
||||
type : float,
|
||||
name : thicknessHeuristic
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : constThickness
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : ssctShadowDistance
|
||||
@@ -98,6 +102,18 @@ material {
|
||||
name : ssctSampleCount
|
||||
}
|
||||
],
|
||||
constants : [
|
||||
{
|
||||
type : bool,
|
||||
name : useVisibilityBitmasks,
|
||||
default : false
|
||||
},
|
||||
{
|
||||
type : bool,
|
||||
name : linearThickness,
|
||||
default : false
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
vertex
|
||||
],
|
||||
|
||||
@@ -65,6 +65,10 @@ material {
|
||||
type : float,
|
||||
name : thicknessHeuristic
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : constThickness
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : ssctShadowDistance
|
||||
@@ -98,6 +102,18 @@ material {
|
||||
name : ssctSampleCount
|
||||
}
|
||||
],
|
||||
constants : [
|
||||
{
|
||||
type : bool,
|
||||
name : useVisibilityBitmasks,
|
||||
default : false
|
||||
},
|
||||
{
|
||||
type : bool,
|
||||
name : linearThickness,
|
||||
default : false
|
||||
}
|
||||
],
|
||||
outputs : [
|
||||
{
|
||||
name : aoData,
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "../utils/geometry.fs"
|
||||
|
||||
#define rsqrt inversesqrt
|
||||
#define SECTOR_COUNT 32u
|
||||
|
||||
#ifndef COMPUTE_BENT_NORMAL
|
||||
#error COMPUTE_BENT_NORMAL must be set
|
||||
@@ -58,6 +59,67 @@ float spatialOffsetsNoise(float2 uv) {
|
||||
return 0.25 * float((position.y - position.x) & 3);
|
||||
}
|
||||
|
||||
// If the new sample value is greater then the current one, update the value with some fallOff.
|
||||
// Otherwise, apply thicknessHeuristic.
|
||||
float updateHorizon(float sampleHorizonCos, float currentHorizonCos, float fallOff) {
|
||||
return sampleHorizonCos > currentHorizonCos
|
||||
? mix(sampleHorizonCos, currentHorizonCos, fallOff)
|
||||
: mix(currentHorizonCos, sampleHorizonCos, materialParams.thicknessHeuristic);
|
||||
}
|
||||
|
||||
float calculateHorizonCos(highp vec3 sampleDelta, highp vec3 viewDir, float horizonCos) {
|
||||
highp float sqSampleDist = dot(sampleDelta, sampleDelta);
|
||||
float invSampleDist = rsqrt(sqSampleDist);
|
||||
|
||||
// Use the view space radius to calculate the fallOff
|
||||
float fallOff = saturate(sqSampleDist * materialParams.invRadiusSquared * 2.0);
|
||||
|
||||
// sample horizon cos
|
||||
float shc = dot(sampleDelta, viewDir) * invSampleDist;
|
||||
|
||||
return updateHorizon(shc, horizonCos, fallOff);
|
||||
}
|
||||
|
||||
// https://cdrinmatane.github.io/posts/ssaovb-code/
|
||||
// https://github.com/cdrinmatane/SSRT3/blob/main/HDRP/Shaders/Resources/SSRTCS.compute
|
||||
uint updateSectors(float minHorizon, float maxHorizon, uint globalOccludedBitfield) {
|
||||
uint startHorizonInt = uint(minHorizon * float(SECTOR_COUNT));
|
||||
uint angleHorizonInt = uint(ceil(saturate(maxHorizon-minHorizon) * float(SECTOR_COUNT)));
|
||||
uint angleHorizonBitfield = angleHorizonInt > 0u ? (0xFFFFFFFFu >> (SECTOR_COUNT-angleHorizonInt)) : 0u;
|
||||
uint currentOccludedBitfield = angleHorizonBitfield << startHorizonInt;
|
||||
return globalOccludedBitfield | currentOccludedBitfield;
|
||||
}
|
||||
|
||||
// https://cdrinmatane.github.io/posts/ssaovb-code/
|
||||
// https://github.com/cdrinmatane/SSRT3/blob/main/HDRP/Shaders/Resources/SSRTCS.compute
|
||||
// The visibility bitmask method replaces the traditional two horizon angles with a bitmask
|
||||
// for each slice. This bitmask flags whether each sector is occluded or not,
|
||||
// which enables surfaces to be modeled with constant thickness, overcoming the limitation
|
||||
// of treating them as a simple height field.
|
||||
uint calculateVisibilityMask(highp vec3 deltaPos, highp vec3 viewDir, float samplingDirection,
|
||||
uint globalOccludedBitfield, float n, highp vec3 origin) {
|
||||
vec2 frontBackHorizon;
|
||||
float linearThicknessMultiplier = materialConstants_linearThickness
|
||||
? saturate(origin.z * materialParams.invFarPlane) * 100.0
|
||||
: 1.0;
|
||||
vec3 deltaPosBackface = deltaPos - viewDir * materialParams.constThickness * linearThicknessMultiplier;
|
||||
|
||||
// Project sample onto the unit circle and compute the angle relative to V
|
||||
frontBackHorizon.x = dot(normalize(deltaPos), viewDir);
|
||||
frontBackHorizon.y = dot(normalize(deltaPosBackface), viewDir);
|
||||
|
||||
frontBackHorizon.x = acosFast(frontBackHorizon.x);
|
||||
frontBackHorizon.y = acosFast(frontBackHorizon.y);
|
||||
|
||||
// Shift sample from V to normal, clamp in [0..1]
|
||||
frontBackHorizon = clamp(((samplingDirection * -frontBackHorizon) + n + HALF_PI) / PI, 0.0, 1.0);
|
||||
|
||||
// Sampling direction inverts min/max angles
|
||||
frontBackHorizon = samplingDirection >= 0.0 ? frontBackHorizon.yx : frontBackHorizon.xy;
|
||||
|
||||
return updateSectors(frontBackHorizon.x, frontBackHorizon.y, globalOccludedBitfield);
|
||||
}
|
||||
|
||||
void groundTruthAmbientOcclusion(out float obscurance, out vec3 bentNormal,
|
||||
highp vec2 uv, highp vec3 origin, vec3 normal) {
|
||||
vec2 uvSamplePos = uv;
|
||||
@@ -84,20 +146,26 @@ void groundTruthAmbientOcclusion(out float obscurance, out vec3 bentNormal,
|
||||
vec3 direction = vec3(cosPhi, sinPhi, 0.0);
|
||||
// Project direction onto the plane orthogonal to viewDir.
|
||||
vec3 orthoDirection = normalize(direction - (dot(direction, viewDir)*viewDir));
|
||||
// axis is orthogonal to direction and viewDir (basically the normal of the slice plane)
|
||||
// `axis` is orthogonal to direction and viewDir (basically the normal of the slice plane)
|
||||
// Used to define projectedNormal
|
||||
vec3 axis = cross(orthoDirection, viewDir);
|
||||
// Project the normal onto the slice plane
|
||||
// `projNormal` is the normal projected onto the slice plane
|
||||
vec3 projNormal = normal - axis * dot(normal, axis);
|
||||
|
||||
float signNorm = sign(dot(orthoDirection, projNormal));
|
||||
float projNormalLength = length(projNormal);
|
||||
float cosNorm = saturate(dot(projNormal, viewDir) / projNormalLength);
|
||||
|
||||
// `n` is the signed angle between projNormal and viewDir
|
||||
float n = signNorm * acosFast(cosNorm);
|
||||
|
||||
// These are only used in non-bitmask mode
|
||||
float horizonCos0 = -1.0;
|
||||
float horizonCos1 = -1.0;
|
||||
|
||||
// This is only used in bitmask mode
|
||||
uint globalOccludedBitfield = 0u;
|
||||
|
||||
for (float j = 0.0; j < materialParams.stepsPerSlice; j += 1.0) {
|
||||
// At least move 1 pixel forward in the screen-space
|
||||
vec2 sampleOffset = max((j + initialRayStep)*stepRadius, 1.0 + j) * omega;
|
||||
@@ -116,33 +184,36 @@ void groundTruthAmbientOcclusion(out float obscurance, out vec3 bentNormal,
|
||||
|
||||
highp vec3 sampleDelta0 = (samplePos0 - origin);
|
||||
highp vec3 sampleDelta1 = (samplePos1 - origin);
|
||||
highp vec2 sqSampleDist = vec2(dot(sampleDelta0, sampleDelta0), dot(sampleDelta1, sampleDelta1));
|
||||
vec2 invSampleDist = rsqrt(sqSampleDist);
|
||||
|
||||
// Use the view space radius to calculate the fallOff
|
||||
vec2 fallOff = saturate(sqSampleDist.xy * materialParams.invRadiusSquared * 2.0);
|
||||
|
||||
// sample horizon cos
|
||||
float shc0 = dot(sampleDelta0, viewDir) * invSampleDist.x;
|
||||
float shc1 = dot(sampleDelta1, viewDir) * invSampleDist.y;
|
||||
|
||||
// If the new sample value is greater then the current one, update the value with some fallOff.
|
||||
// Otherwise, apply thicknessHeuristic.
|
||||
horizonCos0 = shc0 > horizonCos0 ? mix(shc0, horizonCos0, fallOff.x) : mix(horizonCos0, shc0, materialParams.thicknessHeuristic);
|
||||
horizonCos1 = shc1 > horizonCos1 ? mix(shc1, horizonCos1, fallOff.y) : mix(horizonCos1, shc1, materialParams.thicknessHeuristic);
|
||||
if (materialConstants_useVisibilityBitmasks) {
|
||||
globalOccludedBitfield = calculateVisibilityMask(sampleDelta0, viewDir, 1.0, globalOccludedBitfield, n, origin);
|
||||
globalOccludedBitfield = calculateVisibilityMask(sampleDelta1, viewDir, -1.0, globalOccludedBitfield, n, origin);
|
||||
}
|
||||
else {
|
||||
horizonCos0 = calculateHorizonCos(sampleDelta0, viewDir, horizonCos0);
|
||||
horizonCos1 = calculateHorizonCos(sampleDelta1, viewDir, horizonCos1);
|
||||
}
|
||||
}
|
||||
|
||||
float h0 = -acosFast(horizonCos1);
|
||||
float h1 = acosFast(horizonCos0);
|
||||
h0 = n + clamp(h0 - n, -HALF_PI, HALF_PI);
|
||||
h1 = n + clamp(h1 - n, -HALF_PI, HALF_PI);
|
||||
if (materialConstants_useVisibilityBitmasks) {
|
||||
// Calculate the portion of the occluded sector
|
||||
visibility += 1.0 - float(bitCount(globalOccludedBitfield)) / float(SECTOR_COUNT);
|
||||
|
||||
#if COMPUTE_BENT_NORMAL
|
||||
float angle = 0.5 * (h0 + h1);
|
||||
bentNormal += viewDir * cos(angle) - orthoDirection * sin(angle);
|
||||
#endif
|
||||
// Note: bent normal calculation is not supported in visibility bitmasks mode
|
||||
}
|
||||
else {
|
||||
float h0 = -acosFast(horizonCos1);
|
||||
float h1 = acosFast(horizonCos0);
|
||||
h0 = n + clamp(h0 - n, -HALF_PI, HALF_PI);
|
||||
h1 = n + clamp(h1 - n, -HALF_PI, HALF_PI);
|
||||
|
||||
visibility += projNormalLength * (integrateArcCosWeight(h0, n) + integrateArcCosWeight(h1, n));
|
||||
#if COMPUTE_BENT_NORMAL
|
||||
float angle = 0.5 * (h0 + h1);
|
||||
bentNormal += viewDir * cos(angle) - orthoDirection * sin(angle);
|
||||
#endif
|
||||
|
||||
visibility += projNormalLength * (integrateArcCosWeight(h0, n) + integrateArcCosWeight(h1, n));
|
||||
}
|
||||
}
|
||||
|
||||
obscurance = 1.0 - saturate(visibility * materialParams.sliceCount.y);
|
||||
|
||||
@@ -672,7 +672,7 @@ TEST(FilamentTest, FroxelData) {
|
||||
|
||||
Froxelizer froxelData(*engine);
|
||||
froxelData.setOptions(5, 100);
|
||||
froxelData.prepare(engine->getDriverApi(), scope, vp, p, 0.1, 100);
|
||||
froxelData.prepare(engine->getDriverApi(), scope, vp, p, 0.1, 100, {1,1,0,0});
|
||||
|
||||
Froxel f = froxelData.getFroxelAt(0,0,0);
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
|
||||
MaterialDescriptorSetLayoutInfo = charTo64bitNum("MAT_DSLI"),
|
||||
MaterialProperties = charTo64bitNum("MAT_PROP"),
|
||||
MaterialConstants = charTo64bitNum("MAT_CONS"),
|
||||
MaterialMutableConstants = charTo64bitNum("MAT_MCNS"),
|
||||
MaterialPushConstants = charTo64bitNum("MAT_PCON"),
|
||||
|
||||
MaterialName = charTo64bitNum("MAT_NAME"),
|
||||
|
||||
@@ -30,7 +30,19 @@ struct MaterialConstant {
|
||||
ConstantType type;
|
||||
|
||||
MaterialConstant() = default;
|
||||
MaterialConstant(const char* name, ConstantType type) : name(name), type(type) {}
|
||||
MaterialConstant(utils::CString name, ConstantType type) : name(std::move(name)), type(type) {}
|
||||
};
|
||||
|
||||
struct MaterialMutableConstant {
|
||||
utils::CString name;
|
||||
// The Filament engine stores mutable spec constants as a bitfield, meaning that must override
|
||||
// all of the default values built into in the shader souces. Therefore, unlike the immutable
|
||||
// constants above, we have to store the default values in the material metadata.
|
||||
bool defaultValue;
|
||||
|
||||
MaterialMutableConstant() = default;
|
||||
MaterialMutableConstant(utils::CString name, bool defaultValue)
|
||||
: name(std::move(name)), defaultValue(defaultValue) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -120,6 +120,10 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||
math::uint3 fParams; // stride-x, stride-y, stride-z
|
||||
int32_t lightChannels; // light channel bits
|
||||
math::float2 froxelCountXY;
|
||||
int enableFroxelViz;
|
||||
int dynReserved0;
|
||||
int dynReserved1;
|
||||
int dynReserved2;
|
||||
|
||||
// IBL
|
||||
float iblLuminance;
|
||||
@@ -204,7 +208,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||
float es2Reserved2;
|
||||
|
||||
// bring PerViewUib to 2 KiB
|
||||
math::float4 reserved[39];
|
||||
math::float4 reserved[38];
|
||||
};
|
||||
|
||||
// 2 KiB == 128 float4s
|
||||
|
||||
@@ -244,11 +244,13 @@ void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io)
|
||||
}
|
||||
materialInstance = mMaterial2dInstances[material2dIndex++];
|
||||
}
|
||||
materialInstance->setScissor(
|
||||
pcmd.ClipRect.x,
|
||||
mFlipVertical ? pcmd.ClipRect.y : (fbheight - pcmd.ClipRect.w),
|
||||
(uint16_t) (pcmd.ClipRect.z - pcmd.ClipRect.x),
|
||||
(uint16_t) (pcmd.ClipRect.w - pcmd.ClipRect.y));
|
||||
|
||||
auto scissorLeft = static_cast<uint32_t>(std::max(0.0f, pcmd.ClipRect.x));
|
||||
auto scissorBottom = static_cast<uint32_t>(std::max(0.0f,
|
||||
mFlipVertical ? pcmd.ClipRect.y : (fbheight - pcmd.ClipRect.w)));
|
||||
auto scissorWidth = static_cast<uint32_t>(pcmd.ClipRect.z - pcmd.ClipRect.x);
|
||||
auto scissorHeight = static_cast<uint32_t>(pcmd.ClipRect.w - pcmd.ClipRect.y);
|
||||
materialInstance->setScissor(scissorLeft, scissorBottom, scissorWidth, scissorHeight);
|
||||
if (texture) {
|
||||
TextureSampler sampler(MinFilter::LINEAR, MagFilter::LINEAR);
|
||||
materialInstance->setParameter("albedo", texture, sampler);
|
||||
|
||||
@@ -319,14 +319,15 @@ public:
|
||||
MaterialBuilder& parameter(const char* name, size_t size, UniformType type,
|
||||
ParameterPrecision precision = ParameterPrecision::DEFAULT);
|
||||
|
||||
//! Add a constant parameter to this material.
|
||||
//! Add a (mutable) constant parameter to this material.
|
||||
template<typename T>
|
||||
using is_supported_constant_parameter_t = typename std::enable_if<
|
||||
std::is_same<int32_t, T>::value ||
|
||||
std::is_same<float, T>::value ||
|
||||
std::is_same<bool, T>::value>::type;
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
MaterialBuilder& constant(const char *name, ConstantType type, T defaultValue = 0);
|
||||
MaterialBuilder& constant(const char* name, ConstantType type, bool isMutable,
|
||||
T defaultValue = 0);
|
||||
|
||||
/**
|
||||
* Add a sampler parameter to this material.
|
||||
@@ -927,6 +928,7 @@ private:
|
||||
PropertyList mProperties;
|
||||
ParameterList mParameters;
|
||||
ConstantList mConstants;
|
||||
ConstantList mMutableConstants;
|
||||
PushConstantList mPushConstants;
|
||||
SubpassList mSubpasses;
|
||||
VariableList mVariables;
|
||||
|
||||
@@ -322,12 +322,19 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType sample
|
||||
}
|
||||
|
||||
template<typename T, typename>
|
||||
MaterialBuilder& MaterialBuilder::constant(const char* name, ConstantType const type, T defaultValue) {
|
||||
auto result = std::find_if(mConstants.begin(), mConstants.end(), [name](const Constant& c) {
|
||||
return c.name == CString(name);
|
||||
});
|
||||
FILAMENT_CHECK_POSTCONDITION(result == mConstants.end())
|
||||
MaterialBuilder& MaterialBuilder::constant(const char* name, ConstantType const type,
|
||||
bool isMutable, T defaultValue) {
|
||||
ConstantList& constants = isMutable ? mMutableConstants : mConstants;
|
||||
const ConstantList& otherConstants = isMutable ? mConstants : mMutableConstants;
|
||||
auto result = std::find_if(constants.begin(), constants.end(),
|
||||
[name](const Constant& c) { return c.name == CString(name); });
|
||||
FILAMENT_CHECK_POSTCONDITION(result == constants.end())
|
||||
<< "There is already a constant parameter present with the name " << name << ".";
|
||||
auto otherResult = std::find_if(otherConstants.begin(), otherConstants.end(),
|
||||
[name](const Constant& c) { return c.name == CString(name); });
|
||||
FILAMENT_CHECK_POSTCONDITION(otherResult == otherConstants.end())
|
||||
<< "There is already a constant parameter present with the name " << name << ".";
|
||||
|
||||
Constant constant {
|
||||
.name = CString(name),
|
||||
.type = type,
|
||||
@@ -359,15 +366,20 @@ MaterialBuilder& MaterialBuilder::constant(const char* name, ConstantType const
|
||||
assert_invariant(false);
|
||||
}
|
||||
|
||||
mConstants.push_back(constant);
|
||||
if (isMutable) {
|
||||
FILAMENT_CHECK_POSTCONDITION(type == ConstantType::BOOL)
|
||||
<< "Mutable spec constants must be bool type.";
|
||||
}
|
||||
|
||||
constants.push_back(constant);
|
||||
return *this;
|
||||
}
|
||||
template MaterialBuilder& MaterialBuilder::constant<int32_t>(
|
||||
const char* name, ConstantType type, int32_t defaultValue);
|
||||
const char* name, ConstantType type, bool isMutable, int32_t defaultValue);
|
||||
template MaterialBuilder& MaterialBuilder::constant<float>(
|
||||
const char* name, ConstantType type, float defaultValue);
|
||||
const char* name, ConstantType type, bool isMutable, float defaultValue);
|
||||
template MaterialBuilder& MaterialBuilder::constant<bool>(
|
||||
const char* name, ConstantType type, bool defaultValue);
|
||||
const char* name, ConstantType type, bool isMutable, bool defaultValue);
|
||||
|
||||
MaterialBuilder& MaterialBuilder::buffer(BufferInterfaceBlock bib) {
|
||||
FILAMENT_CHECK_POSTCONDITION(mBuffers.size() < MAX_BUFFERS_COUNT) << "Too many buffers";
|
||||
@@ -946,7 +958,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
|
||||
BlobDictionary spirvDictionary;
|
||||
// End: must be protected by lock
|
||||
|
||||
ShaderGenerator sg(mProperties, mVariables, mOutputs, mDefines, mConstants, mPushConstants,
|
||||
ShaderGenerator sg(mProperties, mVariables, mOutputs, mDefines, mConstants, mMutableConstants, mPushConstants,
|
||||
mMaterialFragmentCode.getResolved(), mMaterialFragmentCode.getLineOffset(),
|
||||
mMaterialVertexCode.getResolved(), mMaterialVertexCode.getLineOffset(),
|
||||
mMaterialDomain);
|
||||
@@ -1548,7 +1560,7 @@ bool MaterialBuilder::needsStandardDepthProgram() const noexcept {
|
||||
std::string MaterialBuilder::peek(backend::ShaderStage const stage,
|
||||
const CodeGenParams& params, const PropertyList& properties) noexcept {
|
||||
|
||||
ShaderGenerator const sg(properties, mVariables, mOutputs, mDefines, mConstants, mPushConstants,
|
||||
ShaderGenerator const sg(properties, mVariables, mOutputs, mDefines, mConstants, mMutableConstants, mPushConstants,
|
||||
mMaterialFragmentCode.getResolved(), mMaterialFragmentCode.getLineOffset(),
|
||||
mMaterialVertexCode.getResolved(), mMaterialVertexCode.getLineOffset(),
|
||||
mMaterialDomain);
|
||||
@@ -1676,9 +1688,14 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
|
||||
// User constant parameters
|
||||
FixedCapacityVector<MaterialConstant> constantsEntry(mConstants.size());
|
||||
std::transform(mConstants.begin(), mConstants.end(), constantsEntry.begin(),
|
||||
[](Constant const& c) { return MaterialConstant(c.name.c_str(), c.type); });
|
||||
[](Constant const& c) { return MaterialConstant(c.name, c.type); });
|
||||
container.push<MaterialConstantParametersChunk>(std::move(constantsEntry));
|
||||
|
||||
FixedCapacityVector<MaterialMutableConstant> mutableConstantsEntry(mMutableConstants.size());
|
||||
std::transform(mMutableConstants.begin(), mMutableConstants.end(), mutableConstantsEntry.begin(),
|
||||
[](Constant const& c) { return MaterialMutableConstant(c.name, c.defaultValue.b); });
|
||||
container.push<MaterialMutableConstantParametersChunk>(std::move(mutableConstantsEntry));
|
||||
|
||||
FixedCapacityVector<MaterialPushConstant> pushConstantsEntry(mPushConstants.size());
|
||||
std::transform(mPushConstants.begin(), mPushConstants.end(), pushConstantsEntry.begin(),
|
||||
[](PushConstant const& c) {
|
||||
|
||||
@@ -118,6 +118,20 @@ void MaterialConstantParametersChunk::flatten(Flattener& f) {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
MaterialMutableConstantParametersChunk::MaterialMutableConstantParametersChunk(
|
||||
FixedCapacityVector<MaterialMutableConstant> constants)
|
||||
: Chunk(MaterialMutableConstants), mConstants(std::move(constants)) {}
|
||||
|
||||
void MaterialMutableConstantParametersChunk::flatten(Flattener& f) {
|
||||
f.writeUint64(mConstants.size());
|
||||
for (const auto& constant : mConstants) {
|
||||
f.writeString(constant.name.c_str());
|
||||
f.writeBool(constant.defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
MaterialPushConstantParametersChunk::MaterialPushConstantParametersChunk(
|
||||
CString const& structVarName, FixedCapacityVector<MaterialPushConstant> constants)
|
||||
: Chunk(MaterialPushConstants),
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define TNT_FILAMAT_MAT_INTEFFACE_BLOCK_CHUNK_H
|
||||
|
||||
#include "Chunk.h"
|
||||
#include "private/filament/ConstantInfo.h"
|
||||
|
||||
#include <backend/Program.h>
|
||||
|
||||
@@ -91,6 +92,20 @@ private:
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
class MaterialMutableConstantParametersChunk final : public Chunk {
|
||||
public:
|
||||
explicit MaterialMutableConstantParametersChunk(
|
||||
FixedCapacityVector<filament::MaterialMutableConstant> constants);
|
||||
~MaterialMutableConstantParametersChunk() override = default;
|
||||
|
||||
private:
|
||||
void flatten(Flattener&) override;
|
||||
|
||||
FixedCapacityVector<filament::MaterialMutableConstant> mConstants;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
class MaterialPushConstantParametersChunk final : public Chunk {
|
||||
public:
|
||||
explicit MaterialPushConstantParametersChunk(CString const& structVarName,
|
||||
|
||||
@@ -285,10 +285,17 @@ void ShaderGenerator::appendShader(io::sstream& ss,
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderGenerator::generateUserSpecConstants(
|
||||
const CodeGenerator& cg, io::sstream& fs, MaterialBuilder::ConstantList const& constants) {
|
||||
void ShaderGenerator::generateAllUserSpecConstants(const CodeGenerator& cg, io::sstream& fs,
|
||||
MaterialBuilder::ConstantList const& constants,
|
||||
MaterialBuilder::ConstantList const& mutableConstants) {
|
||||
generateUserSpecConstants(cg, fs, constants, 0);
|
||||
generateUserSpecConstants(cg, fs, mutableConstants, constants.size());
|
||||
}
|
||||
|
||||
void ShaderGenerator::generateUserSpecConstants(const CodeGenerator& cg, io::sstream& fs,
|
||||
MaterialBuilder::ConstantList const& constants, size_t offset) {
|
||||
// Constants 0 to CONFIG_MAX_RESERVED_SPEC_CONSTANTS - 1 are reserved by Filament.
|
||||
size_t index = CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
|
||||
size_t index = CONFIG_MAX_RESERVED_SPEC_CONSTANTS + offset;
|
||||
for (const auto& constant : constants) {
|
||||
std::string const fullName = std::string("materialConstants_") + constant.name.c_str();
|
||||
switch (constant.type) {
|
||||
@@ -316,6 +323,7 @@ ShaderGenerator::ShaderGenerator(
|
||||
MaterialBuilder::OutputList const& outputs,
|
||||
MaterialBuilder::PreprocessorDefineList const& defines,
|
||||
MaterialBuilder::ConstantList const& constants,
|
||||
MaterialBuilder::ConstantList const& mutableConstants,
|
||||
MaterialBuilder::PushConstantList const& pushConstants,
|
||||
CString const& materialCode, size_t const lineOffset,
|
||||
CString const& materialVertexCode, size_t const vertexLineOffset,
|
||||
@@ -338,6 +346,7 @@ ShaderGenerator::ShaderGenerator(
|
||||
mMaterialDomain = materialDomain;
|
||||
mDefines = defines;
|
||||
mConstants = constants;
|
||||
mMutableConstants = mutableConstants;
|
||||
mPushConstants = pushConstants;
|
||||
|
||||
if (mMaterialFragmentCode.empty()) {
|
||||
@@ -394,7 +403,7 @@ std::string ShaderGenerator::createSurfaceVertexProgram(ShaderModel const shader
|
||||
|
||||
cg.generateCommonProlog(vs, ShaderStage::VERTEX, material, variant);
|
||||
|
||||
generateUserSpecConstants(cg, vs, mConstants);
|
||||
generateAllUserSpecConstants(cg, vs, mConstants, mMutableConstants);
|
||||
|
||||
// note: even if the user vertex shader is empty, we can't use the "optimized" version if
|
||||
// we're in masked mode because fragment shader needs the color varyings
|
||||
@@ -527,7 +536,7 @@ std::string ShaderGenerator::createSurfaceFragmentProgram(ShaderModel const shad
|
||||
io::sstream fs;
|
||||
cg.generateCommonProlog(fs, ShaderStage::FRAGMENT, material, variant);
|
||||
|
||||
generateUserSpecConstants(cg, fs, mConstants);
|
||||
generateAllUserSpecConstants(cg, fs, mConstants, mMutableConstants);
|
||||
|
||||
generateSurfaceMaterialVariantDefines(
|
||||
fs, ShaderStage::FRAGMENT, featureLevel, material, variant);
|
||||
@@ -683,7 +692,7 @@ std::string ShaderGenerator::createSurfaceComputeProgram(ShaderModel const shade
|
||||
|
||||
cg.generateCommonProlog(s, ShaderStage::COMPUTE, material, {});
|
||||
|
||||
generateUserSpecConstants(cg, s, mConstants);
|
||||
generateAllUserSpecConstants(cg, s, mConstants, mMutableConstants);
|
||||
|
||||
CodeGenerator::generateSurfaceTypes(s, ShaderStage::COMPUTE);
|
||||
|
||||
@@ -723,7 +732,7 @@ std::string ShaderGenerator::createPostProcessVertexProgram(ShaderModel const sm
|
||||
io::sstream vs;
|
||||
cg.generateCommonProlog(vs, ShaderStage::VERTEX, material, {});
|
||||
|
||||
generateUserSpecConstants(cg, vs, mConstants);
|
||||
generateAllUserSpecConstants(cg, vs, mConstants, mMutableConstants);
|
||||
|
||||
CodeGenerator::generateDefine(vs, "LOCATION_POSITION", uint32_t(POSITION));
|
||||
|
||||
@@ -767,7 +776,7 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(ShaderModel const
|
||||
io::sstream fs;
|
||||
cg.generateCommonProlog(fs, ShaderStage::FRAGMENT, material, {});
|
||||
|
||||
generateUserSpecConstants(cg, fs, mConstants);
|
||||
generateAllUserSpecConstants(cg, fs, mConstants, mMutableConstants);
|
||||
|
||||
generatePostProcessMaterialVariantDefines(fs, PostProcessVariant(variant));
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
MaterialBuilder::OutputList const& outputs,
|
||||
MaterialBuilder::PreprocessorDefineList const& defines,
|
||||
MaterialBuilder::ConstantList const& constants,
|
||||
MaterialBuilder::ConstantList const& mutableConstants,
|
||||
MaterialBuilder::PushConstantList const& pushConstants,
|
||||
utils::CString const& materialCode,
|
||||
size_t lineOffset,
|
||||
@@ -104,9 +105,14 @@ private:
|
||||
static void generatePostProcessMaterialVariantDefines(utils::io::sstream& out,
|
||||
filament::PostProcessVariant variant) noexcept;
|
||||
|
||||
static void generateAllUserSpecConstants(const CodeGenerator& cg, utils::io::sstream& fs,
|
||||
MaterialBuilder::ConstantList const& constants,
|
||||
MaterialBuilder::ConstantList const& mutableConstants);
|
||||
|
||||
static void generateUserSpecConstants(
|
||||
const CodeGenerator& cg, utils::io::sstream& fs,
|
||||
MaterialBuilder::ConstantList const& constants);
|
||||
MaterialBuilder::ConstantList const& constants,
|
||||
size_t offset);
|
||||
|
||||
std::string createPostProcessVertexProgram(filament::backend::ShaderModel sm,
|
||||
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
||||
@@ -135,6 +141,7 @@ private:
|
||||
MaterialBuilder::MaterialDomain mMaterialDomain;
|
||||
MaterialBuilder::PreprocessorDefineList mDefines;
|
||||
MaterialBuilder::ConstantList mConstants;
|
||||
MaterialBuilder::ConstantList mMutableConstants;
|
||||
MaterialBuilder::PushConstantList mPushConstants;
|
||||
utils::CString mMaterialFragmentCode; // fragment or compute code
|
||||
utils::CString mMaterialVertexCode;
|
||||
|
||||
@@ -140,6 +140,10 @@ BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
|
||||
{ "fParams", 0, Type::UINT3 },
|
||||
{ "lightChannels", 0, Type::INT },
|
||||
{ "froxelCountXY", 0, Type::FLOAT2 },
|
||||
{ "enableFroxelViz", 0, Type::INT },
|
||||
{ "dynReserved0", 0, Type::INT },
|
||||
{ "dynReserved1", 0, Type::INT },
|
||||
{ "dynReserved2", 0, Type::INT },
|
||||
|
||||
{ "iblLuminance", 0, Type::FLOAT, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 },
|
||||
{ "iblRoughnessOneLevel", 0, Type::FLOAT, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 },
|
||||
|
||||
@@ -888,10 +888,10 @@ TEST_F(MaterialCompiler, ConstantParameter) {
|
||||
}
|
||||
)");
|
||||
filamat::MaterialBuilder builder;
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, 1.0f);
|
||||
builder.constant("myIntConstant", ConstantType::INT, 123);
|
||||
builder.constant("myBoolConstant", ConstantType::BOOL, true);
|
||||
builder.constant<bool>("myOtherBoolConstant", ConstantType::BOOL);
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, /*isMutable=*/false, 1.0f);
|
||||
builder.constant("myIntConstant", ConstantType::INT, /*isMutable=*/false, 123);
|
||||
builder.constant("myBoolConstant", ConstantType::BOOL, /*isMutable=*/false, true);
|
||||
builder.constant<bool>("myOtherBoolConstant", ConstantType::BOOL, /*isMutable=*/false);
|
||||
|
||||
builder.shading(filament::Shading::LIT);
|
||||
builder.material(shaderCode.c_str());
|
||||
@@ -904,8 +904,8 @@ TEST_F(MaterialCompiler, ConstantParameterSameName) {
|
||||
#ifdef __EXCEPTIONS
|
||||
EXPECT_THROW({
|
||||
filamat::MaterialBuilder builder;
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, 1.0f);
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, 1.0f);
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, /*isMutable=*/false, 1.0f);
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, /*isMutable=*/false, 1.0f);
|
||||
}, utils::PostconditionPanic);
|
||||
#endif
|
||||
}
|
||||
@@ -914,7 +914,7 @@ TEST_F(MaterialCompiler, ConstantParameterWrongType) {
|
||||
#ifdef __EXCEPTIONS
|
||||
EXPECT_THROW({
|
||||
filamat::MaterialBuilder builder;
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, 10);
|
||||
builder.constant("myFloatConstant", ConstantType::FLOAT, /*isMutable=*/false, 10);
|
||||
}, utils::PostconditionPanic);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -352,6 +352,19 @@ static bool processConstant(MaterialBuilder& builder, const JsonishObject& jsonO
|
||||
return false;
|
||||
}
|
||||
|
||||
const JsonishValue* mutableValue = jsonObject.getValue("mutable");
|
||||
bool isMutable;
|
||||
if (mutableValue) {
|
||||
const JsonishBool* mutableBool = mutableValue->toJsonBool();
|
||||
if (!mutableBool) {
|
||||
std::cerr << "constants: mutable value must be BOOL." << std::endl;
|
||||
return false;
|
||||
}
|
||||
isMutable = mutableBool->getBool();
|
||||
} else {
|
||||
isMutable = false;
|
||||
}
|
||||
|
||||
auto typeString = typeValue->toJsonString()->getString();
|
||||
auto nameString = nameValue->toJsonString()->getString();
|
||||
const JsonishValue* defaultValue = jsonObject.getValue("default");
|
||||
@@ -370,7 +383,7 @@ static bool processConstant(MaterialBuilder& builder, const JsonishObject& jsonO
|
||||
// FIXME: Jsonish doesn't distinguish between integers and floats.
|
||||
intDefault = (int32_t)defaultValue->toJsonNumber()->getFloat();
|
||||
}
|
||||
builder.constant(nameString.c_str(), type, intDefault);
|
||||
builder.constant(nameString.c_str(), type, isMutable, intDefault);
|
||||
break;
|
||||
}
|
||||
case ConstantType::FLOAT: {
|
||||
@@ -383,7 +396,7 @@ static bool processConstant(MaterialBuilder& builder, const JsonishObject& jsonO
|
||||
}
|
||||
floatDefault = defaultValue->toJsonNumber()->getFloat();
|
||||
}
|
||||
builder.constant(nameString.c_str(), type, floatDefault);
|
||||
builder.constant(nameString.c_str(), type, isMutable, floatDefault);
|
||||
break;
|
||||
}
|
||||
case ConstantType::BOOL:
|
||||
@@ -396,7 +409,7 @@ static bool processConstant(MaterialBuilder& builder, const JsonishObject& jsonO
|
||||
}
|
||||
boolDefault = defaultValue->toJsonBool()->getBool();
|
||||
}
|
||||
builder.constant(nameString.c_str(), type, boolDefault);
|
||||
builder.constant(nameString.c_str(), type, isMutable, boolDefault);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -20,6 +20,7 @@ set(MATERIAL_DIR "${GENERATION_ROOT}/generated/material")
|
||||
set(PUBLIC_HDRS
|
||||
include/filamentapp/Config.h
|
||||
include/filamentapp/Cube.h
|
||||
include/filamentapp/Grid.h
|
||||
include/filamentapp/FilamentApp.h
|
||||
include/filamentapp/IBL.h
|
||||
include/filamentapp/IcoSphere.h
|
||||
@@ -30,6 +31,7 @@ set(PUBLIC_HDRS
|
||||
|
||||
set(SRCS
|
||||
src/Cube.cpp
|
||||
src/Grid.cpp
|
||||
src/FilamentApp.cpp
|
||||
src/IBL.cpp
|
||||
src/IcoSphere.cpp
|
||||
|
||||
@@ -111,6 +111,15 @@ public:
|
||||
|
||||
void loadIBL(std::string_view path);
|
||||
|
||||
|
||||
// debugging: enable/disable the froxel grid
|
||||
void setCameraFrustumEnabled(bool enabled) noexcept;
|
||||
void setDirectionalShadowFrustumEnabled(bool enabled) noexcept;
|
||||
void setFroxelGridEnabled(bool enabled) noexcept;
|
||||
bool isCameraFrustumEnabled() const noexcept;
|
||||
bool isDirectionalShadowFrustumEnabled() const noexcept;
|
||||
bool isFroxelGridEnabled() const noexcept;
|
||||
|
||||
FilamentApp(const FilamentApp& rhs) = delete;
|
||||
FilamentApp(FilamentApp&& rhs) = delete;
|
||||
FilamentApp& operator=(const FilamentApp& rhs) = delete;
|
||||
@@ -262,6 +271,10 @@ private:
|
||||
float mCameraNear = 0.1f;
|
||||
float mCameraFar = 100.0f;
|
||||
bool mReconfigureCameras = false;
|
||||
uint8_t mFroxelInfoAge = 0x42;
|
||||
uint8_t mFroxelGridEnabled = 0;
|
||||
uint8_t mDirectionalShadowFrustumEnabled = 0x2;
|
||||
uint8_t mCameraFrustumEnabled = 0x2;
|
||||
|
||||
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
|
||||
filament::backend::VulkanPlatform* mVulkanPlatform = nullptr;
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_SAMPLE_GRID_H
|
||||
#define TNT_FILAMENT_SAMPLE_GRID_H
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Box.h>
|
||||
#include <filament/Camera.h>
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec3.h>
|
||||
|
||||
#include <utils/Entity.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
class Grid {
|
||||
public:
|
||||
|
||||
Grid(filament::Engine& engine, filament::Material const* material,
|
||||
filament::math::float3 linearColor);
|
||||
|
||||
Grid(Grid const&) = delete;
|
||||
Grid& operator=(Grid const&) = delete;
|
||||
|
||||
Grid(Grid&& rhs) noexcept;
|
||||
|
||||
utils::Entity getWireFrameRenderable() const {
|
||||
return mWireFrameRenderable;
|
||||
}
|
||||
|
||||
~Grid();
|
||||
|
||||
using Generator = std::function<float(int index)>;
|
||||
|
||||
void update(uint32_t width, uint32_t height, uint32_t depth);
|
||||
|
||||
void update(uint32_t width, uint32_t height, uint32_t depth,
|
||||
Generator const& genWidth, Generator const& genHeight, Generator const& genDepth);
|
||||
|
||||
void mapFrustum(filament::Engine& engine, filament::Camera const* camera);
|
||||
void mapFrustum(filament::Engine& engine, filament::math::mat4 const& transform);
|
||||
void mapAabb(filament::Engine& engine, filament::Box const& box);
|
||||
|
||||
private:
|
||||
filament::Engine& mEngine;
|
||||
filament::VertexBuffer* mVertexBuffer = nullptr;
|
||||
filament::IndexBuffer* mIndexBuffer = nullptr;
|
||||
filament::Material const* mMaterial = nullptr;
|
||||
filament::MaterialInstance* mMaterialInstanceWireFrame = nullptr;
|
||||
utils::Entity mWireFrameRenderable{};
|
||||
};
|
||||
|
||||
|
||||
#endif // TNT_FILAMENT_SAMPLE_GRID_H
|
||||
@@ -55,6 +55,7 @@
|
||||
#include <filagui/ImGuiHelper.h>
|
||||
|
||||
#include <filamentapp/Cube.h>
|
||||
#include <filamentapp/Grid.h>
|
||||
#include <filamentapp/NativeWindowHelper.h>
|
||||
|
||||
#include <stb_image.h>
|
||||
@@ -179,6 +180,7 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
.build(*mEngine);
|
||||
|
||||
std::unique_ptr<Cube> cameraCube{ new Cube(*mEngine, mTransparentMaterial, { 1, 0, 0 }) };
|
||||
std::unique_ptr<Grid> cameraGrid{ new Grid(*mEngine, mTransparentMaterial, { 1, 1, 0 }) };
|
||||
|
||||
// we can't cull the light-frustum because it's not applied a rigid transform
|
||||
// and currently, filament assumes that for culling
|
||||
@@ -192,20 +194,14 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
mScene = mEngine->createScene();
|
||||
|
||||
window->mMainView->getView()->setVisibleLayers(0x4, 0x4);
|
||||
window->mMainView->getView()->setFroxelVizEnabled(true);
|
||||
|
||||
if (config.splitView) {
|
||||
auto& rcm = mEngine->getRenderableManager();
|
||||
|
||||
rcm.setLayerMask(rcm.getInstance(cameraCube->getSolidRenderable()), 0x3, 0x2);
|
||||
rcm.setLayerMask(rcm.getInstance(cameraCube->getWireFrameRenderable()), 0x3, 0x2);
|
||||
mScene->addEntity(cameraCube->getWireFrameRenderable());
|
||||
mScene->addEntity(cameraCube->getSolidRenderable());
|
||||
|
||||
mScene->addEntity(cameraCube->getWireFrameRenderable());
|
||||
for (auto&& cube : lightmapCubes) {
|
||||
rcm.setLayerMask(rcm.getInstance(cube.getSolidRenderable()), 0x3, 0x2);
|
||||
rcm.setLayerMask(rcm.getInstance(cube.getWireFrameRenderable()), 0x3, 0x2);
|
||||
mScene->addEntity(cube.getWireFrameRenderable());
|
||||
mScene->addEntity(cube.getSolidRenderable());
|
||||
mScene->addEntity(cube.getWireFrameRenderable());
|
||||
}
|
||||
|
||||
window->mDepthView->getView()->setVisibleLayers(0x4, 0x4);
|
||||
@@ -218,6 +214,9 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
window->mOrthoView->getView()->setShadowingEnabled(false);
|
||||
}
|
||||
|
||||
// froxel debug grid always added (but hidden)
|
||||
mScene->addEntity(cameraGrid->getWireFrameRenderable());
|
||||
|
||||
loadDirt(config);
|
||||
loadIBL(config);
|
||||
|
||||
@@ -467,11 +466,60 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
filament::math::float3 eye, center, up;
|
||||
window->mMainCameraMan->getLookAt(&eye, ¢er, &up);
|
||||
window->mMainCamera->lookAt(eye, center, up);
|
||||
|
||||
window->mDebugCameraMan->getLookAt(&eye, ¢er, &up);
|
||||
window->mDebugCamera->lookAt(eye, center, up);
|
||||
window->mDebugCamera->setExposure(
|
||||
window->mMainCamera->getAperture(),
|
||||
window->mMainCamera->getShutterSpeed(),
|
||||
window->mMainCamera->getSensitivity());
|
||||
|
||||
window->mOrthoCamera->setExposure(
|
||||
window->mMainCamera->getAperture(),
|
||||
window->mMainCamera->getShutterSpeed(),
|
||||
window->mMainCamera->getSensitivity());
|
||||
|
||||
auto const fci = window->mMainView->getView()->getFroxelConfigurationInfo();
|
||||
if (UTILS_UNLIKELY(fci.age != mFroxelInfoAge)) {
|
||||
mFroxelInfoAge = fci.age;
|
||||
auto width = fci.info.width;
|
||||
auto height = fci.info.height;
|
||||
auto depth = fci.info.depth;
|
||||
auto froxelDimension = fci.info.froxelDimension;
|
||||
auto viewportWidth = fci.info.viewportWidth;
|
||||
auto viewportHeight = fci.info.viewportHeight;
|
||||
auto zLightFar = fci.info.zLightFar;
|
||||
auto linearizer = fci.info.linearizer;
|
||||
auto p = fci.info.p;
|
||||
auto ct = fci.info.clipTransform;
|
||||
cameraGrid->update(width, height, depth,
|
||||
[=](int const i) {
|
||||
float x = float(2 * i * froxelDimension.x) / float(viewportWidth ) - 1.0f;
|
||||
x = (x - ct.z) / ct.x;
|
||||
return x;
|
||||
},
|
||||
[=](int const j) {
|
||||
float y = float(2 * j * froxelDimension.y) / float(viewportHeight) - 1.0f;
|
||||
y = (y - ct.w) / ct.y;
|
||||
return y;
|
||||
},
|
||||
[=](int const k) {
|
||||
float const z_view = -zLightFar * std::exp2(float(k - depth) * linearizer);
|
||||
auto c = p * float4{ 0, 0, z_view, 1 };
|
||||
float const z_clip_dx = k == 0 ? 1.0f : c.z / c.w;
|
||||
float const z_clip_gl = (1 - z_clip_dx) * 2.0f - 1.0f;
|
||||
return z_clip_gl;
|
||||
});
|
||||
}
|
||||
|
||||
auto& rcm = mEngine->getRenderableManager();
|
||||
if (config.splitView) {
|
||||
rcm.setLayerMask(rcm.getInstance(cameraCube->getSolidRenderable()), 0x3, mCameraFrustumEnabled);
|
||||
rcm.setLayerMask(rcm.getInstance(cameraCube->getWireFrameRenderable()), 0x3, mCameraFrustumEnabled);
|
||||
}
|
||||
rcm.setLayerMask(rcm.getInstance(cameraGrid->getWireFrameRenderable()), 0x3, mFroxelGridEnabled);
|
||||
|
||||
// Update the cube distortion matrix used for frustum visualization.
|
||||
auto& rcm = mEngine->getRenderableManager();
|
||||
auto const csm = window->mMainView->getView()->getDirectionalShadowCameras();
|
||||
// show/hide the cascades
|
||||
for (size_t i = 0 ; i < 4; i++) {
|
||||
@@ -483,15 +531,14 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
if (csm[i]) {
|
||||
lightmapCubes[i].mapFrustum(*mEngine, csm[i]);
|
||||
}
|
||||
uint8_t const layer = csm[i] ? 0x2 : 0x0;
|
||||
rcm.setLayerMask(rcm.getInstance(lightmapCubes[i].getSolidRenderable()),
|
||||
0x3, layer);
|
||||
rcm.setLayerMask(rcm.getInstance(lightmapCubes[i].getWireFrameRenderable()),
|
||||
0x3, layer);
|
||||
uint8_t const layer = csm[i] ? mDirectionalShadowFrustumEnabled : 0x0;
|
||||
rcm.setLayerMask(rcm.getInstance(lightmapCubes[i].getSolidRenderable()), 0x3, layer);
|
||||
rcm.setLayerMask(rcm.getInstance(lightmapCubes[i].getWireFrameRenderable()), 0x3, layer);
|
||||
}
|
||||
}
|
||||
|
||||
cameraCube->mapFrustum(*mEngine, window->mMainCamera);
|
||||
cameraGrid->mapFrustum(*mEngine, window->mMainCamera);
|
||||
|
||||
// Delay rendering for roughly one monitor refresh interval
|
||||
// TODO: Use SDL_GL_SetSwapInterval for proper vsync
|
||||
@@ -549,6 +596,7 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
cleanupCallback(mEngine, window->mMainView->getView(), mScene);
|
||||
|
||||
cameraCube.reset();
|
||||
cameraGrid.reset();
|
||||
lightmapCubes.clear();
|
||||
window.reset();
|
||||
|
||||
@@ -661,6 +709,30 @@ void FilamentApp::initSDL() {
|
||||
FILAMENT_CHECK_POSTCONDITION(SDL_Init(SDL_INIT_EVENTS) == 0) << "SDL_Init Failure";
|
||||
}
|
||||
|
||||
void FilamentApp::setCameraFrustumEnabled(bool enabled) noexcept {
|
||||
mCameraFrustumEnabled = enabled ? 0x2 : 0x0;
|
||||
}
|
||||
|
||||
void FilamentApp::setDirectionalShadowFrustumEnabled(bool enabled) noexcept {
|
||||
mDirectionalShadowFrustumEnabled = enabled ? 0x2 : 0x0;
|
||||
}
|
||||
|
||||
void FilamentApp::setFroxelGridEnabled(bool enabled) noexcept {
|
||||
mFroxelGridEnabled = enabled ? 0x3 : 0x0;
|
||||
}
|
||||
|
||||
bool FilamentApp::isCameraFrustumEnabled() const noexcept {
|
||||
return !!mCameraFrustumEnabled;
|
||||
}
|
||||
|
||||
bool FilamentApp::isDirectionalShadowFrustumEnabled() const noexcept {
|
||||
return !!mDirectionalShadowFrustumEnabled;
|
||||
}
|
||||
|
||||
bool FilamentApp::isFroxelGridEnabled() const noexcept {
|
||||
return !!mFroxelGridEnabled;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
FilamentApp::Window::Window(FilamentApp* filamentApp,
|
||||
@@ -979,25 +1051,22 @@ void FilamentApp::Window::configureCamerasForWindow() {
|
||||
|
||||
double near = mFilamentApp->mCameraNear;
|
||||
double far = mFilamentApp->mCameraFar;
|
||||
if (mMainView->getView()->getStereoscopicOptions().enabled) {
|
||||
mat4 projections[4];
|
||||
projections[0] = Camera::projection(mFilamentApp->mCameraFocalLength, 1.0, near, far);
|
||||
projections[1] = projections[0];
|
||||
// simulate foveated rendering
|
||||
projections[2] = Camera::projection(mFilamentApp->mCameraFocalLength * 2.0, 1.0, near, far);
|
||||
projections[3] = projections[2];
|
||||
mMainCamera->setCustomEyeProjection(projections, 4, projections[0], near, far);
|
||||
} else {
|
||||
mMainCamera->setLensProjection(mFilamentApp->mCameraFocalLength, 1.0, near, far);
|
||||
}
|
||||
mDebugCamera->setProjection(45.0, double(mainWidth) / height, 0.0625, 4096, Camera::Fov::VERTICAL);
|
||||
|
||||
auto aspectRatio = double(mainWidth) / height;
|
||||
if (mMainView->getView()->getStereoscopicOptions().enabled) {
|
||||
const int ec = mConfig.stereoscopicEyeCount;
|
||||
aspectRatio = double(mainWidth) / ec / height;
|
||||
|
||||
mat4 projections[4];
|
||||
projections[0] = Camera::projection(mFilamentApp->mCameraFocalLength, aspectRatio, near, far);
|
||||
projections[1] = projections[0];
|
||||
// simulate foveated rendering
|
||||
projections[2] = Camera::projection(mFilamentApp->mCameraFocalLength * 2.0, aspectRatio, near, far);
|
||||
projections[3] = projections[2];
|
||||
mMainCamera->setCustomEyeProjection(projections, 4, projections[0], near, far);
|
||||
} else {
|
||||
mMainCamera->setLensProjection(mFilamentApp->mCameraFocalLength, aspectRatio, near, far);
|
||||
}
|
||||
mMainCamera->setScaling({1.0 / aspectRatio, 1.0});
|
||||
mDebugCamera->setProjection(45.0, aspectRatio, 0.0625, 4096, Camera::Fov::VERTICAL);
|
||||
|
||||
// We're in split view when there are more views than just the Main and UI views.
|
||||
if (splitview) {
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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 <filamentapp/Grid.h>
|
||||
|
||||
#include <filament/Box.h>
|
||||
#include <filament/Camera.h>
|
||||
#include <filament/Color.h>
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndexBuffer.h>
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MaterialEnums.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
|
||||
#include <utils/EntityManager.h>
|
||||
|
||||
#include <math/vec3.h>
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <utils/Log.h>
|
||||
|
||||
using namespace filament;
|
||||
|
||||
using namespace filament::math;
|
||||
using namespace filament;
|
||||
|
||||
Grid::Grid(Engine& engine, Material const* material, float3 linearColor)
|
||||
: mEngine(engine),
|
||||
mMaterial(material) {
|
||||
|
||||
if (mMaterial) {
|
||||
mMaterialInstanceWireFrame = mMaterial->createInstance();
|
||||
mMaterialInstanceWireFrame->setDepthCulling(true);
|
||||
mMaterialInstanceWireFrame->setParameter("color", RgbaType::LINEAR,
|
||||
LinearColorA{linearColor.r, linearColor.g, linearColor.b, 0.25f});
|
||||
}
|
||||
|
||||
utils::EntityManager& em = utils::EntityManager::get();
|
||||
mWireFrameRenderable = em.create();
|
||||
|
||||
RenderableManager::Builder(1)
|
||||
.boundingBox({ { -1.0f, -1.0f, -1.0f }, { 1.0f, 1.0f, 1.0f } })
|
||||
.material(0, mMaterialInstanceWireFrame)
|
||||
.priority(6)
|
||||
.culling(false)
|
||||
.build(engine, mWireFrameRenderable);
|
||||
}
|
||||
|
||||
Grid::Grid(Grid&& rhs) noexcept
|
||||
: mEngine(rhs.mEngine) {
|
||||
using std::swap;
|
||||
swap(rhs.mVertexBuffer, mVertexBuffer);
|
||||
swap(rhs.mIndexBuffer, mIndexBuffer);
|
||||
swap(rhs.mMaterial, mMaterial);
|
||||
swap(rhs.mMaterialInstanceWireFrame, mMaterialInstanceWireFrame);
|
||||
swap(rhs.mWireFrameRenderable, mWireFrameRenderable);
|
||||
}
|
||||
|
||||
Grid::~Grid() {
|
||||
mEngine.destroy(mVertexBuffer);
|
||||
mEngine.destroy(mIndexBuffer);
|
||||
|
||||
// We don't own the material, only instances
|
||||
mEngine.destroy(mWireFrameRenderable);
|
||||
|
||||
// material instances must be destroyed after the renderables
|
||||
mEngine.destroy(mMaterialInstanceWireFrame);
|
||||
|
||||
utils::EntityManager& em = utils::EntityManager::get();
|
||||
em.destroy(mWireFrameRenderable);
|
||||
}
|
||||
|
||||
void Grid::mapFrustum(Engine& engine, Camera const* camera) {
|
||||
// the Camera far plane is at infinity, but we want it closer for display
|
||||
const mat4 vm(camera->getModelMatrix());
|
||||
mat4 const p(vm * inverse(camera->getProjectionMatrix()));
|
||||
mapFrustum(engine, p);
|
||||
}
|
||||
|
||||
void Grid::mapFrustum(Engine& engine, mat4 const& transform) {
|
||||
// the Camera far plane is at infinity, but we want it closer for display
|
||||
mat4f const p(transform);
|
||||
auto& tcm = engine.getTransformManager();
|
||||
tcm.setTransform(tcm.getInstance(mWireFrameRenderable), p);
|
||||
}
|
||||
|
||||
void Grid::mapAabb(Engine& engine, Box const& box) {
|
||||
mat4 const p = mat4::translation(box.center) * mat4::scaling(box.halfExtent);
|
||||
mapFrustum(engine, p);
|
||||
}
|
||||
|
||||
void Grid::update(uint32_t width, uint32_t height, uint32_t depth) {
|
||||
// [-1, 1] range (default behavior)
|
||||
update(width, height, depth,
|
||||
[](int const index) { return float(index) * 2.0f - 1.0f; },
|
||||
[](int const index) { return float(index) * 2.0f - 1.0f; },
|
||||
[](int const index) { return float(index) * 2.0f - 1.0f; });
|
||||
}
|
||||
|
||||
void Grid::update(uint32_t const width, uint32_t const height, uint32_t const depth,
|
||||
Generator const& genWidth, Generator const& genHeight, Generator const& genDepth) {
|
||||
|
||||
// First, destroy the existing buffers.
|
||||
mEngine.destroy(mVertexBuffer);
|
||||
mEngine.destroy(mIndexBuffer);
|
||||
|
||||
std::vector<float3> vertices;
|
||||
std::vector<uint32_t> indices;
|
||||
size_t const verticeCount = (depth + 1) * (height + 1) * (width + 1);
|
||||
vertices.reserve(verticeCount);
|
||||
indices.reserve(verticeCount * 2);
|
||||
|
||||
// Generate vertices
|
||||
// The vertices are generated to form a grid in the [-1, 1] range on all axes.
|
||||
for (int k = 0; k <= depth; ++k) {
|
||||
auto const z = genDepth(k);
|
||||
for (int j = 0; j <= height; ++j) {
|
||||
auto const y = genHeight(j);
|
||||
for (int i = 0; i <= width; ++i) {
|
||||
auto const x = genWidth(i);
|
||||
vertices.push_back({ x, y, z });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate indices for the lines
|
||||
// The indices connect the vertices to form the grid lines.
|
||||
auto getVertexIndex = [=](uint32_t const i, uint32_t const j, uint32_t const k) {
|
||||
return k * (width + 1) * (height + 1) + j * (width + 1) + i;
|
||||
};
|
||||
|
||||
// Lines along X axis
|
||||
for (uint32_t k = 0; k <= depth; ++k) {
|
||||
for (uint32_t j = 0; j <= height; ++j) {
|
||||
indices.push_back(getVertexIndex(0, j, k));
|
||||
indices.push_back(getVertexIndex(width, j, k));
|
||||
}
|
||||
}
|
||||
|
||||
// Lines along Y axis
|
||||
for (uint32_t k = 0; k <= depth; ++k) {
|
||||
for (uint32_t i = 0; i <= width; ++i) {
|
||||
indices.push_back(getVertexIndex(i, 0, k));
|
||||
indices.push_back(getVertexIndex(i, height, k));
|
||||
}
|
||||
}
|
||||
|
||||
// Lines along Z axis
|
||||
for (uint32_t j = 0; j <= height; ++j) {
|
||||
for (uint32_t i = 0; i <= width; ++i) {
|
||||
indices.push_back(getVertexIndex(i, j, 0));
|
||||
indices.push_back(getVertexIndex(i, j, depth));
|
||||
}
|
||||
}
|
||||
|
||||
const size_t vertexCount = vertices.size();
|
||||
mVertexBuffer = VertexBuffer::Builder()
|
||||
.vertexCount(vertexCount)
|
||||
.bufferCount(1)
|
||||
.attribute(POSITION, 0, VertexBuffer::AttributeType::FLOAT3)
|
||||
.build(mEngine);
|
||||
|
||||
auto vertexData = vertices.data();
|
||||
mVertexBuffer->setBufferAt(mEngine, 0,
|
||||
VertexBuffer::BufferDescriptor::make(
|
||||
vertexData, vertexCount * sizeof(vertices[0]),
|
||||
[v = std::move(vertices)](void*, size_t) {}));
|
||||
|
||||
const size_t indexCount = indices.size();
|
||||
mIndexBuffer = IndexBuffer::Builder()
|
||||
.indexCount(indexCount)
|
||||
.build(mEngine);
|
||||
|
||||
auto indexData = indices.data();
|
||||
mIndexBuffer->setBuffer(mEngine,
|
||||
IndexBuffer::BufferDescriptor::make(
|
||||
indexData, indexCount * sizeof(uint32_t),
|
||||
[i = std::move(indices)](void*, size_t) {}));
|
||||
|
||||
auto& rcm = mEngine.getRenderableManager();
|
||||
auto instance = rcm.getInstance(mWireFrameRenderable);
|
||||
rcm.setGeometryAt(instance, 0, RenderableManager::PrimitiveType::LINES,
|
||||
mVertexBuffer, mIndexBuffer, 0, indexCount);
|
||||
}
|
||||
@@ -586,6 +586,12 @@ int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusio
|
||||
i = parse(tokens, i + 1, jsonChunk, &out->sampleStepsPerSlice);
|
||||
} else if (compare(tok, jsonChunk, "thicknessHeuristic") == 0) {
|
||||
i = parse(tokens, i + 1, jsonChunk, &out->thicknessHeuristic);
|
||||
} else if (compare(tok, jsonChunk, "useVisibilityBitmasks") == 0) {
|
||||
i = parse(tokens, i + 1, jsonChunk, &out->useVisibilityBitmasks);
|
||||
} else if (compare(tok, jsonChunk, "constThickness") == 0) {
|
||||
i = parse(tokens, i + 1, jsonChunk, &out->constThickness);
|
||||
} else if (compare(tok, jsonChunk, "linearThickness") == 0) {
|
||||
i = parse(tokens, i + 1, jsonChunk, &out->linearThickness);
|
||||
} else {
|
||||
slog.w << "Invalid Gtao key: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
||||
i = parse(tokens, i + 1);
|
||||
@@ -602,7 +608,10 @@ std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions::Gtao&
|
||||
return out << "{\n"
|
||||
<< "\"sampleSliceCount\": " << int(in.sampleSliceCount) << ",\n"
|
||||
<< "\"sampleStepsPerSlice\": " << int(in.sampleStepsPerSlice) << ",\n"
|
||||
<< "\"thicknessHeuristic\": " << (in.thicknessHeuristic) << "\n"
|
||||
<< "\"thicknessHeuristic\": " << (in.thicknessHeuristic) << ",\n"
|
||||
<< "\"useVisibilityBitmasks\": " << to_string(in.useVisibilityBitmasks) << ",\n"
|
||||
<< "\"constThickness\": " << (in.constThickness) << ",\n"
|
||||
<< "\"linearThickness\": " << to_string(in.linearThickness) << "\n"
|
||||
<< "}";
|
||||
}
|
||||
|
||||
|
||||
@@ -854,6 +854,11 @@ void ViewerGui::updateUserInterface() {
|
||||
|
||||
ImGui::SliderInt("Slice Count", &sliceCount, 1, 10);
|
||||
ImGui::SliderInt("Steps Per Slice", &stepsPerSlice, 1, 4);
|
||||
ImGui::Checkbox("Use Visibility Bitmasks", &ssao.gtao.useVisibilityBitmasks);
|
||||
if (ssao.gtao.useVisibilityBitmasks) {
|
||||
ImGui::SliderFloat("Constant Thickness", &ssao.gtao.constThickness, 0.01f, 10.0f);
|
||||
ImGui::Checkbox("Linear Thickness", &ssao.gtao.linearThickness);
|
||||
}
|
||||
|
||||
ssao.gtao.sampleSliceCount = static_cast<uint8_t>(sliceCount);
|
||||
ssao.gtao.sampleStepsPerSlice = static_cast<uint8_t>(stepsPerSlice);
|
||||
@@ -1133,15 +1138,15 @@ void ViewerGui::updateUserInterface() {
|
||||
debug.getPropertyAddress<bool>("d.stereo.combine_multiview_images"));
|
||||
ImGui::Unindent();
|
||||
#endif
|
||||
#endif
|
||||
ImGui::SliderFloat("Ocular distance", &mSettings.viewer.cameraEyeOcularDistance, 0.0f,
|
||||
1.0f);
|
||||
ImGui::SliderFloat("Ocular distance",
|
||||
&mSettings.viewer.cameraEyeOcularDistance, 0.0f, 1.0f);
|
||||
|
||||
float toeInDegrees = mSettings.viewer.cameraEyeToeIn / f::PI * 180.0f;
|
||||
ImGui::SliderFloat("Toe in", &toeInDegrees, 0.0f, 30.0, "%.3f°");
|
||||
mSettings.viewer.cameraEyeToeIn = toeInDegrees / 180.0f * f::PI;
|
||||
|
||||
ImGui::Unindent();
|
||||
#endif
|
||||
}
|
||||
|
||||
colorGradingUI(mSettings, mRangePlot, mCurvePlot, mToneMapPlot);
|
||||
|
||||
@@ -19,6 +19,7 @@ set(RESOURCE_BINS)
|
||||
set(MATERIAL_SRCS
|
||||
materials/aiDefaultMat.mat
|
||||
materials/bakedColor.mat
|
||||
materials/bakedColorWithMutableConstants.mat
|
||||
materials/bakedTexture.mat
|
||||
materials/aoPreview.mat
|
||||
materials/arrayTexture.mat
|
||||
@@ -281,6 +282,7 @@ if (NOT ANDROID)
|
||||
add_demo(lightbulb)
|
||||
add_demo(material_sandbox)
|
||||
add_demo(multiple_windows)
|
||||
add_demo(mutable_constants)
|
||||
# Tint does not support setting gl_PointSize, disable the relevant sample if using WebGPU
|
||||
# https://github.com/gpuweb/gpuweb/issues/1190
|
||||
if (NOT FILAMENT_SUPPORTS_WEBGPU)
|
||||
|
||||
+11
-1
@@ -217,7 +217,7 @@ static std::ifstream::pos_type getFileSize(const char* filename) {
|
||||
}
|
||||
|
||||
static int handleCommandLineArguments(int argc, char* argv[], App* app) {
|
||||
static constexpr const char* OPTSTR = "ha:f:i:usc:rt:b:evg:dw:";
|
||||
static constexpr const char* OPTSTR = "ha:f:i:usc:rt:y:b:evg:dw:";
|
||||
static const struct option OPTIONS[] = {
|
||||
{ "help", no_argument, nullptr, 'h' },
|
||||
{ "api", required_argument, nullptr, 'a' },
|
||||
@@ -949,12 +949,22 @@ int main(int argc, char** argv) {
|
||||
"d.shadowmap.display_shadow_texture_channel"), 0, 3);
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
bool cameraFrustum = FilamentApp::get().isCameraFrustumEnabled();
|
||||
ImGui::Checkbox("Show Camera Frustum", &cameraFrustum);
|
||||
FilamentApp::get().setCameraFrustumEnabled(cameraFrustum);
|
||||
|
||||
bool shadowFrustum = FilamentApp::get().isDirectionalShadowFrustumEnabled();
|
||||
ImGui::Checkbox("Show Shadow Frustum", &shadowFrustum);
|
||||
FilamentApp::get().setDirectionalShadowFrustumEnabled(shadowFrustum);
|
||||
|
||||
bool debugFroxelVisualization;
|
||||
if (debug.getProperty("d.lighting.debug_froxel_visualization",
|
||||
&debugFroxelVisualization)) {
|
||||
ImGui::Checkbox("Froxel Visualization", &debugFroxelVisualization);
|
||||
debug.setProperty("d.lighting.debug_froxel_visualization",
|
||||
debugFroxelVisualization);
|
||||
FilamentApp::get().setFroxelGridEnabled(debugFroxelVisualization);
|
||||
}
|
||||
|
||||
auto dataSource = debug.getDataSource("d.view.frame_info");
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
material {
|
||||
name : BakedColorWithMutableConstants,
|
||||
requires : [
|
||||
color
|
||||
],
|
||||
constants : [
|
||||
{
|
||||
name: swizzle,
|
||||
type: bool,
|
||||
mutable: true,
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
name: darken,
|
||||
type: bool,
|
||||
mutable: true,
|
||||
},
|
||||
{
|
||||
// In generated shader sources, we first specify immutable
|
||||
// specialization constants, then mutable specialization constants.
|
||||
// By including this dummy constant, we offset the ID of the first
|
||||
// mutable constant by one. This is done here purely for testing
|
||||
// purposes.
|
||||
name: dummy,
|
||||
type: bool,
|
||||
}
|
||||
],
|
||||
shadingModel : unlit,
|
||||
culling : none,
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
if (materialConstants_swizzle) {
|
||||
material.baseColor = getColor().gbra;
|
||||
} else {
|
||||
material.baseColor = getColor();
|
||||
}
|
||||
if (materialConstants_darken) {
|
||||
material.baseColor.rgb *= 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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/Camera.h>
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndexBuffer.h>
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/Skybox.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <utils/EntityManager.h>
|
||||
|
||||
#include <filamentapp/Config.h>
|
||||
#include <filamentapp/FilamentApp.h>
|
||||
|
||||
#include <getopt/getopt.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
#include "generated/resources/resources.h"
|
||||
|
||||
using namespace filament;
|
||||
using utils::Entity;
|
||||
using utils::EntityManager;
|
||||
|
||||
struct App {
|
||||
Config config;
|
||||
VertexBuffer* vb;
|
||||
IndexBuffer* ib;
|
||||
Material* mat;
|
||||
Camera* cam;
|
||||
Entity camera;
|
||||
Skybox* skybox;
|
||||
Entity renderable;
|
||||
};
|
||||
|
||||
struct Vertex {
|
||||
filament::math::float2 position;
|
||||
uint32_t color;
|
||||
};
|
||||
|
||||
static const Vertex TRIANGLE_VERTICES[3] = {
|
||||
{{1, 0}, 0xffff0000u},
|
||||
{{cos(M_PI * 2 / 3), sin(M_PI * 2 / 3)}, 0xff00ff00u},
|
||||
{{cos(M_PI * 4 / 3), sin(M_PI * 4 / 3)}, 0xff0000ffu},
|
||||
};
|
||||
|
||||
static constexpr uint16_t TRIANGLE_INDICES[3] = { 0, 1, 2 };
|
||||
|
||||
static void printUsage(char* name) {
|
||||
std::string exec_name(utils::Path(name).getName());
|
||||
std::string usage(
|
||||
"MUTABLE_CONSTANTS renders a spinning colored triangle\n"
|
||||
"Usage:\n"
|
||||
" MUTABLE_CONSTANTS [options]\n"
|
||||
"Options:\n"
|
||||
" --help, -h\n"
|
||||
" Prints this message\n\n"
|
||||
" --api, -a\n"
|
||||
" Specify the backend API: opengl, vulkan, metal, or webgpu\n"
|
||||
" (note: webgpu is a no-op for now, printing backend\n"
|
||||
" component info if FILAMENT_BACKEND_DEBUG_FLAG, \n"
|
||||
" set at build time, includes the \n"
|
||||
" FWGPU_PRINT_SYSTEM bit flag 0x2)\n"
|
||||
);
|
||||
const std::string from("MUTABLE_CONSTANTS");
|
||||
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
|
||||
usage.replace(pos, from.length(), exec_name);
|
||||
}
|
||||
std::cout << usage;
|
||||
}
|
||||
|
||||
static int handleCommandLineArguments(int argc, char* argv[], App* app) {
|
||||
static constexpr const char* OPTSTR = "ha:";
|
||||
static const struct option OPTIONS[] = {
|
||||
{ "help", no_argument, nullptr, 'h' },
|
||||
{ "api", required_argument, nullptr, 'a' },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
int opt;
|
||||
int option_index = 0;
|
||||
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) {
|
||||
std::string arg(optarg ? optarg : "");
|
||||
switch (opt) {
|
||||
default:
|
||||
case 'h':
|
||||
printUsage(argv[0]);
|
||||
exit(0);
|
||||
case 'a':
|
||||
if (arg == "opengl") {
|
||||
app->config.backend = Engine::Backend::OPENGL;
|
||||
} else if (arg == "vulkan") {
|
||||
app->config.backend = Engine::Backend::VULKAN;
|
||||
} else if (arg == "metal") {
|
||||
app->config.backend = Engine::Backend::METAL;
|
||||
} else if (arg == "webgpu") {
|
||||
app->config.backend = Engine::Backend::WEBGPU;
|
||||
} else {
|
||||
std::cerr << "Unrecognized backend. Must be "
|
||||
"'opengl'|'vulkan'|'metal'|'webgpu'.\n";
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return optind;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
App app{};
|
||||
app.config.title = "mutable_constants";
|
||||
handleCommandLineArguments(argc, argv, &app);
|
||||
|
||||
auto setup = [&app](Engine* engine, View* view, Scene* scene) {
|
||||
app.skybox = Skybox::Builder().color({0.1, 0.125, 0.25, 1.0}).build(*engine);
|
||||
scene->setSkybox(app.skybox);
|
||||
view->setPostProcessingEnabled(false);
|
||||
static_assert(sizeof(Vertex) == 12, "Strange vertex size.");
|
||||
app.vb = VertexBuffer::Builder()
|
||||
.vertexCount(3)
|
||||
.bufferCount(1)
|
||||
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT2, 0, 12)
|
||||
.attribute(VertexAttribute::COLOR, 0, VertexBuffer::AttributeType::UBYTE4, 8, 12)
|
||||
.normalized(VertexAttribute::COLOR)
|
||||
.build(*engine);
|
||||
app.vb->setBufferAt(*engine, 0,
|
||||
VertexBuffer::BufferDescriptor(TRIANGLE_VERTICES, 36, nullptr));
|
||||
app.ib = IndexBuffer::Builder()
|
||||
.indexCount(3)
|
||||
.bufferType(IndexBuffer::IndexType::USHORT)
|
||||
.build(*engine);
|
||||
app.ib->setBuffer(*engine,
|
||||
IndexBuffer::BufferDescriptor(TRIANGLE_INDICES, 6, nullptr));
|
||||
app.mat = Material::Builder().package(RESOURCES_BAKEDCOLORWITHMUTABLECONSTANTS_DATA,
|
||||
RESOURCES_BAKEDCOLORWITHMUTABLECONSTANTS_SIZE)
|
||||
.build(*engine);
|
||||
app.renderable = EntityManager::get().create();
|
||||
RenderableManager::Builder(1)
|
||||
.boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
|
||||
.material(0, app.mat->getDefaultInstance())
|
||||
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, app.vb, app.ib, 0, 3)
|
||||
.culling(false)
|
||||
.receiveShadows(false)
|
||||
.castShadows(false)
|
||||
.build(*engine, app.renderable);
|
||||
scene->addEntity(app.renderable);
|
||||
app.camera = utils::EntityManager::get().create();
|
||||
app.cam = engine->createCamera(app.camera);
|
||||
view->setCamera(app.cam);
|
||||
};
|
||||
|
||||
auto cleanup = [&app](Engine* engine, View*, Scene*) {
|
||||
engine->destroy(app.skybox);
|
||||
engine->destroy(app.renderable);
|
||||
engine->destroy(app.mat);
|
||||
engine->destroy(app.vb);
|
||||
engine->destroy(app.ib);
|
||||
engine->destroyCameraComponent(app.camera);
|
||||
utils::EntityManager::get().destroy(app.camera);
|
||||
};
|
||||
|
||||
auto gui = [&app](Engine* engine, View* view) {
|
||||
MaterialInstance* mi = app.mat->getDefaultInstance();
|
||||
bool swizzle = mi->getConstant<bool>("swizzle");
|
||||
bool darken = mi->getConstant<bool>("darken");
|
||||
ImGui::Checkbox("Swizzle", &swizzle);
|
||||
ImGui::Checkbox("Darken", &darken);
|
||||
if (swizzle != mi->getConstant<bool>("swizzle")) {
|
||||
mi->setConstant("swizzle", swizzle);
|
||||
}
|
||||
if (darken != mi->getConstant<bool>("darken")) {
|
||||
mi->setConstant("darken", darken);
|
||||
}
|
||||
};
|
||||
|
||||
FilamentApp::get().animate([&app](Engine* engine, View* view, double now) {
|
||||
constexpr float ZOOM = 1.5f;
|
||||
const uint32_t w = view->getViewport().width;
|
||||
const uint32_t h = view->getViewport().height;
|
||||
const float aspect = (float) w / h;
|
||||
app.cam->setProjection(Camera::Projection::ORTHO,
|
||||
-aspect * ZOOM, aspect * ZOOM,
|
||||
-ZOOM, ZOOM, 0, 1);
|
||||
auto& tcm = engine->getTransformManager();
|
||||
tcm.setTransform(tcm.getInstance(app.renderable),
|
||||
filament::math::mat4f::rotation(now, filament::math::float3{ 0, 0, 1 }));
|
||||
});
|
||||
|
||||
FilamentApp::get().run(app.config, setup, cleanup, gui);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -506,11 +506,7 @@ vec3 evaluateRefraction(
|
||||
|
||||
// compute transmission T
|
||||
#if defined(MATERIAL_HAS_ABSORPTION)
|
||||
#if defined(MATERIAL_HAS_THICKNESS) || defined(MATERIAL_HAS_MICRO_THICKNESS)
|
||||
vec3 T = min(vec3(1.0), exp(-pixel.absorption * ray.d));
|
||||
#else
|
||||
vec3 T = 1.0 - pixel.absorption;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Roughness remapping so that an IOR of 1.0 means no microfacet refraction and an IOR
|
||||
|
||||
@@ -247,7 +247,7 @@ void evaluatePunctualLights(const MaterialInputs material,
|
||||
}
|
||||
|
||||
if (CONFIG_DEBUG_FROXEL_VISUALIZATION) {
|
||||
if (froxel.count > 0u) {
|
||||
if (froxel.count > 0u && frameUniforms.enableFroxelViz != 0) {
|
||||
const vec3 debugColors[17] = vec3[](
|
||||
vec3(0.0, 0.0, 0.0), // black
|
||||
vec3(0.0, 0.0, 0.1647), // darkest blue
|
||||
|
||||
@@ -117,15 +117,11 @@ void getCommonPixelParams(const MaterialInputs material, inout PixelParams pixel
|
||||
pixel.transmission = 1.0;
|
||||
#endif
|
||||
#if defined(MATERIAL_HAS_ABSORPTION)
|
||||
#if defined(MATERIAL_HAS_THICKNESS) || defined(MATERIAL_HAS_MICRO_THICKNESS)
|
||||
pixel.absorption = max(vec3(0.0), material.absorption);
|
||||
#else
|
||||
pixel.absorption = saturate(material.absorption);
|
||||
#endif
|
||||
#else
|
||||
pixel.absorption = vec3(0.0);
|
||||
#endif
|
||||
#if defined(MATERIAL_HAS_THICKNESS)
|
||||
#if defined(SHADING_MODEL_SUBSURFACE) || defined(MATERIAL_HAS_REFRACTION)
|
||||
pixel.thickness = max(0.0, material.thickness);
|
||||
#endif
|
||||
#if defined(MATERIAL_HAS_MICRO_THICKNESS) && (REFRACTION_TYPE == REFRACTION_TYPE_THIN)
|
||||
|
||||
@@ -23,6 +23,10 @@ function start_render_() {
|
||||
bash ${BUILD_COMMON_DIR}/get-mesa.sh
|
||||
fi
|
||||
|
||||
if [ ! -d ${GLTF_DIR} ]; then
|
||||
cat ${RENDERDIFF_TEST_DIR}/tests/gltf_models.txt | xargs bash ${BUILD_COMMON_DIR}/get-gltf-sample-assets.sh
|
||||
fi
|
||||
|
||||
# Install python deps
|
||||
python3 -m venv ${VENV_DIR}
|
||||
source ${VENV_DIR}/bin/activate
|
||||
|
||||
@@ -22,6 +22,7 @@ GOLDEN_OUTPUT_DIR="$(pwd)/out/renderdiff/goldens"
|
||||
RENDERDIFF_TEST_DIR="$(pwd)/test/renderdiff"
|
||||
MESA_DIR="$(pwd)/mesa/out/"
|
||||
VENV_DIR="$(pwd)/venv"
|
||||
GLTF_DIR="$(pwd)/gltf/Models"
|
||||
BUILD_COMMON_DIR="$(pwd)/build/common"
|
||||
|
||||
os_name=$(uname -s)
|
||||
|
||||
@@ -44,11 +44,22 @@ class RenderingConfig():
|
||||
class PresetConfig(RenderingConfig):
|
||||
def __init__(self, data, existing_models):
|
||||
RenderingConfig.__init__(self, data)
|
||||
models = data.get('models')
|
||||
if models:
|
||||
self.models = []
|
||||
|
||||
def _check(models):
|
||||
assert _is_list_of_strings(models)
|
||||
assert all(m in existing_models for m in models)
|
||||
|
||||
models = data.get('models')
|
||||
if models:
|
||||
_check(models)
|
||||
self.models = models
|
||||
model_list_file = data.get('model_list_file')
|
||||
if model_list_file and os.path.exists(model_list_file):
|
||||
with open(model_list_file, 'r') as f:
|
||||
models = list(filter(lambda a: len(a) > 0, map(lambda a: a.strip(), f.read().split('\n'))))
|
||||
_check(models)
|
||||
self.models += models
|
||||
|
||||
class TestConfig(RenderingConfig):
|
||||
def __init__(self, data, existing_models, presets):
|
||||
@@ -105,7 +116,9 @@ class RenderTestConfig():
|
||||
assert all(path.isdir(p) for p in model_search_paths)
|
||||
|
||||
model_paths = list(
|
||||
chain(*(glob.glob(f'{d}/**/*.glb', recursive=True) for d in model_search_paths)))
|
||||
chain(*(glob.glob(f'{d}/**/*.glb', recursive=True) for d in model_search_paths))) + \
|
||||
list(
|
||||
chain(*(glob.glob(f'{d}/**/*.gltf', recursive=True) for d in model_search_paths)))
|
||||
# This flatten the output for glob.glob
|
||||
self.models = {path.splitext(path.basename(model))[0]: model for model in model_paths}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
AlphaBlendModeTest
|
||||
AttenuationTest
|
||||
Box
|
||||
BoomBoxWithAxes
|
||||
BoxInterleaved
|
||||
BoxTextured
|
||||
BoxTexturedNonPowerOfTwo
|
||||
Duck
|
||||
IridescenceSuzanne
|
||||
Lantern
|
||||
MetalRoughSpheres
|
||||
NegativeScaleTest
|
||||
NormalTangentMirrorTest
|
||||
NormalTangentTest
|
||||
SpecularTest
|
||||
Sponza
|
||||
Suzanne
|
||||
TextureCoordinateTest
|
||||
TextureSettingsTest
|
||||
TwoSidedPlane
|
||||
@@ -113,6 +113,9 @@ Filament.loadGeneratedExtensions = function() {
|
||||
sampleSliceCount: 4,
|
||||
sampleStepsPerSlice: 3,
|
||||
thicknessHeuristic: 0.004,
|
||||
useVisibilityBitmasks: false,
|
||||
constThickness: 0.5,
|
||||
linearThickness: false,
|
||||
};
|
||||
return Object.assign(options, overrides);
|
||||
};
|
||||
|
||||
Vendored
+15
-1
@@ -1595,9 +1595,23 @@ export interface View$AmbientOcclusionOptions$Gtao {
|
||||
*/
|
||||
sampleStepsPerSlice?: number;
|
||||
/**
|
||||
* thickness heuristic, should be closed to 0
|
||||
* thickness heuristic, should be closed to 0. No effect when useVisibilityBitmasks sets to true.
|
||||
*/
|
||||
thicknessHeuristic?: number;
|
||||
/**
|
||||
* Enables or disables visibility bitmasks mode. Notes that bent normal doesn't work under this mode.
|
||||
* Caution: Changing this option at runtime is very expensive as it may trigger a shader re-compilation.
|
||||
*/
|
||||
useVisibilityBitmasks?: boolean;
|
||||
/**
|
||||
* constant thickness value of objects on the screen in world space. Only take effect when useVisibilityBitmasks is set to true.
|
||||
*/
|
||||
constThickness?: number;
|
||||
/**
|
||||
* Increase thickness with distance to maintain detail on distant surfaces.
|
||||
* Caution: Changing this option at runtime is very expensive as it may trigger a shader re-compilation.
|
||||
*/
|
||||
linearThickness?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -99,6 +99,9 @@ value_object<View::AmbientOcclusionOptions::Gtao>("View$AmbientOcclusionOptions$
|
||||
.field("sampleSliceCount", &View::AmbientOcclusionOptions::Gtao::sampleSliceCount)
|
||||
.field("sampleStepsPerSlice", &View::AmbientOcclusionOptions::Gtao::sampleStepsPerSlice)
|
||||
.field("thicknessHeuristic", &View::AmbientOcclusionOptions::Gtao::thicknessHeuristic)
|
||||
.field("useVisibilityBitmasks", &View::AmbientOcclusionOptions::Gtao::useVisibilityBitmasks)
|
||||
.field("constThickness", &View::AmbientOcclusionOptions::Gtao::constThickness)
|
||||
.field("linearThickness", &View::AmbientOcclusionOptions::Gtao::linearThickness)
|
||||
;
|
||||
|
||||
value_object<View::AmbientOcclusionOptions>("View$AmbientOcclusionOptions")
|
||||
|
||||
Reference in New Issue
Block a user