Compare commits

..
Author SHA1 Message Date
Powei Feng 4261577724 Add unfilterable 2025-06-16 10:25:13 -07:00
56 changed files with 540 additions and 1201 deletions
+1 -1
View File
@@ -138,7 +138,7 @@ jobs:
- uses: actions/upload-artifact@v4
with:
name: presubmit-renderdiff-result
path: ./out/renderdiff
path: ./out/renderdiff_tests
validate-wgsl-webgpu:
name: validate-wgsl-webgpu
+1 -1
View File
@@ -6,4 +6,4 @@ We are chaning the way Vulkan buffers are handled. We need to switch over to a m
**If you are cherry-picking a commit into an rc/ branch**: add the release note under the
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
## Release notes for next branch cut
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.62.0'
implementation 'com.google.android.filament:filament-android:1.61.1'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.62.0'
pod 'Filament', '~> 1.61.1'
```
## Documentation
-4
View File
@@ -7,10 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.62.0
- Add new `unfilterable` field to Filament Material's `sampler` [⚠️ **New Material Version**]
## v1.61.2
- samples: samples now have a CLI to select backend api
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.62.0
VERSION_NAME=1.61.1
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
Binary file not shown.
+1 -2
View File
@@ -1,7 +1,6 @@
#Wed Nov 17 10:40:18 PST 2021
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+109 -173
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#!/usr/bin/env sh
#
# Copyright © 2015-2021 the original authors.
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,99 +17,67 @@
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
MAX_FD="maximum"
warn () {
echo "$*"
} >&2
}
die () {
echo
echo "$*"
echo
exit 1
} >&2
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -119,9 +87,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD=$JAVA_HOME/bin/java
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -130,120 +98,88 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=$( cygpath --unix "$JAVACMD" )
JAVACMD=`cygpath --unix "$JAVACMD"`
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
+16 -19
View File
@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,8 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -41,13 +40,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
if "%ERRORLEVEL%" == "0" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
@@ -57,11 +56,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
@@ -76,15 +75,13 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
+2 -5
View File
@@ -1027,11 +1027,8 @@ samplerCubemap | Cubemap texture
[Table [materialParamsTypes]: Material parameter types]
Samplers
: Sampler types can have the following fields:
- `format` : which can be either `int` or `float` (defaults to `float`).
- `multisample` : a boolean to indicate whether the sampler is meant for multisampling (defaults to `false`)
- `unfilterable` : a boolean to indicate whether the sampling is not filtered (defaults to `false`)
: Sampler types can also specify a `format` which can be either `int` or `float` (defaults to
`float`).
Arrays
: A parameter can define an array of values by appending `[size]` after the type name, where
+1 -12
View File
@@ -269,8 +269,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPUDescriptorSetLayout.h
src/webgpu/WebGPUDriver.cpp
src/webgpu/WebGPUDriver.h
src/webgpu/WebGPUFence.cpp
src/webgpu/WebGPUFence.h
src/webgpu/WebGPUIndexBuffer.cpp
src/webgpu/WebGPUIndexBuffer.h
src/webgpu/WebGPUPipelineCreation.cpp
@@ -505,20 +503,11 @@ if (FILAMENT_SUPPORTS_WEBGPU)
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING}>:FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING>)
endif()
# ==================================================================================================
# Linker flags
# ==================================================================================================
target_link_libraries(${TARGET} PRIVATE
${OSMESA_LINKER_FLAGS}
$<$<AND:$<PLATFORM_ID:Linux>,$<CONFIG:Release>>:${LINUX_LINKER_OPTIMIZATION_FLAGS}>
)
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)
endif()
# ==================================================================================================
# Installation
# ==================================================================================================
@@ -500,12 +500,18 @@ struct DescriptorSetLayoutBinding {
DescriptorFlags flags = DescriptorFlags::NONE;
uint16_t count = 0;
// TODO: uncomment when needed. Note that this class is used as hash key. We need to ensure
// no uninitialized padding bytes.
// uint8_t externalSamplerDataIndex = EXTERNAL_SAMPLER_DATA_INDEX_UNUSED;
friend bool operator==(DescriptorSetLayoutBinding const& lhs,
DescriptorSetLayoutBinding const& rhs) noexcept {
return lhs.type == rhs.type &&
lhs.flags == rhs.flags &&
lhs.count == rhs.count &&
lhs.stageFlags == rhs.stageFlags;
// lhs.stageFlags == rhs.stageFlags &&
// lhs.externalSamplerDataIndex == rhs.externalSamplerDataIndex;
}
};
@@ -1253,6 +1259,26 @@ enum class SamplerCompareFunc : uint8_t {
N //!< Never. The depth / stencil test always fails.
};
//! this API is copied from (and only applies to) the Vulkan spec.
//! These specify YUV to RGB conversions.
enum class SamplerYcbcrModelConversion : uint8_t {
RGB_IDENTITY = 0,
YCBCR_IDENTITY = 1,
YCBCR_709 = 2,
YCBCR_601 = 3,
YCBCR_2020 = 4,
};
enum class SamplerYcbcrRange : uint8_t {
ITU_FULL = 0,
ITU_NARROW = 1,
};
enum class ChromaLocation : uint8_t {
COSITED_EVEN = 0,
MIDPOINT = 1,
};
//! Sampler parameters
struct SamplerParams { // NOLINT
SamplerMagFilter filterMag : 1; //!< magnification filter (NEAREST)
@@ -1321,9 +1347,94 @@ static_assert(sizeof(SamplerParams) == 4);
static_assert(sizeof(SamplerParams) <= sizeof(uint64_t),
"SamplerParams must be no more than 64 bits");
//! Sampler parameters
struct SamplerYcbcrConversion {// NOLINT
SamplerYcbcrModelConversion ycbcrModel : 4;
TextureSwizzle r : 4;
TextureSwizzle g : 4;
TextureSwizzle b : 4;
TextureSwizzle a : 4;
SamplerYcbcrRange ycbcrRange : 1;
ChromaLocation xChromaOffset : 1;
ChromaLocation yChromaOffset : 1;
SamplerMagFilter chromaFilter : 1;
uint8_t padding;
struct Hasher {
size_t operator()(const SamplerYcbcrConversion p) const noexcept {
// we don't use std::hash<> here, so we don't have to include <functional>
return *reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&p));
}
};
struct EqualTo {
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
assert_invariant(lhs.padding == 0);
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
return *pLhs == *pRhs;
}
};
struct LessThan {
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
assert_invariant(lhs.padding == 0);
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
return *pLhs < *pRhs;
}
};
private:
friend bool operator == (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept {
return SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
}
friend bool operator != (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept {
return !SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
}
friend bool operator < (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept {
return SamplerYcbcrConversion::LessThan{}(lhs, rhs);
}
};
static_assert(sizeof(SamplerYcbcrConversion) == 4);
static_assert(sizeof(SamplerYcbcrConversion) <= sizeof(uint64_t),
"SamplerYcbcrConversion must be no more than 64 bits");
struct ExternalSamplerDatum {
ExternalSamplerDatum(SamplerYcbcrConversion ycbcr, SamplerParams spm, uint32_t extFmt)
: YcbcrConversion(ycbcr),
samplerParams(spm),
externalFormat(extFmt) {}
bool operator==(ExternalSamplerDatum const& rhs) const {
return (YcbcrConversion == rhs.YcbcrConversion && samplerParams == rhs.samplerParams &&
externalFormat == rhs.externalFormat);
}
struct EqualTo {
bool operator()(const ExternalSamplerDatum& lhs,
const ExternalSamplerDatum& rhs) const noexcept {
return (lhs.YcbcrConversion == rhs.YcbcrConversion &&
lhs.samplerParams == rhs.samplerParams &&
lhs.externalFormat == rhs.externalFormat);
}
};
SamplerYcbcrConversion YcbcrConversion;
SamplerParams samplerParams;
uint32_t externalFormat;
};
// No implicit padding allowed due to it being a hash key.
static_assert(sizeof(ExternalSamplerDatum) == 12);
struct DescriptorSetLayout {
std::variant<utils::StaticString, utils::CString, std::monostate> label;
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
// TODO: uncomment when needed
// utils::FixedCapacityVector<ExternalSamplerDatum> externalSamplerData;
};
//! blending equation function
@@ -107,36 +107,6 @@ public:
MULTIVIEW,
};
/**
* This controls the priority level for GPU work scheduling, which helps prioritize the
* submitted GPU work and enables preemption.
*/
enum class GpuContextPriority : uint8_t {
/**
* Backend default GPU context priority (typically MEDIUM)
*/
DEFAULT,
/**
* For non-interactive, deferrable workloads. This should not interfere with standard
* applications.
*/
LOW,
/**
* The default priority level for standard applications.
*/
MEDIUM,
/**
* For high-priority, latency-sensitive workloads that are more important than standard
* applications.
*/
HIGH,
/**
* The highest priority, intended for system-critical, real-time applications where missing
* deadlines is unacceptable (e.g., VR/AR compositors or other system-critical tasks).
*/
REALTIME,
};
struct DriverConfig {
/**
* Size of handle arena in bytes. Setting to 0 indicates default value is to be used.
@@ -186,13 +156,6 @@ public:
* - PlatformMetal
*/
bool metalDisablePanicOnDrawableFailure = false;
/**
* GPU context priority level. Controls GPU work scheduling and preemption.
* This is only supported for:
* - PlatformEGL
*/
GpuContextPriority gpuContextPriority = GpuContextPriority::DEFAULT;
};
Platform() noexcept;
@@ -116,7 +116,7 @@ public:
* Destroys the object D at Handle<B> and construct a new D in its place
* e.g.:
* Handle<ConcreteTexture> h = allocateAndConstruct(w, h);
* ConcreteTexture* p = destroyAndConstruct(h, w, h);
* ConcreteTexture* p = reconstruct(h, w, h);
*/
template<typename D, typename B, typename ... ARGS>
std::enable_if_t<std::is_base_of_v<B, D>, D>*
@@ -209,42 +209,10 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const DriverConfig& drive
}
#endif
// Configure GPU context priority level for scheduling and preemption
if (driverConfig.gpuContextPriority != Platform::GpuContextPriority::DEFAULT) {
if (extensions.has("EGL_IMG_context_priority")) {
EGLint priorityLevel = EGL_CONTEXT_PRIORITY_MEDIUM_IMG;
const char* priorityName;
switch (driverConfig.gpuContextPriority) {
case Platform::GpuContextPriority::DEFAULT:
assert_invariant(false);
break;
case Platform::GpuContextPriority::LOW:
priorityLevel = EGL_CONTEXT_PRIORITY_LOW_IMG;
priorityName = "LOW";
break;
case Platform::GpuContextPriority::MEDIUM:
priorityLevel = EGL_CONTEXT_PRIORITY_MEDIUM_IMG;
priorityName = "MEDIUM";
break;
case Platform::GpuContextPriority::HIGH:
priorityLevel = EGL_CONTEXT_PRIORITY_HIGH_IMG;
priorityName = "HIGH";
break;
case Platform::GpuContextPriority::REALTIME:
priorityLevel = EGL_CONTEXT_PRIORITY_HIGH_IMG;
priorityName = "REALTIME(=HIGH)";
break;
}
contextAttribs[EGL_CONTEXT_PRIORITY_LEVEL_IMG] = priorityLevel;
LOG(INFO) << "EGL: Enabling GPU context priority: " << priorityName;
} else {
LOG(WARNING) << "EGL: GPU context priority requested but not supported";
}
}
// config use for creating the context
EGLConfig eglConfig = EGL_NO_CONFIG_KHR;
if (UTILS_UNLIKELY(!ext.egl.KHR_no_config_context)) {
// find a config we can use if we don't have "EGL_KHR_no_config_context" and that we can use
// for the dummy pbuffer surface.
@@ -15,10 +15,10 @@
*/
#include "VulkanBufferProxy.h"
#include "VulkanCommands.h"
#include "VulkanMemory.h"
#include "VulkanBufferCache.h"
#include "VulkanCommands.h"
#include "VulkanContext.h"
#include "VulkanMemory.h"
using namespace bluevk;
@@ -36,38 +36,6 @@ VulkanBufferProxy::VulkanBufferProxy(VmaAllocator allocator, VulkanStagePool& st
void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* cpuData,
uint32_t byteOffset, uint32_t numBytes) {
// The VulkanBuffer is available if the only object reference is hold by the
// `VulkanBufferProxy`. This means that the buffer is not currently referenced by a
// `VulkanCommandBuffer`.
bool const isAvailable = mBuffer->getCount() == 1;
if (isAvailable) {
// We are up to date with all the update operations, so no need to synchronize with previous
// updates.
mUpdatedBytes = 0;
mUpdatedOffset = 0;
}
// Keep track of the VulkanBuffer usage
commands.acquire(mBuffer);
// Check if we can just memcpy directly to the GPU memory.
// This is only allowed for UNIFORMS that are AVAILABLE and the memory is HOST_VISIBLE
// (supports memcpy from host). This works regardless if it's a full or partial update of the
// buffer.
bool const isMemcopyable = mBuffer->getGpuBuffer()->allocationInfo.pMappedData != nullptr;
bool const isUniform = getUsage() == VulkanBufferUsage::UNIFORM;
bool const useMemcpy =
isUniform && isMemcopyable && isAvailable && !FVK_FORCE_STAGING_FOR_BUFFER_UPDATES;
if (useMemcpy) {
char* dest = static_cast<char*>(mBuffer->getGpuBuffer()->allocationInfo.pMappedData) +
byteOffset;
memcpy(dest, cpuData, numBytes);
vmaFlushAllocation(mAllocator, mBuffer->getGpuBuffer()->vmaAllocation, byteOffset,
numBytes);
return;
}
// Note: this should be stored within the command buffer before going out of
// scope, so that the command buffer can manage its lifecycle.
fvkmemory::resource_ptr<VulkanStage::Segment> stage = mStagePool.acquireStage(numBytes);
@@ -160,4 +128,4 @@ VulkanBufferUsage VulkanBufferProxy::getUsage() const noexcept {
return mBuffer->getGpuBuffer()->usage;
}
} // namespace filament::backend
}// namespace filament::backend
@@ -49,6 +49,6 @@ private:
uint32_t mUpdatedBytes = 0;
};
} // namespace filament::backend
}// namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANBUFFERPROXY_H
#endif// TNT_FILAMENT_BACKEND_VULKANBUFFERPROXY_H
@@ -214,8 +214,4 @@ constexpr static const int FVK_MAX_PIPELINE_AGE = FVK_MAX_COMMAND_BUFFERS;
// destroying any unused pipeline object.
static_assert(FVK_MAX_PIPELINE_AGE >= FVK_MAX_COMMAND_BUFFERS);
// Whether the buffer updates always use staging or not. Otherwise its allowed to also use memcpy
// for buffer updates. Default is false
constexpr static const bool FVK_FORCE_STAGING_FOR_BUFFER_UPDATES = false;
#endif
+2 -9
View File
@@ -394,13 +394,7 @@ void VulkanDriver::collectGarbage() {
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
int64_t refreshIntervalNs, uint32_t frameId) {
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
// Check if any command have finished and reset all its used resources. The resources
// wont be destroyed but their reference count will decreased if the command is already
// completed.
//
// This will let us check if any VulkanBuffer is currently in flight or not.
mCommands.gc();
// Do nothing.
if (mAppState.hasExternalSamplers()) {
mExternalImageManager.onBeginFrame();
@@ -1242,8 +1236,7 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor
scheduleDestroy(std::move(p));
}
void VulkanDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh,
BufferObjectStreamDescriptor&& streams) {
void VulkanDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh, BufferObjectStreamDescriptor&& streams) {
// Noop
}
+20 -41
View File
@@ -34,23 +34,16 @@ namespace {
static constexpr uint32_t MAX_EMPTY_STAGES_TO_RETAIN = 1;
constexpr uint32_t STAGE_SIZE = 1048576;
uint32_t alignValue(uint32_t valueToAlign, uint32_t alignment) {
if (alignment == 0) {
return valueToAlign;
}
uint32_t remainder = valueToAlign % alignment;
return remainder == 0 ? valueToAlign : valueToAlign + (alignment - remainder);
}
}// namespace
fvkmemory::resource_ptr<VulkanStage::Segment> VulkanStage::acquireSegment(
fvkmemory::ResourceManager* resManager, uint32_t segmentOffset, uint32_t numBytes) {
auto segment = fvkmemory::resource_ptr<Segment>::construct(resManager, this, numBytes,
segmentOffset, [this](uint32_t offset) { mSegments.erase(offset); });
mSegments.insert({ segmentOffset, segment.get() });
mCurrentOffset = segmentOffset + numBytes;
fvkmemory::ResourceManager* resManager, uint32_t numBytes) {
auto segment = fvkmemory::resource_ptr<Segment>::construct(
resManager, this, numBytes, mCurrentOffset, [this](uint32_t offset) {
mSegments.erase(offset);
});
mSegments.insert({mCurrentOffset, segment.get()});
mCurrentOffset += numBytes;
return segment;
}
@@ -61,8 +54,7 @@ VulkanStagePool::VulkanStagePool(VmaAllocator allocator, fvkmemory::ResourceMana
mCommands(commands),
mDeviceLimits(deviceLimits) {}
fvkmemory::resource_ptr<VulkanStage::Segment> VulkanStagePool::acquireStage(uint32_t numBytes,
uint32_t alignment) {
fvkmemory::resource_ptr<VulkanStage::Segment> VulkanStagePool::acquireStage(uint32_t numBytes) {
// Apply alignment to the byte count to ensure that, when we later flush
// data written by the host, we only flush the atoms that we modified, and
// no adjacent atoms.
@@ -72,35 +64,17 @@ fvkmemory::resource_ptr<VulkanStage::Segment> VulkanStagePool::acquireStage(uint
// equal to the requested size.
auto iter = mStages.lower_bound(numBytes);
// If we have potential buffer candidates, loop through and try to find one
// that works with our alignment. Also, set the offset for the buffer.
// If we have no buffer, create a new one.
VulkanStage* pStage = nullptr;
uint32_t segmentOffset = 0;
while (iter != mStages.end()) {
segmentOffset = alignValue(iter->second->currentOffset(), alignment);
// Check for overflow + if there's space.
if (segmentOffset >= iter->second->currentOffset() &&
iter->second->capacity() - numBytes >= segmentOffset) {
// This buffer has enough space.
pStage = iter->second;
mStages.erase(iter);
break;
}
// This buffer wasn't suitable. Check the next larger one.
++iter;
}
if (!pStage) {
VulkanStage* pStage;
if (iter != mStages.end()) {
pStage = iter->second;
mStages.erase(iter);
} else {
pStage = allocateNewStage(std::max(numBytes, STAGE_SIZE));
}
// Note: this allocation updates `currentOffset` and `segments` within
// the parent stage. When destroyed, it will update `segments`.
fvkmemory::resource_ptr<VulkanStage::Segment> pSegment =
pStage->acquireSegment(mResManager, segmentOffset, numBytes);
fvkmemory::resource_ptr<VulkanStage::Segment> pSegment = pStage->acquireSegment(mResManager, numBytes);
// Update the stage's metadata, and reinsert it with the remaining segment
// capacity.
@@ -112,7 +86,12 @@ fvkmemory::resource_ptr<VulkanStage::Segment> VulkanStagePool::acquireStage(uint
uint32_t VulkanStagePool::alignToNonCoherentAtomSize(uint32_t bytes) {
VkDeviceSize alignment = mDeviceLimits->nonCoherentAtomSize;
return alignValue(bytes, alignment);
if (alignment == 0) {
return bytes;
}
uint32_t remainder = bytes % alignment;
return remainder == 0 ? bytes : bytes + (alignment - remainder);
}
VulkanStage* VulkanStagePool::allocateNewStage(uint32_t capacity) {
@@ -105,11 +105,8 @@ public:
// Marks a region of the block as "in-use", and provides information about
// the allocated region to the caller. Note: this assumes that numBytes
// is aligned to the physical device's nonCoherentAtomSize.
// Takes the offset to use as the segment's start offset. It is assumed
// that this is greater than the segment's current offset, and it is
// undefined behavior if not.
fvkmemory::resource_ptr<Segment> acquireSegment(fvkmemory::ResourceManager* resManager,
uint32_t segmentOffset, uint32_t numBytes);
uint32_t numBytes);
private:
const VmaAllocation mMemory;
@@ -147,10 +144,7 @@ public:
// buffers so that we have less objects around that we have to keep track
// of.
// This function is NOT thread-safe.
// numBytes - the number of bytes required by this segment.
// alignment - the multiple to align the buffer offset to
fvkmemory::resource_ptr<VulkanStage::Segment> acquireStage(uint32_t numBytes,
uint32_t alignment = 0);
fvkmemory::resource_ptr<VulkanStage::Segment> acquireStage(uint32_t numBytes);
// Images have VK_IMAGE_LAYOUT_GENERAL and must not be transitioned to any other layout
VulkanStageImage const* acquireImage(PixelDataFormat format, PixelDataType type,
@@ -522,10 +522,8 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
// Note: the following stageSegment must be stored within the command buffer
// before going out of scope, to ensure proper bookkeeping within the
// staging buffer pool.
uint8_t alignment =
fvkutils::getTexelBlockSize(fvkutils::getVkFormat(hostData->format, hostData->type));
fvkmemory::resource_ptr<VulkanStage::Segment> stageSegment =
mState->mStagePool.acquireStage(writeSize, alignment);
mState->mStagePool.acquireStage(writeSize);
assert_invariant(stageSegment->memory());
adjustedMemcpy(stageSegment->mapping(), *hostData, width, height, depth);
vmaFlushAllocation(mState->mAllocator, stageSegment->memory(), stageSegment->offset(),
@@ -29,8 +29,6 @@ using namespace bluevk;
namespace filament::backend {
using namespace fvkutils;
VulkanYcbcrConversionCache::VulkanYcbcrConversionCache(VkDevice device)
: mDevice(device) {}
@@ -17,8 +17,6 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
#define TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
#include "utils/Definitions.h"
#include <backend/DriverEnums.h>
#include <utils/Hash.h>
@@ -32,7 +30,7 @@ namespace filament::backend {
class VulkanYcbcrConversionCache {
public:
struct Params {
fvkutils::SamplerYcbcrConversion conversion = {}; // 4
SamplerYcbcrConversion conversion = {}; // 4
VkFormat format; // 4
uint64_t externalFormat = 0; // 8
};
@@ -47,15 +45,16 @@ private:
struct ConversionEqualTo {
bool operator()(Params lhs, Params rhs) const noexcept {
fvkutils::SamplerYcbcrConversion::EqualTo equal;
SamplerYcbcrConversion::EqualTo equal;
return equal(lhs.conversion, rhs.conversion) &&
lhs.externalFormat == rhs.externalFormat && lhs.format == rhs.format;
lhs.externalFormat == rhs.externalFormat &&
lhs.format == rhs.format;
}
};
using ConversionHashFn = utils::hash::MurmurHashFn<Params>;
tsl::robin_map<Params, VkSamplerYcbcrConversion, ConversionHashFn, ConversionEqualTo> mCache;
};
} // namespace filament::backend
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
@@ -71,8 +71,6 @@ struct Resource {
restype(ResourceType::UNDEFINED_TYPE),
mHandleConsideredDestroyed(false) {}
uint32_t getCount() const { return mCount; }
private:
inline void inc() noexcept {
mCount++;
@@ -832,9 +832,6 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
context.mPhysicalDeviceFeatures.features.shaderClipDistance = VK_FALSE;
}
// TODO: Add support of VK_KHR_global_priority with `driverConfig.gpuContextPriority`
// in VulkanPlatform::createDriver.
ExtensionSet deviceExts;
// If using a shared context, we do not assume any extensions.
if (!mImpl->mSharedContext) {
@@ -926,7 +923,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
// Keep a copy of context for swapchains.
mImpl->mContext = context;
return VulkanDriver::create(this, mImpl->mContext, driverConfig);
return VulkanDriver::create(this, context, driverConfig);
}
// This needs to be explictly written for
@@ -233,212 +233,6 @@ VkFormat getVkFormat(TextureFormat format) {
}
}
// As per VUID-vkCmdCopyBufferToImage-dstImage-07975 and
// VUID-vkCmdCopyBufferToImage-dstImage-07978, these provide the texel block
// sizes for each format (as confirmed using the table at
// https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#formats-compatibility-classes).
// We have not listed values for multi-plane formats, as we do not support them
// unless they're externally provided.
uint8_t getTexelBlockSize(VkFormat format) {
switch (format) {
// 8-bit formats.
case VK_FORMAT_R8_UNORM:
case VK_FORMAT_R8_SNORM:
case VK_FORMAT_R8_USCALED:
case VK_FORMAT_R8_SSCALED:
case VK_FORMAT_R8_UINT:
case VK_FORMAT_R8_SINT:
case VK_FORMAT_R8_SRGB:
return 1;
// 16-bit formats.
case VK_FORMAT_R16_UNORM:
case VK_FORMAT_R16_SNORM:
case VK_FORMAT_R16_USCALED:
case VK_FORMAT_R16_SSCALED:
case VK_FORMAT_R16_UINT:
case VK_FORMAT_R16_SINT:
case VK_FORMAT_R16_SFLOAT:
case VK_FORMAT_R8G8_UNORM:
case VK_FORMAT_R8G8_SNORM:
case VK_FORMAT_R8G8_USCALED:
case VK_FORMAT_R8G8_SSCALED:
case VK_FORMAT_R8G8_UINT:
case VK_FORMAT_R8G8_SINT:
case VK_FORMAT_R8G8_SRGB:
case VK_FORMAT_R5G6B5_UNORM_PACK16:
case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
return 2;
// 24-bit formats.
case VK_FORMAT_R8G8B8_UNORM:
case VK_FORMAT_R8G8B8_SNORM:
case VK_FORMAT_R8G8B8_USCALED:
case VK_FORMAT_R8G8B8_SSCALED:
case VK_FORMAT_R8G8B8_UINT:
case VK_FORMAT_R8G8B8_SINT:
case VK_FORMAT_R8G8B8_SRGB:
case VK_FORMAT_B8G8R8_UNORM:
case VK_FORMAT_B8G8R8_SNORM:
case VK_FORMAT_B8G8R8_USCALED:
case VK_FORMAT_B8G8R8_SSCALED:
case VK_FORMAT_B8G8R8_UINT:
case VK_FORMAT_B8G8R8_SINT:
case VK_FORMAT_B8G8R8_SRGB:
return 3;
// 32-bit formats.
case VK_FORMAT_R32_UINT:
case VK_FORMAT_R32_SINT:
case VK_FORMAT_R32_SFLOAT:
case VK_FORMAT_R16G16_UNORM:
case VK_FORMAT_R16G16_SNORM:
case VK_FORMAT_R16G16_USCALED:
case VK_FORMAT_R16G16_SSCALED:
case VK_FORMAT_R16G16_UINT:
case VK_FORMAT_R16G16_SINT:
case VK_FORMAT_R16G16_SFLOAT:
case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_R8G8B8A8_SNORM:
case VK_FORMAT_R8G8B8A8_USCALED:
case VK_FORMAT_R8G8B8A8_SSCALED:
case VK_FORMAT_R8G8B8A8_UINT:
case VK_FORMAT_R8G8B8A8_SINT:
case VK_FORMAT_R8G8B8A8_SRGB:
case VK_FORMAT_B8G8R8A8_UNORM:
case VK_FORMAT_B8G8R8A8_SNORM:
case VK_FORMAT_B8G8R8A8_USCALED:
case VK_FORMAT_B8G8R8A8_SSCALED:
case VK_FORMAT_B8G8R8A8_UINT:
case VK_FORMAT_B8G8R8A8_SINT:
case VK_FORMAT_B8G8R8A8_SRGB:
case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
case VK_FORMAT_A8B8G8R8_USCALED_PACK32:
case VK_FORMAT_A8B8G8R8_SSCALED_PACK32:
case VK_FORMAT_A8B8G8R8_UINT_PACK32:
case VK_FORMAT_A8B8G8R8_SINT_PACK32:
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
// Depth and stencil formats.
case VK_FORMAT_S8_UINT:
case VK_FORMAT_D16_UNORM:
case VK_FORMAT_X8_D24_UNORM_PACK32:
case VK_FORMAT_D32_SFLOAT:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
return 4;
// 48-bit formats.
case VK_FORMAT_R16G16B16_UNORM:
case VK_FORMAT_R16G16B16_SNORM:
case VK_FORMAT_R16G16B16_USCALED:
case VK_FORMAT_R16G16B16_SSCALED:
case VK_FORMAT_R16G16B16_UINT:
case VK_FORMAT_R16G16B16_SINT:
case VK_FORMAT_R16G16B16_SFLOAT:
return 6;
// 64-bit formats.
case VK_FORMAT_R32G32_UINT:
case VK_FORMAT_R32G32_SINT:
case VK_FORMAT_R32G32_SFLOAT:
case VK_FORMAT_R16G16B16A16_UNORM:
case VK_FORMAT_R16G16B16A16_SNORM:
case VK_FORMAT_R16G16B16A16_USCALED:
case VK_FORMAT_R16G16B16A16_SSCALED:
case VK_FORMAT_R16G16B16A16_UINT:
case VK_FORMAT_R16G16B16A16_SINT:
case VK_FORMAT_R16G16B16A16_SFLOAT:
// Compressed formats.
case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
case VK_FORMAT_BC4_UNORM_BLOCK:
case VK_FORMAT_BC4_SNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
return 8;
// 96-bit formats.
case VK_FORMAT_R32G32B32_UINT:
case VK_FORMAT_R32G32B32_SINT:
case VK_FORMAT_R32G32B32_SFLOAT:
return 12;
// 128-bit formats.
case VK_FORMAT_R32G32B32A32_UINT:
case VK_FORMAT_R32G32B32A32_SINT:
case VK_FORMAT_R32G32B32A32_SFLOAT:
// Compressed formats.
case VK_FORMAT_BC2_UNORM_BLOCK:
case VK_FORMAT_BC2_SRGB_BLOCK:
case VK_FORMAT_BC3_UNORM_BLOCK:
case VK_FORMAT_BC3_SRGB_BLOCK:
case VK_FORMAT_BC5_UNORM_BLOCK:
case VK_FORMAT_BC5_SNORM_BLOCK:
case VK_FORMAT_BC6H_SFLOAT_BLOCK:
case VK_FORMAT_BC6H_UFLOAT_BLOCK:
case VK_FORMAT_BC7_UNORM_BLOCK:
case VK_FORMAT_BC7_SRGB_BLOCK:
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
return 16;
case VK_FORMAT_UNDEFINED:
// In cases where we've explicitly already determined that the
// format is not supported, let the rest of the system handle
// that explicitly. Return '1' for no alignment.
return 1;
default:
// This is the case where there's a format that we added support
// for in getVkFormat that we have not added support for in this
// function, and should be treated as an error.
assert_invariant(false && "Unknown data type, conversion is not supported.");
return 0;
}
}
VkFormat getVkFormat(PixelDataFormat format, PixelDataType type) {
if (type == PixelDataType::USHORT_565) return VK_FORMAT_R5G6B5_UNORM_PACK16;
if (type == PixelDataType::UINT_2_10_10_10_REV) return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
@@ -17,8 +17,6 @@
#ifndef TNT_FILAMENT_BACKEND_VULKAN_UTILS_CONVERSION_H
#define TNT_FILAMENT_BACKEND_VULKAN_UTILS_CONVERSION_H
#include "Definitions.h"
#include <backend/DriverEnums.h>
#include <private/backend/BackendUtils.h> // for getFormatSize()
@@ -49,9 +47,6 @@ VkFormat getVkFormatLinear(VkFormat format);
// than a driver-level Texture format, and can account for a specified byte alignment.
uint32_t getBytesPerPixel(TextureFormat format);
// Required for aligning textures in staging buffers.
uint8_t getTexelBlockSize(VkFormat format);
VkCompareOp getCompareOp(SamplerCompareFunc func);
VkBlendFactor getBlendFactor(BlendFunction mode);
VkCullModeFlags getCullMode(CullingMode mode);
@@ -17,8 +17,6 @@
#ifndef TNT_FILAMENT_BACKEND_VULKAN_UTILS_DEFINITIONS_H
#define TNT_FILAMENT_BACKEND_VULKAN_UTILS_DEFINITIONS_H
#include <backend/DriverEnums.h>
#include <utils/bitset.h>
#include <utils/FixedCapacityVector.h>
@@ -381,81 +379,6 @@ static constexpr uint8_t getFragmentStageShift() noexcept {
// We have at most 4 descriptor sets. This is to indicate which ones are active.
using DescriptorSetMask = utils::bitset8;
//! this API is copied from (and only applies to) the Vulkan spec.
//! These specify YUV to RGB conversions.
enum class SamplerYcbcrModelConversion : uint8_t {
RGB_IDENTITY = 0,
YCBCR_IDENTITY = 1,
YCBCR_709 = 2,
YCBCR_601 = 3,
YCBCR_2020 = 4,
};
enum class SamplerYcbcrRange : uint8_t {
ITU_FULL = 0,
ITU_NARROW = 1,
};
enum class ChromaLocation : uint8_t {
COSITED_EVEN = 0,
MIDPOINT = 1,
};
//! Sampler parameters
struct SamplerYcbcrConversion { // NOLINT
SamplerYcbcrModelConversion ycbcrModel : 4;
TextureSwizzle r : 4;
TextureSwizzle g : 4;
TextureSwizzle b : 4;
TextureSwizzle a : 4;
SamplerYcbcrRange ycbcrRange : 1;
ChromaLocation xChromaOffset : 1;
ChromaLocation yChromaOffset : 1;
SamplerMagFilter chromaFilter : 1;
uint8_t padding;
struct Hasher {
size_t operator()(const SamplerYcbcrConversion p) const noexcept {
// we don't use std::hash<> here, so we don't have to include <functional>
return *reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&p));
}
};
struct EqualTo {
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
assert_invariant(lhs.padding == 0);
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
return *pLhs == *pRhs;
}
};
struct LessThan {
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
assert_invariant(lhs.padding == 0);
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
return *pLhs < *pRhs;
}
};
private:
friend bool operator==(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) noexcept {
return SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
}
friend bool operator!=(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) noexcept {
return !SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
}
friend bool operator<(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) noexcept {
return SamplerYcbcrConversion::LessThan{}(lhs, rhs);
}
};
static_assert(sizeof(SamplerYcbcrConversion) == 4);
static_assert(sizeof(SamplerYcbcrConversion) <= sizeof(uint64_t),
"SamplerYcbcrConversion must be no more than 64 bits");
} // namespace filament::backend::fvkutils
#endif // TNT_FILAMENT_BACKEND_VULKAN_UTILS_DEFINITIONS_H
@@ -15,6 +15,7 @@
*/
#include "WebGPUDescriptorSetLayout.h"
#include "WebGPUConstants.h"
#include <backend/DriverEnums.h>
+73 -166
View File
@@ -18,7 +18,6 @@
#include "WebGPUBufferObject.h"
#include "WebGPUDescriptorSet.h"
#include "WebGPUDescriptorSetLayout.h"
#include "WebGPUFence.h"
#include "WebGPUIndexBuffer.h"
#include "WebGPUPipelineCreation.h"
#include "WebGPUProgram.h"
@@ -33,6 +32,7 @@
#include "CommandStreamDispatcher.h"
#include "DriverBase.h"
#include "private/backend/Dispatcher.h"
#include "webgpu/WebGPUConstants.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/TargetBufferInfo.h>
@@ -54,8 +54,6 @@
#include <sstream>
#include <utility>
using namespace std::chrono_literals;
namespace filament::backend {
Driver* WebGPUDriver::create(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept {
@@ -104,7 +102,6 @@ void WebGPUDriver::terminate() {
void WebGPUDriver::tick(int) {
mDevice.Tick();
mAdapter.GetInstance().ProcessEvents();
}
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
@@ -266,9 +263,7 @@ Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
}
Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
// The handle must be constructed here, as a synchronous call to get the status
// might happen before createFenceR is executed.
return allocAndConstructHandle<WebGPUFence, HwFence>();
return Handle<HwFence>((Handle<HwFence>::HandleId) mNextFakeHandle++);
}
Handle<HwTimerQuery> WebGPUDriver::createTimerQueryS() noexcept {
@@ -341,6 +336,9 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
mDevice, flags);
assert_invariant(mSwapChain);
FWGPU_LOGW << "WebGPU support is highly experimental, in development, and tested for only a "
"small set of simple samples (e.g. hellotriangle and texturedquad), thus issues "
"are likely to be encountered at this stage.";
#if !FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) && !defined(NDEBUG)
char printSystemHex[16];
snprintf(printSystemHex, sizeof(printSystemHex), "%#x", FWGPU_PRINT_SYSTEM);
@@ -452,18 +450,15 @@ void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> renderTarge
}
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> renderTargetHandle,
const TargetBufferFlags targetFlags, const uint32_t width, const uint32_t height,
const TargetBufferFlags targets, 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) {
constructHandle<WebGPURenderTarget>(renderTargetHandle, width, height, samples, layerCount,
color, depth, stencil, targetFlags);
color, depth, stencil);
}
void WebGPUDriver::createFenceR(Handle<HwFence> fenceHandle, const int /* dummy */) {
// the handle was already constructed in createFenceS
const auto fence = handleCast<WebGPUFence>(fenceHandle);
assert_invariant(mQueue);
fence->addMarkerToQueueState(mQueue);
void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {
//todo
}
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
@@ -516,18 +511,13 @@ void WebGPUDriver::updateStreams(CommandStream* driver) {
//todo
}
void WebGPUDriver::destroyFence(Handle<HwFence> fenceHandle) {
if (fenceHandle) {
destructHandle<WebGPUFence>(fenceHandle);
}
void WebGPUDriver::destroyFence(Handle<HwFence> fh) {
//todo
}
FenceStatus WebGPUDriver::getFenceStatus(Handle<HwFence> fenceHandle) {
const auto fence = handleCast<WebGPUFence>(fenceHandle);
if (!fence) {
return FenceStatus::ERROR;
}
return fence->getStatus();
FenceStatus WebGPUDriver::getFenceStatus(Handle<HwFence> fh) {
//todo
return FenceStatus::CONDITION_SATISFIED;
}
// We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that
@@ -617,28 +607,15 @@ uint8_t WebGPUDriver::getMaxDrawBuffers() {
}
size_t WebGPUDriver::getMaxUniformBufferSize() {
return mDeviceLimits.maxUniformBufferBindingSize;
return 16384u;
}
size_t WebGPUDriver::getMaxTextureSize(const SamplerType target) {
size_t result = 2048u;
switch (target) {
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_2D_ARRAY:
case SamplerType::SAMPLER_EXTERNAL:
case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
result = mDeviceLimits.maxTextureDimension2D;
break;
case SamplerType::SAMPLER_3D:
result = mDeviceLimits.maxTextureDimension3D;
break;
}
return result;
return 2048u;
}
size_t WebGPUDriver::getMaxArrayTextureLayers() {
return mDeviceLimits.maxTextureArrayLayers;
return 256u;
}
void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> indexBufferHandle,
@@ -823,9 +800,10 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
std::array<wgpu::TextureView, MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT> customColorViews{};
uint32_t customColorViewCount = 0;
wgpu::TextureView customDepthStencilView = nullptr;
wgpu::TextureFormat customDepthStencilFormat = wgpu::TextureFormat::Undefined;
wgpu::TextureView customDepthView = nullptr;
wgpu::TextureFormat customDepthFormat = wgpu::TextureFormat::Undefined;
wgpu::TextureFormat customStencilFormat = wgpu::TextureFormat::Undefined;
wgpu::TextureView customStencilView = nullptr;
mCurrentRenderTarget = renderTarget;
if (renderTarget->isDefaultRenderTarget()) {
@@ -833,24 +811,6 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
defaultColorView = mTextureView;
defaultDepthStencilView = mSwapChain->getDepthTextureView();
defaultDepthStencilFormat = mSwapChain->getDepthFormat();
if (any(renderTarget->getTargetFlags() & TargetBufferFlags::STENCIL) &&
!(defaultDepthStencilFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
defaultDepthStencilFormat == wgpu::TextureFormat::Depth32FloatStencil8 ||
defaultDepthStencilFormat == wgpu::TextureFormat::Stencil8)) {
FILAMENT_CHECK_POSTCONDITION(false) << "Default render target requested stencil, but swap chain's depth format "
<< (uint32_t)defaultDepthStencilFormat << " does not have a stencil aspect.";
}
if (any(renderTarget->getTargetFlags() & TargetBufferFlags::DEPTH) &&
!(defaultDepthStencilFormat == wgpu::TextureFormat::Depth16Unorm ||
defaultDepthStencilFormat == wgpu::TextureFormat::Depth32Float ||
defaultDepthStencilFormat == wgpu::TextureFormat::Depth24Plus ||
defaultDepthStencilFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
defaultDepthStencilFormat == wgpu::TextureFormat::Depth32FloatStencil8)) {
FILAMENT_CHECK_POSTCONDITION(false) << "Default render target requested depth, but swap chain's depth format "
<< (uint32_t)defaultDepthStencilFormat << " does not have a depth aspect.";
}
} else {
// Resolve views for custom render target
const auto& colorInfos = renderTarget->getColorAttachmentInfos();
@@ -870,63 +830,36 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
}
}
}
const auto& depthInfo = renderTarget->getDepthAttachmentInfo();
const auto& stencilInfo = renderTarget->getStencilAttachmentInfo();
Handle<HwTexture> depthStencilSourceHandle = {};
uint8_t depthStencilMipLevel = 0;
uint32_t depthStencilArrayLayer = 0;
if (depthInfo.handle && stencilInfo.handle) {
auto const depthTexture{handleCast<WebGPUTexture>(depthInfo.handle)->getTexture()};
auto const stencilTexture{handleCast<WebGPUTexture>(stencilInfo.handle)->getTexture()};
FILAMENT_CHECK_POSTCONDITION(depthTexture.Get() == stencilTexture.Get())
<< "Filament must reference the same resource if both depth and stencil "
"textures are present";
}
if (depthInfo.handle) {
depthStencilSourceHandle = depthInfo.handle;
depthStencilMipLevel = depthInfo.level;
depthStencilArrayLayer = depthInfo.layer;
} else if (stencilInfo.handle) {
depthStencilSourceHandle = stencilInfo.handle;
depthStencilMipLevel = stencilInfo.level;
depthStencilArrayLayer = stencilInfo.layer;
FILAMENT_CHECK_POSTCONDITION(depthInfo.layer < renderTarget->getLayerCount())
<< "Depth attachment requests layer " << depthInfo.layer
<< "but render target has only " << renderTarget->getLayerCount() << ".";
auto depthTexture = handleCast<WebGPUTexture>(depthInfo.handle);
if (depthTexture) {
const uint8_t depthMipLevel = depthInfo.level;
const uint32_t depthArrayLayer = depthInfo.layer;
customDepthView =
depthTexture->getOrMakeTextureView(depthMipLevel, depthArrayLayer);
customDepthFormat = depthTexture->getViewFormat();
}
}
if (depthStencilSourceHandle) {
auto dsTexture = handleCast<WebGPUTexture>(depthStencilSourceHandle);
if (dsTexture) {
customDepthStencilView = dsTexture->getOrMakeTextureView(depthStencilMipLevel, depthStencilArrayLayer);
customDepthStencilFormat = dsTexture->getViewFormat();
if (any(renderTarget->getTargetFlags() & TargetBufferFlags::STENCIL) &&
!(customDepthStencilFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
customDepthStencilFormat ==
wgpu::TextureFormat::Depth32FloatStencil8 ||
customDepthStencilFormat ==
wgpu::TextureFormat::Stencil8)) {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Custom render target requested stencil, but the provided texture"
"format number"
<< (uint32_t) customDepthStencilFormat
<< " does not have a stencil aspect.";
}
if (any(renderTarget->getTargetFlags() & TargetBufferFlags::DEPTH) &&
!(customDepthStencilFormat == wgpu::TextureFormat::Depth16Unorm ||
customDepthStencilFormat == wgpu::TextureFormat::Depth32Float ||
customDepthStencilFormat ==
wgpu::TextureFormat::Depth24Plus ||
customDepthStencilFormat ==
wgpu::TextureFormat::Depth24PlusStencil8 ||
customDepthStencilFormat ==
wgpu::TextureFormat::Depth32FloatStencil8)) {
FILAMENT_CHECK_POSTCONDITION(false) << "Custom render target requested depth, "
"but the provided texture format number"
<< (uint32_t) customDepthStencilFormat
<< " does not have a depth aspect.";
}
const auto& stencilInfo = renderTarget->getStencilAttachmentInfo();
if (stencilInfo.handle) {
// If depth and stencil use the same texture handle, this will re-cast but that's fine.
auto stencilTexture = handleCast<WebGPUTexture>(stencilInfo.handle);
if (stencilTexture) {
FILAMENT_CHECK_POSTCONDITION(stencilInfo.layer < renderTarget->getLayerCount())
<< "Stencil attachment requests layer " << stencilInfo.layer
<< " but render target has only " << renderTarget->getLayerCount()
<< " layers.";
const uint8_t stencilMipLevel = stencilInfo.level;
const uint32_t stencilArrayLayer = stencilInfo.layer;
customStencilView =
stencilTexture->getOrMakeTextureView(stencilMipLevel, stencilArrayLayer);
customStencilFormat = stencilTexture->getViewFormat();
}
}
}
@@ -935,9 +868,13 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
params,
defaultColorView,
defaultDepthStencilView,
defaultDepthStencilFormat,
customColorViews.data(),
customColorViewCount,
customDepthStencilView);
customDepthView,
customStencilView,
customDepthFormat,
customStencilFormat);
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
@@ -972,25 +909,9 @@ void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
wgpu::Extent2D surfaceSize = mPlatform.getSurfaceExtent(mNativeWindow);
mTextureView = mSwapChain->getCurrentSurfaceTextureView(surfaceSize);
assert_invariant(mTextureView);
assert_invariant(mDefaultRenderTarget);
wgpu::TextureFormat depthFormat = mSwapChain->getDepthFormat();
TargetBufferFlags newTargetFlags = filament::backend::TargetBufferFlags::NONE;
//Assuming Color and Depth are always present.
newTargetFlags |= filament::backend::TargetBufferFlags::COLOR;
if (depthFormat != wgpu::TextureFormat::Undefined) {
newTargetFlags |= filament::backend::TargetBufferFlags::DEPTH;
if (depthFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
depthFormat == wgpu::TextureFormat::Depth32FloatStencil8) {
newTargetFlags |= filament::backend::TargetBufferFlags::STENCIL;
}
}
mDefaultRenderTarget->setTargetFlags(newTargetFlags);
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = { .label = "frame_command_encoder" };
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
.label = "command_encoder"
};
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
assert_invariant(mCommandEncoder);
}
@@ -1003,22 +924,6 @@ void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
assert_invariant(mCommandBuffer);
mCommandEncoder = nullptr;
mQueue.Submit(1, &mCommandBuffer);
static bool firstRender = true;
// For the first frame rendered, we need to make sure the work is done before presenting or we
// get a purple flash
if (firstRender) {
auto f = mQueue.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
[=](wgpu::QueueWorkDoneStatus) {});
const wgpu::Instance instance = mAdapter.GetInstance();
auto wStatus = instance.WaitAny(f,
std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count());
if (wStatus != wgpu::WaitStatus::Success) {
FWGPU_LOGW << "Waiting for first frame work to finish resulted in an error"
<< static_cast<uint32_t>(wStatus);
}
firstRender = false;
}
mCommandBuffer = nullptr;
mTextureView = nullptr;
assert_invariant(mSwapChain);
@@ -1125,15 +1030,14 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
assert_invariant(vertexBufferInfo);
std::vector<wgpu::TextureFormat> pipelineColorFormats;
wgpu::TextureFormat pipelineDepthStencilFormat = wgpu::TextureFormat::Undefined;
wgpu::TextureFormat pipelineDepthFormat = wgpu::TextureFormat::Undefined;
uint8_t pipelineSamples = 1;
bool const requestedDepth = any(mCurrentRenderTarget->getTargetFlags() & TargetBufferFlags::DEPTH);
bool const requestedStencil = any(mCurrentRenderTarget->getTargetFlags() & TargetBufferFlags::STENCIL);
pipelineSamples = mCurrentRenderTarget->getSamples();
if (mCurrentRenderTarget->isDefaultRenderTarget()) {
pipelineColorFormats.push_back(mSwapChain->getColorFormat());
pipelineDepthStencilFormat = mSwapChain->getDepthFormat();
pipelineDepthFormat = mSwapChain->getDepthFormat();
pipelineSamples =
mCurrentRenderTarget->getSamples();// Default RT should have samples (usually 1)
} else {
const auto& mrtColorAttachments = mCurrentRenderTarget->getColorAttachmentInfos();
for (size_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; ++i) {
@@ -1147,19 +1051,21 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
const auto& depthInfo = mCurrentRenderTarget->getDepthAttachmentInfo();
const auto& stencilInfo = mCurrentRenderTarget->getStencilAttachmentInfo();
Handle<HwTexture> depthStencilHandle = {};
if (depthInfo.handle) {
depthStencilHandle = depthInfo.handle;
} else if (stencilInfo.handle) {
depthStencilHandle = stencilInfo.handle;
}
if (depthStencilHandle) {
const auto dsTexture = handleCast<WebGPUTexture>(depthStencilHandle);
if (dsTexture) {
pipelineDepthStencilFormat = dsTexture->getTexture().GetFormat();
FILAMENT_CHECK_POSTCONDITION(!stencilInfo.handle)
<< "depth and stencil attachments cannot both be provided for WebGPU";
const auto depthTexture = handleCast<WebGPUTexture>(depthInfo.handle);
if (depthTexture) pipelineDepthFormat = depthTexture->getTexture().GetFormat();
} else {
if (stencilInfo.handle) {
const auto stencilTexture = handleCast<WebGPUTexture>(stencilInfo.handle);
// Assuming combined depth/stencil format if only stencil is present
if (stencilTexture) {
pipelineDepthFormat = stencilTexture->getTexture().GetFormat();
}
}
}
pipelineSamples = mCurrentRenderTarget->getSamples();
}
// TODO: We expected this to be a sane check, however it complains when running shadowtest.
@@ -1171,7 +1077,7 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
wgpu::RenderPipeline pipeline = createWebGPURenderPipeline(mDevice, *program, *vertexBufferInfo,
layout, pipelineState.rasterState, pipelineState.stencilState,
pipelineState.polygonOffset, pipelineState.primitiveType, pipelineColorFormats,
pipelineDepthStencilFormat, pipelineSamples, requestedDepth, requestedStencil);
pipelineDepthFormat, pipelineSamples);
assert_invariant(pipeline);
mPipelineMap[hash] = pipeline;
mRenderPassEncoder.SetPipeline(pipeline);
@@ -1261,6 +1167,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) {
auto bindGroup = handleCast<WebGPUDescriptorSet>(descriptorSetHandle);
auto texture = handleCast<WebGPUTexture>(textureHandle);
@@ -121,11 +121,6 @@ private:
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
}
template<typename D, typename B, typename... ARGS>
Handle<B> allocAndConstructHandle(ARGS&&... args) {
return mHandleAllocator.allocateAndConstruct<D>(std::forward<ARGS>(args)...);
}
template<typename D, typename B>
D* handleCast(Handle<B> handle) noexcept {
return mHandleAllocator.handle_cast<D*>(handle);
@@ -1,50 +0,0 @@
/*
* 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 "WebGPUFence.h"
#include <backend/DriverEnums.h>
#include <webgpu/webgpu_cpp.h>
#include <atomic>
namespace filament::backend {
FenceStatus WebGPUFence::getStatus() { return mStatus.load(); }
void WebGPUFence::addMarkerToQueueState(wgpu::Queue const& queue) {
queue.OnSubmittedWorkDone(
wgpu::CallbackMode::AllowSpontaneous,
[](const wgpu::QueueWorkDoneStatus status,
std::atomic<FenceStatus>* const fenceStatus) {
if (!fenceStatus) {
return;
}
switch (status) {
case wgpu::QueueWorkDoneStatus::Success:
fenceStatus->store(FenceStatus::CONDITION_SATISFIED);
break;
case wgpu::QueueWorkDoneStatus::CallbackCancelled:
case wgpu::QueueWorkDoneStatus::Error:
fenceStatus->store(FenceStatus::ERROR);
break;
}
},
&mStatus);
}
} // namespace filament::backend
-43
View File
@@ -1,43 +0,0 @@
/*
* 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_BACKEND_WEBGPUFENCE_H
#define TNT_FILAMENT_BACKEND_WEBGPUFENCE_H
#include "DriverBase.h"
#include <backend/DriverEnums.h>
#include <atomic>
namespace wgpu {
class Queue;
} // namespace wgpu
namespace filament::backend {
class WebGPUFence final : public HwFence {
public:
[[nodiscard]] FenceStatus getStatus();
void addMarkerToQueueState(wgpu::Queue const&);
private:
std::atomic<FenceStatus> mStatus{ FenceStatus::TIMEOUT_EXPIRED };
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_WEBGPUFENCE_H
@@ -169,52 +169,39 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
wgpu::PipelineLayout const& layout, RasterState const& rasterState,
StencilState const& stencilState, PolygonOffset const& polygonOffset,
const PrimitiveType primitiveType, std::vector<wgpu::TextureFormat> const& colorFormats,
wgpu::TextureFormat const& depthStencilFormat, const uint8_t samplesCount, const bool requestedDepth, const bool requestedStencil) {
const wgpu::TextureFormat depthFormat, const uint8_t samplesCount) {
assert_invariant(program.vertexShaderModule);
wgpu::DepthStencilState depthStencilState{};
const bool depthOrStencilRequested = (requestedDepth || requestedStencil);
if (depthFormat != wgpu::TextureFormat::Undefined) {
depthStencilState.format = depthFormat;
depthStencilState.depthWriteEnabled = rasterState.depthWrite;
depthStencilState.depthCompare = toWebGPU(rasterState.depthFunc);
depthStencilState.stencilFront = {
.compare = toWebGPU(stencilState.front.stencilFunc),
.failOp = toWebGPU(stencilState.front.stencilOpStencilFail),
.depthFailOp = toWebGPU(stencilState.front.stencilOpDepthFail),
.passOp = toWebGPU(stencilState.front.stencilOpDepthStencilPass),
};
depthStencilState.stencilBack = {
.compare = toWebGPU(stencilState.back.stencilFunc),
.failOp = toWebGPU(stencilState.back.stencilOpStencilFail),
.depthFailOp = toWebGPU(stencilState.back.stencilOpDepthFail),
.passOp = toWebGPU(stencilState.back.stencilOpDepthStencilPass),
};
depthStencilState.stencilReadMask =
stencilState.front.readMask; // Use front face's comparison mask for read mask
depthStencilState.stencilWriteMask = stencilState.stencilWrite ? 0xFFFFFFFF : 0u;
depthStencilState.depthBias = static_cast<int32_t>(polygonOffset.constant);
depthStencilState.depthBiasSlopeScale = polygonOffset.slope;
depthStencilState.depthBiasClamp = 0.0f;
if (depthOrStencilRequested) {
FILAMENT_CHECK_PRECONDITION(depthStencilFormat != wgpu::TextureFormat::Undefined)
<< "Depth or Stencil requested for pipeline, but depthStencilFormat is "
"wgpu::TextureFormat::Undefined.";
depthStencilState.format = depthStencilFormat;
if (requestedDepth) {
depthStencilState.depthWriteEnabled = rasterState.depthWrite;
depthStencilState.depthCompare = toWebGPU(rasterState.depthFunc);
depthStencilState.depthBias = static_cast<int32_t>(polygonOffset.constant);
depthStencilState.depthBiasSlopeScale = polygonOffset.slope;
depthStencilState.depthBiasClamp = 0.0f;
} else {
depthStencilState.depthWriteEnabled = false;
depthStencilState.depthCompare = wgpu::CompareFunction::Undefined;
depthStencilState.depthBias = 0;
depthStencilState.depthBiasSlopeScale = 0.0f;
depthStencilState.depthBiasClamp = 0.0f;
}
if (hasStencilAspect(depthStencilFormat) && requestedStencil) {
depthStencilState.stencilFront = {
.compare = toWebGPU(stencilState.front.stencilFunc),
.failOp = toWebGPU(stencilState.front.stencilOpStencilFail),
.depthFailOp = toWebGPU(stencilState.front.stencilOpDepthFail),
.passOp = toWebGPU(stencilState.front.stencilOpDepthStencilPass),
};
depthStencilState.stencilBack = {
.compare = toWebGPU(stencilState.back.stencilFunc),
.failOp = toWebGPU(stencilState.back.stencilOpStencilFail),
.depthFailOp = toWebGPU(stencilState.back.stencilOpDepthFail),
.passOp = toWebGPU(stencilState.back.stencilOpDepthStencilPass),
};
depthStencilState.stencilReadMask = stencilState.front.readMask;
depthStencilState.stencilWriteMask = stencilState.stencilWrite ? stencilState.front.writeMask : 0u;
} else {
depthStencilState.stencilFront.compare = wgpu::CompareFunction::Undefined;
if (!hasStencilAspect(depthFormat)) {
depthStencilState.stencilFront.compare = wgpu::CompareFunction::Always;
depthStencilState.stencilFront.failOp = wgpu::StencilOperation::Keep;
depthStencilState.stencilFront.depthFailOp = wgpu::StencilOperation::Keep;
depthStencilState.stencilFront.passOp = wgpu::StencilOperation::Keep;
depthStencilState.stencilBack = depthStencilState.stencilFront;
depthStencilState.stencilBack =
depthStencilState.stencilFront; // Keep back and front consistent
depthStencilState.stencilReadMask = 0;
depthStencilState.stencilWriteMask = 0;
}
@@ -258,11 +245,11 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
.unclippedDepth = !rasterState.depthClamp &&
device.HasFeature(wgpu::FeatureName::DepthClipControl)
},
.depthStencil = depthOrStencilRequested ? &depthStencilState: nullptr,
.depthStencil = depthFormat != wgpu::TextureFormat::Undefined ? &depthStencilState: nullptr,
.multisample = {
.count = samplesCount,
.mask = 0xFFFFFFFF,
.alphaToCoverageEnabled = (samplesCount > 1) && rasterState.alphaToCoverage
.alphaToCoverageEnabled = rasterState.alphaToCoverage
},
.fragment = nullptr // will add below if fragment module is included
};
@@ -40,8 +40,8 @@ class WebGPUProgram;
[[nodiscard]] wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const&,
WebGPUProgram const&, WebGPUVertexBufferInfo const&, wgpu::PipelineLayout const&,
RasterState const&, StencilState const&, PolygonOffset const&, PrimitiveType primitiveType,
std::vector<wgpu::TextureFormat> const& colorFormats, wgpu::TextureFormat const& depthStencilFormat,
uint8_t samplesCount, bool requestedDepth, bool requestedStencil);
std::vector<wgpu::TextureFormat> const& colorFormats, wgpu::TextureFormat depthFormat,
uint8_t samplesCount);
}// namespace filament::backend
@@ -32,27 +32,24 @@ namespace filament::backend {
WebGPURenderTarget::WebGPURenderTarget(const uint32_t width, const uint32_t height,
const uint8_t samples, const uint8_t layerCount, MRT const& colorAttachmentsMRT,
Attachment const& depthAttachmentInfo, Attachment const& stencilAttachmentInfo, TargetBufferFlags const& targetFlags)
Attachment const& depthAttachmentInfo, Attachment const& stencilAttachmentInfo)
: HwRenderTarget{ width, height },
mDefaultRenderTarget{ false },
mTargetFlags{ targetFlags },
mSamples{ samples },
mLayerCount{ layerCount },
mColorAttachments{ colorAttachmentsMRT },
mDepthAttachment{ depthAttachmentInfo },
mStencilAttachment{ stencilAttachmentInfo } {
// TODO consider making this an array
mColorAttachmentDesc.reserve(MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
mColorAttachmentDescriptors.reserve(MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
}
// Default constructor for the default render target
WebGPURenderTarget::WebGPURenderTarget()
: HwRenderTarget{ 0, 0 },
mDefaultRenderTarget{ true },
mTargetFlags{ TargetBufferFlags::NONE },
mSamples{ 1 },
mLayerCount{ 1 }
{}
mLayerCount{ 1 } {}
wgpu::LoadOp WebGPURenderTarget::getLoadOperation(RenderPassParams const& params,
const TargetBufferFlags bufferToOperateOn) {
@@ -60,7 +57,7 @@ wgpu::LoadOp WebGPURenderTarget::getLoadOperation(RenderPassParams const& params
return wgpu::LoadOp::Clear;
}
if (any(params.flags.discardStart & bufferToOperateOn)) {
return wgpu::LoadOp::Clear;
return wgpu::LoadOp::Clear; // Or wgpu::LoadOp::Undefined if clear is not desired on discard
}
return wgpu::LoadOp::Load;
}
@@ -76,30 +73,59 @@ wgpu::StoreOp WebGPURenderTarget::getStoreOperation(RenderPassParams const& para
void WebGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor& outDescriptor,
RenderPassParams const& params, wgpu::TextureView const& defaultColorTextureView,
wgpu::TextureView const& defaultDepthStencilTextureView,
wgpu::TextureFormat const& defaultDepthStencilFormat,
wgpu::TextureView const* customColorTextureViews, uint32_t customColorTextureViewCount,
wgpu::TextureView const& customDepthStencilTextureView){
mColorAttachmentDesc.clear();
wgpu::TextureView const& customDepthTextureView,
wgpu::TextureView const& customStencilTextureView,
const wgpu::TextureFormat customDepthFormat,
const wgpu::TextureFormat customStencilFormat) {
mColorAttachmentDescriptors.clear();
mHasDepthStencilAttachment = false;
const bool hasDepth = any(mTargetFlags & TargetBufferFlags::DEPTH);
const bool hasStencil = any(mTargetFlags & TargetBufferFlags::STENCIL);
mHasDepthStencilAttachment = hasDepth || hasStencil;
bool const depthReadOnly =
(params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0;
// Color attachments
if (mDefaultRenderTarget) {
assert_invariant(defaultColorTextureView);
mColorAttachmentDesc.push_back({
.view = defaultColorTextureView,
mColorAttachmentDescriptors.push_back({ .view = defaultColorTextureView,
.resolveTarget = nullptr,
.loadOp = WebGPURenderTarget::getLoadOperation(params, TargetBufferFlags::COLOR0),
.storeOp = WebGPURenderTarget::getStoreOperation(params, TargetBufferFlags::COLOR0),
.clearValue = { params.clearColor.r, params.clearColor.g, params.clearColor.b,
params.clearColor.a } });
} else {
bool const depthReadonly =
(params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0;
if (defaultDepthStencilTextureView) {
mDepthStencilAttachmentDescriptor = {
.view = defaultDepthStencilTextureView,
.depthLoadOp = depthReadonly ? wgpu::LoadOp::Undefined
: WebGPURenderTarget::getLoadOperation(params,
TargetBufferFlags::DEPTH),
.depthStoreOp = depthReadonly ? wgpu::StoreOp::Undefined
: WebGPURenderTarget::getStoreOperation(params,
TargetBufferFlags::DEPTH),
.depthClearValue = static_cast<float>(params.clearDepth),
.depthReadOnly = depthReadonly,
.stencilLoadOp = wgpu::LoadOp::Undefined,
.stencilStoreOp = wgpu::StoreOp::Undefined,
.stencilClearValue = params.clearStencil,
.stencilReadOnly =
(params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0,
};
if (defaultDepthStencilFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
defaultDepthStencilFormat == wgpu::TextureFormat::Depth32FloatStencil8 ||
defaultDepthStencilFormat == wgpu::TextureFormat::Stencil8) {
mDepthStencilAttachmentDescriptor.stencilLoadOp =
WebGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDescriptor.stencilStoreOp =
WebGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL);
}
mHasDepthStencilAttachment = true;
}
} else { // Custom Render Target
for (uint32_t i = 0; i < customColorTextureViewCount; ++i) {
if (customColorTextureViews[i]) {
mColorAttachmentDesc.push_back({ .view = customColorTextureViews[i],
mColorAttachmentDescriptors.push_back({ .view = customColorTextureViews[i],
// .resolveTarget = nullptr; // TODO: MSAA resolve for custom RT
.loadOp =
WebGPURenderTarget::getLoadOperation(params, getTargetBufferFlagsAt(i)),
@@ -111,62 +137,73 @@ void WebGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor&
.a = params.clearColor.a } });
}
}
}
outDescriptor.colorAttachmentCount = mColorAttachmentDesc.size();
outDescriptor.colorAttachments = mColorAttachmentDesc.data();
// Depth/Stencil Attachments
if (mHasDepthStencilAttachment) {
wgpu::TextureView depthStencilViewToUse{nullptr};
if (mDefaultRenderTarget) {
depthStencilViewToUse = defaultDepthStencilTextureView;
assert_invariant(depthStencilViewToUse);
} else {
if (customDepthStencilTextureView) {
depthStencilViewToUse = customDepthStencilTextureView;
}
}
FILAMENT_CHECK_POSTCONDITION(!(customDepthTextureView && customStencilTextureView))
<< "WebGPU CANNOT support separate texture views for depth + stencil. depth + "
"stencil needs to be in one texture view";
if (depthStencilViewToUse) {
mDepthStencilAttachmentDesc = {};
mDepthStencilAttachmentDesc.view = depthStencilViewToUse;
const bool hasStencil =
customStencilTextureView ||
(customDepthFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
customDepthFormat == wgpu::TextureFormat::Depth32FloatStencil8);
const bool hasDepth =
customDepthTextureView ||
(customStencilFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
customDepthFormat == wgpu::TextureFormat::Depth32FloatStencil8);
if (customDepthTextureView || customStencilTextureView) {
assert_invariant((hasDepth || hasStencil) &&
"Depth or Texture view without a valid texture format");
mDepthStencilAttachmentDescriptor = {};
mDepthStencilAttachmentDescriptor.view =
customDepthTextureView ? customDepthTextureView : customStencilTextureView;
if (hasDepth) {
mDepthStencilAttachmentDesc.depthLoadOp = depthReadOnly ? wgpu::LoadOp::Undefined :getLoadOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDesc.depthStoreOp = depthReadOnly ? wgpu::StoreOp::Undefined :getStoreOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDesc.depthClearValue = static_cast<float>(params.clearDepth);
mDepthStencilAttachmentDesc.depthReadOnly = depthReadOnly;
bool const depthReadonly =
(params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0;
mDepthStencilAttachmentDescriptor.depthLoadOp =
depthReadonly ? wgpu::LoadOp::Undefined :
WebGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDescriptor.depthStoreOp =
depthReadonly ? wgpu::StoreOp::Undefined :
WebGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDescriptor.depthClearValue =
static_cast<float>(params.clearDepth);
mDepthStencilAttachmentDescriptor.depthReadOnly = depthReadonly;
} else {
mDepthStencilAttachmentDesc.depthLoadOp = wgpu::LoadOp::Undefined;
mDepthStencilAttachmentDesc.depthStoreOp = wgpu::StoreOp::Undefined;
mDepthStencilAttachmentDesc.depthReadOnly = true;
mDepthStencilAttachmentDescriptor.depthLoadOp = wgpu::LoadOp::Undefined;
mDepthStencilAttachmentDescriptor.depthStoreOp = wgpu::StoreOp::Undefined;
mDepthStencilAttachmentDescriptor.depthReadOnly = true;
}
if (hasStencil) {
mDepthStencilAttachmentDesc.stencilLoadOp = getLoadOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDesc.stencilStoreOp = getStoreOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDesc.stencilClearValue = params.clearStencil;
mDepthStencilAttachmentDesc.stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0;
mDepthStencilAttachmentDescriptor.stencilLoadOp =
WebGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDescriptor.stencilStoreOp =
WebGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDescriptor.stencilClearValue = params.clearStencil;
mDepthStencilAttachmentDescriptor.stencilReadOnly =
(params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0;
} else {
mDepthStencilAttachmentDesc.stencilLoadOp = wgpu::LoadOp::Undefined;
mDepthStencilAttachmentDesc.stencilStoreOp = wgpu::StoreOp::Undefined;
mDepthStencilAttachmentDesc.stencilReadOnly = true;
mDepthStencilAttachmentDescriptor.stencilLoadOp = wgpu::LoadOp::Undefined;
mDepthStencilAttachmentDescriptor.stencilStoreOp = wgpu::StoreOp::Undefined;
mDepthStencilAttachmentDescriptor.stencilReadOnly = true;
}
outDescriptor.depthStencilAttachment = &mDepthStencilAttachmentDesc;
} else {
outDescriptor.depthStencilAttachment = nullptr;
mDepthStencilAttachmentDesc = {};
mHasDepthStencilAttachment = true;
}
} else {
outDescriptor.depthStencilAttachment = nullptr;
mDepthStencilAttachmentDesc = {};
}
outDescriptor.colorAttachmentCount = mColorAttachmentDescriptors.size();
outDescriptor.colorAttachments = mColorAttachmentDescriptors.data();
outDescriptor.depthStencilAttachment =
mHasDepthStencilAttachment ? &mDepthStencilAttachmentDescriptor : nullptr;
// descriptor.sampleCount was removed from the core spec. If your webgpu.h still has it,
// and your Dawn version expects it, you might need to set it here based on this->samples.
// e.g., descriptor.sampleCount = this->samples;
}
} // namespace filament::backend
}// namespace filament::backend
@@ -34,41 +34,44 @@ public:
WebGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount,
MRT const& colorAttachments, Attachment const& depthAttachment,
Attachment const& stencilAttachment, TargetBufferFlags const& targetFlags);
Attachment const& stencilAttachment);
// Default constructor for the default render target
WebGPURenderTarget();
// Updated signature: takes resolved views for custom RTs, and default views for default RT
void setUpRenderPassAttachments(
wgpu::RenderPassDescriptor& outDescriptor,
RenderPassParams const& params,
// For default render target:
wgpu::TextureView const& defaultColorTextureView,
wgpu::TextureView const& defaultDepthStencilTextureView,
wgpu::TextureFormat const& defaultDepthStencilFormat,
// For custom render targets:
wgpu::TextureView const* customColorTextureViews, // Array of views
uint32_t customColorTextureViewCount,
wgpu::TextureView const& customDepthStencilTextureView);
wgpu::TextureView const& customDepthTextureView,
wgpu::TextureView const& customStencilTextureView,
wgpu::TextureFormat customDepthFormat,
wgpu::TextureFormat customStencilFormat);
[[nodiscard]] bool isDefaultRenderTarget() const { return mDefaultRenderTarget; }
[[nodiscard]] uint8_t getSamples() const { return mSamples; }
[[nodiscard]] uint8_t getLayerCount() const { return mLayerCount; }
// Accessors for the driver to get stored attachment info
[[nodiscard]] MRT const& getColorAttachmentInfos() const { return mColorAttachments; }
[[nodiscard]] Attachment const& getDepthAttachmentInfo() const { return mDepthAttachment; }
[[nodiscard]] Attachment const& getStencilAttachmentInfo() const { return mStencilAttachment; }
// Static helpers for load/store operations
[[nodiscard]] static wgpu::LoadOp getLoadOperation(RenderPassParams const& params,
TargetBufferFlags buffer);
[[nodiscard]] static wgpu::StoreOp getStoreOperation(RenderPassParams const& params,
TargetBufferFlags buffer);
[[nodiscard]] TargetBufferFlags getTargetFlags() const { return mTargetFlags; }
void setTargetFlags( TargetBufferFlags value) { mTargetFlags = value; }
private:
bool mDefaultRenderTarget = false;
TargetBufferFlags mTargetFlags = TargetBufferFlags::NONE;
uint8_t mSamples = 1;
uint8_t mLayerCount = 1;
@@ -79,8 +82,8 @@ private:
Attachment mStencilAttachment{};
// Cached descriptors for the render pass
std::vector<wgpu::RenderPassColorAttachment> mColorAttachmentDesc;
wgpu::RenderPassDepthStencilAttachment mDepthStencilAttachmentDesc{};
std::vector<wgpu::RenderPassColorAttachment> mColorAttachmentDescriptors;
wgpu::RenderPassDepthStencilAttachment mDepthStencilAttachmentDescriptor{};
bool mHasDepthStencilAttachment = false;
};
+1 -1
View File
@@ -101,7 +101,7 @@ template<typename WebGPUPrintable>
return out.str();
}
[[nodiscard]] inline std::string adapterInfoToString(wgpu::AdapterInfo const& info) {
[[nodiscard]] std::string adapterInfoToString(wgpu::AdapterInfo const& info) {
std::stringstream out;
out << "vendor (" << info.vendorID << ") '" << info.vendor
<< "' device (" << info.deviceID << ") '" << info.device
@@ -110,6 +110,7 @@ void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
return wgpu::TextureFormat::Depth24PlusStencil8;
}
} else {
// other options: Depth16Unorm or Depth24Plus
return wgpu::TextureFormat::Depth32Float;
}
}
@@ -216,9 +217,10 @@ void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
}
[[nodiscard]] wgpu::TextureView createDepthTextureView(wgpu::Texture const& depthTexture,
wgpu::TextureFormat const& depthFormat, bool const needStencil) {
wgpu::TextureFormat depthFormat, bool needStencil) {
wgpu::TextureViewDescriptor descriptor{
.label = "depth_texture_view",
.format = depthFormat,
.dimension = wgpu::TextureViewDimension::e2D,
.baseMipLevel = 0,
.mipLevelCount = 1,
@@ -229,16 +231,6 @@ void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
};
if (needStencil) {
descriptor.aspect = wgpu::TextureAspect::All;
descriptor.format = depthFormat;
} else {
descriptor.aspect = wgpu::TextureAspect::DepthOnly;
if (depthFormat == wgpu::TextureFormat::Depth32FloatStencil8) {
descriptor.format = wgpu::TextureFormat::Depth32Float;
} else if (depthFormat == wgpu::TextureFormat::Depth24PlusStencil8) {
descriptor.format = wgpu::TextureFormat::Depth24Plus;
} else {
descriptor.format = depthFormat;
}
}
wgpu::TextureView depthTextureView = depthTexture.CreateView(&descriptor);
FILAMENT_CHECK_POSTCONDITION(depthTextureView) << "Failed to create depth texture view";
+3 -15
View File
@@ -40,24 +40,18 @@ using namespace filament::math;
using namespace image;
#endif
#include <iostream>
namespace test {
Backend BackendTest::sBackend = Backend::NOOP;
OperatingSystem BackendTest::sOperatingSystem = OperatingSystem::OTHER;
bool BackendTest::sIsMobilePlatform = false;
int BackendTest::sArgc = 0;
char** BackendTest::sArgv = nullptr;
std::vector<std::string> BackendTest::sFailedImages;
void BackendTest::init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform,
int argc, char** argv) {
void BackendTest::init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform) {
sBackend = backend;
sOperatingSystem = operatingSystem;
sIsMobilePlatform = isMobilePlatform;
sArgc = argc;
sArgv = argv;
}
BackendTest::BackendTest() : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE,
@@ -166,11 +160,6 @@ void BackendTest::markImageAsFailure(std::string failedImageName) {
sFailedImages.emplace_back(std::move(failedImageName));
}
std::filesystem::path BackendTest::binaryDirectory() {
assert(sArgc >= 1);
return std::filesystem::path(sArgv[0]).remove_filename().string();
}
void BackendTest::recordFailedImages() {
if (!sFailedImages.empty()) {
std::string failedImages;
@@ -198,9 +187,8 @@ public:
}
};
void initTests(Backend backend, OperatingSystem operatingSystem, bool isMobile, int& argc,
char* argv[]) {
BackendTest::init(backend, operatingSystem, isMobile, argc, argv);
void initTests(Backend backend, OperatingSystem operatingSystem, bool isMobile, int& argc, char* argv[]) {
BackendTest::init(backend, operatingSystem, isMobile);
::testing::InitGoogleTest(&argc, argv);
::testing::AddGlobalTestEnvironment(new Environment);
}
+2 -8
View File
@@ -19,8 +19,6 @@
#include <gtest/gtest.h>
#include <filesystem>
#include <backend/Platform.h>
#include "private/backend/CommandBufferQueue.h"
@@ -33,20 +31,16 @@ namespace test {
class BackendTest : public ::testing::Test {
public:
static void init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform,
int argc, char** argv);
static void init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform);
static Backend sBackend;
static OperatingSystem sOperatingSystem;
static bool sIsMobilePlatform;
static int sArgc;
static char** sArgv;
// Takes the name of the image that wasn't correct, without the .png suffix
static void markImageAsFailure(std::string failedImageName);
static std::filesystem::path binaryDirectory();
protected:
BackendTest();
+8 -8
View File
@@ -59,28 +59,28 @@ uint32_t ScreenshotParams::expectedHash() const {
return mExpectedPixelHash;
}
std::filesystem::path ScreenshotParams::actualDirectoryPath() {
return BackendTest::binaryDirectory().append("images/actual_images");
std::string ScreenshotParams::actualDirectoryPath() {
return "images/actual_images";
}
std::string ScreenshotParams::actualFileName() const {
return absl::StrFormat("%s_actual.png", mFileName);
}
std::filesystem::path ScreenshotParams::actualFilePath() const {
return actualDirectoryPath().append(actualFileName());
std::string ScreenshotParams::actualFilePath() const {
return absl::StrFormat("%s/%s", actualDirectoryPath(), actualFileName());
}
std::filesystem::path ScreenshotParams::expectedDirectoryPath() {
return BackendTest::binaryDirectory().append("images/expected_images");
std::string ScreenshotParams::expectedDirectoryPath() {
return "images/expected_images";
}
std::string ScreenshotParams::expectedFileName() const {
return absl::StrFormat("%s.png", mFileName);
}
std::filesystem::path ScreenshotParams::expectedFilePath() const {
return expectedDirectoryPath().append(expectedFileName());
std::string ScreenshotParams::expectedFilePath() const {
return absl::StrFormat("%s/%s", expectedDirectoryPath(), expectedFileName());
}
const std::string ScreenshotParams::filePrefix() const {
+4 -5
View File
@@ -17,7 +17,6 @@
#ifndef TNT_IMAGE_EXPECTATIONS_H
#define TNT_IMAGE_EXPECTATIONS_H
#include <filesystem>
#include <vector>
#include "gtest/gtest.h"
@@ -52,12 +51,12 @@ public:
bool isSrgb() const;
uint32_t expectedHash() const;
static std::filesystem::path actualDirectoryPath();
static std::string actualDirectoryPath();
std::string actualFileName() const;
std::filesystem::path actualFilePath() const;
static std::filesystem::path expectedDirectoryPath();
std::string actualFilePath() const;
static std::string expectedDirectoryPath();
std::string expectedFileName() const;
std::filesystem::path expectedFilePath() const;
std::string expectedFilePath() const;
const std::string filePrefix() const;
private:
-20
View File
@@ -139,24 +139,6 @@ TEST_F(ReadPixelsTest, ReadPixels) {
return bufferDimension;
}
void exportScreenshot(void* pixelData) const {
#ifndef FILAMENT_IOS
const size_t width = readRect.width, height = readRect.height;
LinearImage image(width, height, 4);
if (format == PixelDataFormat::RGBA && type == PixelDataType::UBYTE) {
image = toLinearWithAlpha<uint8_t>(width, height, width * 4, (uint8_t*)pixelData);
}
if (format == PixelDataFormat::RGBA && type == PixelDataType::FLOAT) {
memcpy(image.getPixelRef(), pixelData, width * height * sizeof(math::float4));
}
std::string png = std::string(testName) + ".png";
std::filesystem::path path = ScreenshotParams::actualDirectoryPath();
path.append(png);
std::ofstream outputStream(path.c_str(), std::ios::binary | std::ios::trunc);
ImageEncoder::encode(outputStream, ImageEncoder::Format::PNG, image, "", png);
#endif
}
// The format and type for the readPixels call.
PixelDataFormat format = PixelDataFormat::RGBA;
PixelDataType type = PixelDataType::UBYTE;
@@ -329,8 +311,6 @@ TEST_F(ReadPixelsTest, ReadPixels) {
const auto* test = (const TestCase*)user;
assert_invariant(test);
test->exportScreenshot(buffer);
// Hash the contents of the buffer and check that they match.
uint32_t hash = utils::hash::murmur3((const uint32_t*)buffer, size / 4, 0);
+1 -1
View File
@@ -442,7 +442,7 @@ void FTexture::setImage(FEngine& engine, size_t const level,
// TODO: PBD should have a p.depth (# layers to skip)
FILAMENT_CHECK_PRECONDITION(
bpp * (pbdWidth - p.left) * (pbdHeight - p.top) * (pbdDepth - 0) >=
bpp * width * height * depth)
bpp * (width - xoffset) * (height - yoffset) * (depth - zoffset))
<< "buffer overflow: (size=" << size_t(p.size) << ", stride=" << size_t(p.stride)
<< ", left=" << unsigned(p.left) << ", top=" << unsigned(p.top)
<< ") smaller than specified region "
+2 -1
View File
@@ -162,7 +162,8 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
FILAMENT_CHECK_PRECONDITION(mImpl->mBufferCount <= DOCUMENTED_MAX_VERTEX_BUFFER_COUNT)
<< "bufferCount cannot be more than " << DOCUMENTED_MAX_VERTEX_BUFFER_COUNT;
} else if (mImpl->mBufferCount > DOCUMENTED_MAX_VERTEX_BUFFER_COUNT) {
LOG(WARNING) << "bufferCount cannot be more than " << DOCUMENTED_MAX_VERTEX_BUFFER_COUNT;
utils::slog.w << "bufferCount cannot be more than " << DOCUMENTED_MAX_VERTEX_BUFFER_COUNT
<< utils::io::endl;
}
// Next we check if any unused buffer slots have been allocated. This helps prevent errors
+1 -3
View File
@@ -66,7 +66,6 @@ DescriptorSet& DescriptorSet::operator=(DescriptorSet&& rhs) noexcept {
mDirty = rhs.mDirty;
mValid = rhs.mValid;
mSetAfterCommitWarning = rhs.mSetAfterCommitWarning;
mSetUndefinedParameterWarning = rhs.mSetUndefinedParameterWarning;
}
return *this;
}
@@ -107,12 +106,11 @@ void DescriptorSet::commitSlow(DescriptorSetLayout const& layout,
});
auto const unsetValidDescriptors = layout.getValidDescriptors() & ~mValid;
if (UTILS_VERY_UNLIKELY(!unsetValidDescriptors.empty() && !mSetUndefinedParameterWarning)) {
if (UTILS_VERY_UNLIKELY(!unsetValidDescriptors.empty())) {
unsetValidDescriptors.forEachSetBit([&](auto i) {
LOG(WARNING) << (layout.isSampler(i) ? "Sampler" : "Buffer") << " descriptor " << i
<< " of " << mName.c_str() << " is not set. Please report this issue.";
});
mSetUndefinedParameterWarning = true;
}
}
-1
View File
@@ -110,7 +110,6 @@ private:
mutable utils::bitset64 mValid; // 8
backend::DescriptorSetHandle mDescriptorSetHandle; // 4
mutable bool mSetAfterCommitWarning = false; // 1
mutable bool mSetUndefinedParameterWarning = false; // 1
utils::StaticString mName; // 16
};
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.62.0"
spec.version = "1.61.1"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.62.0/filament-v1.62.0-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.61.1/filament-v1.61.1-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 62;
static constexpr size_t MATERIAL_VERSION = 61;
/**
* Supported shading models
+7 -22
View File
@@ -27,7 +27,6 @@
#include <backend/DriverEnums.h>
#include <backend/TargetBufferInfo.h>
#include <mutex>
#include <utils/BitmaskEnum.h>
#include <utils/bitset.h>
#include <utils/compiler.h>
@@ -665,33 +664,19 @@ public:
precision(p),
samplerType(t),
format(f),
parameterType(SAMPLER),
unfilterable(unfilterable),
multisample(ms),
transformName(tn),
stages(s),
parameterType(SAMPLER) {}
stages(s) {}
// Uniform
Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p)
: name(paramName),
size(typeSize),
uniformType(t),
precision(p),
format{ 0 },
unfilterable(false),
multisample(false),
parameterType(UNIFORM) {}
: name(paramName), size(typeSize), uniformType(t), precision(p), parameterType(UNIFORM) { }
// Subpass
Parameter(const char* paramName, SubpassType t, SamplerFormat f, ParameterPrecision p)
: name(paramName),
size(1),
precision(p),
subpassType(t),
format(f),
unfilterable(false),
multisample(false),
parameterType(SUBPASS) {}
: name(paramName), size(1), precision(p), subpassType(t), format(f), parameterType(SUBPASS) { }
utils::CString name;
size_t size;
@@ -699,9 +684,9 @@ public:
ParameterPrecision precision;
SamplerType samplerType;
SubpassType subpassType;
SamplerFormat format;
bool unfilterable;
bool multisample;
SamplerFormat format = SamplerFormat::INT; // 0 of the enum
bool unfilterable = false;
bool multisample = false;
utils::CString transformName;
ShaderStageFlags stages;
enum {
-10
View File
@@ -196,7 +196,6 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
uint64_t fieldSize;
uint8_t fieldType;
uint8_t fieldPrecision;
uint8_t fieldAssociatedSampler;
if (!uib.read(&fieldName)) {
return false;
@@ -214,10 +213,6 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
return false;
}
if (!uib.read(&fieldAssociatedSampler)) {
return false;
}
text << " "
<< setw(alignment) << fieldName.c_str()
<< setw(shortAlignment) << toString(UniformType(fieldType))
@@ -232,7 +227,6 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
uint8_t fieldType;
uint8_t fieldFormat;
uint8_t fieldPrecision;
bool fieldUnfilterable;
bool fieldMultisample;
if (!sib.read(&fieldName)) {
@@ -254,10 +248,6 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
return false;
}
if (!sib.read(&fieldUnfilterable)) {
return false;
}
if (!sib.read(&fieldMultisample)) {
return false;
}
+4 -2
View File
@@ -383,8 +383,10 @@ ViewerGui::ViewerGui(filament::Engine* engine, filament::Scene* scene, filament:
mSettings.view.vsmShadowOptions.anisotropy = 0;
mSettings.view.dithering = Dithering::TEMPORAL;
mSettings.view.antiAliasing = AntiAliasing::FXAA;
mSettings.view.msaa = { .enabled = true, .sampleCount = 4 };
mSettings.view.ssao.enabled = true;
// mSettings.view.msaa = { .enabled = true, .sampleCount = 4 };
mSettings.view.msaa = { .enabled = false, .sampleCount = 4 };
// mSettings.view.ssao.enabled = true;
mSettings.view.ssao.enabled = false;
mSettings.view.bloom.enabled = true;
DebugRegistry& debug = mEngine->getDebugRegistry();
+3 -17
View File
@@ -82,14 +82,10 @@ class GoldenManager:
self._git_exec('checkout main')
self._git_exec('rebase')
def _git_exec(self, cmd, capture_output=False):
return execute(f'git {cmd}', cwd=self._assets_dir(),
capture_output=capture_output)
def _git_exec(self, cmd):
return execute(f'git {cmd}', cwd=self._assets_dir(), capture_output=False)
# Merge a local branch to the main branch.
# - `tag` is a hash in the filament repo that this merge is associated with
# - `push_to_remote` indicates that the local changes should be pushed to the
# remote branch.
# tag represent a hash in the filament repo that this merge is associated with
def merge_to_main(self, branch, tag, push_to_remote=False):
self.update()
assets_dir = self._assets_dir()
@@ -112,12 +108,6 @@ class GoldenManager:
self._git_exec(f'push origin main')
self.update()
# Create a branch on the local repo by copying the content from a source directory.
# The change/diff is indicated by the 'updates' and the 'deletes' list,
# - Files in 'updates' need to be present in both src_dir and in the repo.
# - Files in 'deletes' only need to be present in the repo.
# If caller wants the changes to be pushed to remote, set the `push_to_remote`
# value to True.
def source_from(self, src_dir, commit_msg, branch,
updates=[], deletes=[], push_to_remote=False):
assets_dir = self._assets_dir()
@@ -149,16 +139,12 @@ class GoldenManager:
self._git_exec(f'push -f origin {branch}')
self.update()
# Download the content of a branch to a directory.
def download_to(self, dest_dir, branch='main'):
self._git_exec(f'checkout {branch}')
assets_dir = self._assets_dir()
mkdir_p(dest_dir)
rdiff_dir = os.path.join(assets_dir, GOLDENS_DIR)
shutil.copytree(rdiff_dir, dest_dir, dirs_exist_ok=True)
code, o = self._git_exec(f'rev-parse HEAD', capture_output=True)
with open(os.path.join(dest_dir, 'GIT_COMMIT_HASH'), 'w') as f:
f.write(o)
# The main entry point will enable download content of a branch to a directory
if __name__ == "__main__":
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.62.0",
"version": "1.61.1",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",