Compare commits

..

35 Commits

Author SHA1 Message Date
bridgewaterrobbie
43ab60521c Quick change to hello triangle to comply with WebGPU requirements on alignment. Long term it would check for which backend is in use 2025-05-14 19:59:34 -04:00
bridgewaterrobbie
b7c685647a Revert "Dawn change to enable writeBuffer for size != multiple of 4"
This reverts commit b94c802076.
2025-05-14 19:58:11 -04:00
Ben Doherty
d45a5cc926 Add documentation on using Instruments (#8737) 2025-05-14 14:53:12 -07:00
Powei Feng
361ba2afea renderdiff: separate comparison from rendering (#8733)
- Move the comparison logic into its own script
- Add entry point bash script to generate the renderings
- Separate the preamble bash logic into its own file
2025-05-14 09:48:16 -07:00
Ben Doherty
78419cd992 Add initialize method to PlatformMetal (#8708) 2025-05-13 14:30:02 -07:00
Sungun Park
cc7361dba5 Release Filament 1.60.0 2025-05-13 21:27:27 +00:00
Powei Feng
5c0841ff56 Update release note after cherry-pick 2025-05-13 14:26:13 -07:00
Matthew Hoffman
10af183756 Document which vulkan tests have issues with associated bugs. (#8731)
BUGS=[409100093]
2025-05-13 21:10:34 +00:00
David Neto
56e0e9a424 Move calls to SPV remapper to another .cpp file (#8729) 2025-05-13 20:33:49 +00:00
Mathias Agopian
86a500c846 clean public header dependencies (#8725)
* remove io::ostream dependency from CString.h + clang tidy

* remove ostream.h dependency from Invocable.h

* remove Panic.h dependency from FixedCapacityVector.h

* remove ostream.h dependency from backend/ headers

* clang tidy Panic.h/Panic.cpp
2025-05-13 11:32:45 -07:00
Sungun Park
5b3f13fc1d Revert "Add getter functions for settings to build ColorGrading object (#8699)"
This reverts commit f10d226565.
2025-05-13 11:22:33 -07:00
Powei Feng
1b1dfaa57c build.sh ndk version fix again (#8726)
Fix again after #8717
2025-05-13 06:44:21 +00:00
Powei Feng
bf8c84bbe5 Clean-up build.sh android (#8722)
Failed to build android when using CMake >= 4.0.

Remove a no longer required check for the android build. This check
should be covered by the cmake_minimum_required() call, which is
present in relevant CMakeLists.txts.
2025-05-13 06:29:17 +00:00
Jeremy Nelson
fb179cabbc Using usage and format from filament 2025-05-12 16:15:21 -07:00
Jeremy Nelson
a80ea743e8 Simplifying aspect cases 2025-05-12 16:15:21 -07:00
Jeremy Nelson
97f8106909 removing duplicate return 2025-05-12 16:15:21 -07:00
Jeremy Nelson
d455899b93 Simplify cases and default to All 2025-05-12 16:15:21 -07:00
Jeremy Nelson
63fe439a5e Infer TextureView Aspect from format and usage
Picks an appropriate aspect based on formate and usage.
2025-05-12 16:15:21 -07:00
Ben Doherty
7847220aba Materials: allow specifying the shader stages for samplers (#8720) 2025-05-12 14:20:11 -07:00
Matthew Hoffman
d76ae4395b Mark ExternalImageCocoaGL as final not just its destructor. (#8719)
This is necessary to build on MacOS with homebrew's llvm as opposed to Apple's.
2025-05-12 19:43:30 +00:00
Powei Feng
f994cb58ce gl: add invalidate_framebuffer workaround for Mesa+Intel (#8706)
FIXES=405252622
2025-05-12 18:18:13 +00:00
Sungun Park
52eb682498 Update MATERIAL_VERSION to 60 2025-05-12 16:45:36 +00:00
Mathias Agopian
5a5168a191 CString tidy cleanup and rvalue methods
We add rvalue version of append/insert/replace so that calling
those on a temporary yields to a temporary.

Also add string literal versions of those so that we can 
append/insert/replace a literal without allocation, e.g.:

`CString foo = CString{ bar }.append("baz");`

This will not end-up creating a temporary CString for "baz".
2025-05-09 16:14:01 -07:00
Powei Feng
e85dfe75c8 github: move test script to /test and same for /build (#8714)
- Moved get_mesa.sh to build/common/get-mesa.sh
- Moved check-headers.sh to test/check-headers/test.sh
- Moved check-metal-shaders.sh to test/check-metal-shaders/test.sh
2025-05-09 22:35:18 +00:00
Logan Lawrence
6beb40b0a1 Update release.yml to include native android build files (#8694) 2025-05-09 17:25:00 +00:00
Powei Feng
5cb96e5732 Clean up ndk version in build.sh (#8717)
Follow up to #8663
2025-05-09 15:51:30 +00:00
Matthew Hoffman
927aa57a4e Document ASAN (with leak detection) on MacOS (#8716)
* Revert "Optional CMake flag for enabling ASAN for backend and its tests. (#8696)"

This reverts commit 543b93939a.
There were other already existing ways to achieve this without the need for new flags.

* Add documentation on running with ASAN and leak detection on mac.

BUGS=[398198310]
2025-05-09 10:22:29 -05:00
Powei Feng
36e775902d renderdiff: script for updating golden images (#8709)
Adding a python script to enable updating new goldens into
a staging branch in the golden repo (filament-assets).

The same script can be used in github workflow to automatically
create a golden staging branch. This will be useful for users
without access to a mac (the only platform for generating
goldens as of now).
2025-05-08 22:07:17 +00:00
Powei Feng
53e28f3b33 github: fix release mac build (#8713) 2025-05-08 11:44:08 -07:00
Powei Feng
7ccbdb4633 Release Filament 1.59.5 2025-05-08 09:06:44 -07:00
Daisuke Kasuga
f10d226565 Add getter functions for settings to build ColorGrading object (#8699)
* add getter methods for ColorGrading builder settings
* add tone mapper impl clone funcs
* update the release notes
---------

Co-authored-by: Daisuke Kasuga <dkasuga@google.com>
2025-05-09 00:43:40 +09:00
Powei Feng
28ecf5c35d renderdiff: add golden repo support (#8689)
- Add GoldenManager to manage access to the repo containing the
  goldens
- Add tif comparison code
- Enable comparison by default for actual test
2025-05-07 21:20:22 +00:00
Matthew Hoffman
9683eb649c Backend test python script updated to display side by side images. (#8695) 2025-05-07 20:48:55 +00:00
Powei Feng
3d10ae3ee3 github: more build file refactoring (#8678)
- Move emscripten download into its own script
 - Refactor the common "CI choice" prompt into its own file.
 - Move the content of `build/common/ci-common.sh` to the
   "CI choice" script.
 - Mention the get-emscripten.sh script in BUILDING.md
2025-05-07 19:39:11 +00:00
rafadevai
44a75dd44b VK: Add a new platform method to check UMA (#8704)
* VK: Add a new context method to check UMA

This heuristic should work on almost all devices,
can be extended later as needed.

* Addressing PR comments

---------

Co-authored-by: Powei Feng <powei@google.com>
Co-authored-by: Serge Metral <sergemetral@google.com>
2025-05-07 12:08:34 -07:00
121 changed files with 1899 additions and 676 deletions

View File

@@ -3,5 +3,5 @@ runs:
using: "composite"
steps:
- name: Set up dependency versions
shell: bash
shell: bash
run: cat ./build/common/versions >> $GITHUB_ENV

16
.github/actions/web-prereq/action.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: 'Web Preqrequisites'
runs:
using: "composite"
steps:
- uses: ./.github/actions/dep-versions
- name: Cache EMSDK
id: emsdk-cache
uses: actions/cache@v4 # Use a specific version
with:
path: emsdk
key: ${{ runner.os }}-emsdk-${{ env.GITHUB_EMSDK_VERSION }}
- name: Install Web Prerequisites
shell: bash
run: |
bash ./build/common/get-emscripten.sh
echo "EMSDK=$PWD/emsdk" >> $GITHUB_ENV

View File

@@ -26,4 +26,4 @@ jobs:
path: out/filament-release-darwin.tgz
- name: Check public headers
run: |
build/common/check-headers.sh out/release/filament/include
test/check-headers/test.sh out/release/filament/include

View File

@@ -96,6 +96,7 @@ jobs:
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- uses: ./.github/actions/web-prereq
- name: Run build script
run: |
cd build/web && printf "y" | ./build.sh presubmit
@@ -123,13 +124,15 @@ jobs:
- uses: ./.github/actions/mac-prereq
- name: Cache Mesa and deps
id: mesa-cache
uses: actions/cache@v4 # Use a specific version
uses: actions/cache@v4
with:
path: mesa
key: ${{ runner.os }}-mesa-deps-2-${{ vars.MESA_VERSION }}
- name: Get Mesa
id: mesa-prereq
run: bash test/utils/get_mesa.sh
- name: Prerequisites
id: prereqs
run: |
bash build/common/get-mesa.sh
pip install tifffile numpy
- name: Run Test
run: bash test/renderdiff/test.sh
- uses: actions/upload-artifact@v4
@@ -150,8 +153,8 @@ jobs:
- name: Run test
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
code-correcteness:
name: code-correctness
test-code-correctness:
name: test-code-correctness
runs-on: 'macos-14-xlarge'
steps:
- uses: actions/checkout@v4.1.6

View File

@@ -65,13 +65,9 @@ jobs:
build-mac:
name: build-mac
runs-on: ${{ matrix.os }}
runs-on: macos-14-xlarge
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
strategy:
matrix:
os: [macos-14-xlarge, ubuntu-22.04-32core]
steps:
- name: Decide Git ref
id: git_ref
@@ -118,6 +114,7 @@ jobs:
with:
ref: ${{ steps.git_ref.outputs.ref }}
- uses: ./.github/actions/linux-prereq
- uses: ./.github/actions/web-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
@@ -166,6 +163,9 @@ jobs:
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
mv out/gltfio-android-release.aar out/gltfio-${TAG}-android.aar
mv out/filament-utils-android-release.aar out/filament-utils-${TAG}-android.aar
cd out/android-release/filament
tar -czf ../../filament-${TAG}-android-native.tgz .
cd ../../..
- name: Sign sample-gltf-viewer
run: |
echo "${APK_KEYSTORE_BASE64}" > filament.jks.base64
@@ -187,7 +187,7 @@ jobs:
script: |
const upload = require('./build/common/upload-release-assets');
const { TAG } = process.env;
const globber = await glob.create(['out/*.aar', 'out/*.apk'].join('\n'));
const globber = await glob.create(['out/*.aar', 'out/*.apk', 'out/*.tgz'].join('\n'));
await upload({ github, context }, await globber.glob(), TAG);
build-ios:

View File

@@ -17,6 +17,7 @@ jobs:
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- uses: ./.github/actions/web-prereq
- name: Run build script
run: |
cd build/web && printf "y" | ./build.sh continuous

View File

@@ -363,6 +363,8 @@ python ./emsdk.py activate latest
source ./emsdk_env.sh
```
Alternatively, you can try running the script `build/common/get-emscripten.sh`.
After this you can invoke the [easy build](#easy-build) script as follows:
```shell

View File

@@ -7,5 +7,3 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- materials: remove dependence on per-view descset layout from filamat. [⚠️ **New Material Version**]

View File

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

View File

@@ -7,6 +7,16 @@ 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.60.1
## v1.60.0
- materials: remove dependence on per-view descset layout from filamat. [⚠️ **New Material Version**]
- matc non-functional change: Update GLSL postprocessor to
isolate calls to SPVRemap from calls to SPIRV-Cross.
## v1.59.5

View File

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

View File

@@ -151,7 +151,7 @@ function print_fgviewer_help {
}
# Unless explicitly specified, NDK version will be selected as highest available version within same major release chain
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/build/android/ndk.version | cut -f 1 -d ".")}
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/build/common/versions | grep GITHUB_NDK_VERSION | sed s/GITHUB_NDK_VERSION=//g | cut -f 1 -d ".")}
# Requirements
CMAKE_MAJOR=3
@@ -463,16 +463,6 @@ function ensure_android_build {
echo "Error: Android NDK side-by-side version ${FILAMENT_NDK_VERSION} or compatible must be installed, exiting"
exit 1
fi
local cmake_version=$(cmake --version)
if [[ "${cmake_version}" =~ ([0-9]+)\.([0-9]+)\.[0-9]+ ]]; then
if [[ "${BASH_REMATCH[1]}" -lt "${CMAKE_MAJOR}" ]] || \
[[ "${BASH_REMATCH[2]}" -lt "${CMAKE_MINOR}" ]]; then
echo "Error: cmake version ${CMAKE_MAJOR}.${CMAKE_MINOR}+ is required," \
"${BASH_REMATCH[1]}.${BASH_REMATCH[2]} installed, exiting"
exit 1
fi
fi
}
function build_android {

View File

@@ -1,28 +1,6 @@
#!/bin/bash
# Usage: the first argument selects the build type:
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
source `dirname $0`/../common/ci-check.sh
set -e
set -x
@@ -30,11 +8,6 @@ set -x
UNAME=`echo $(uname)`
LC_UNAME=`echo $UNAME | tr '[:upper:]' '[:lower:]'`
# build-common.sh will generate the following variables:
# $GENERATE_ARCHIVES
# $BUILD_DEBUG
# $BUILD_RELEASE
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/../common/build-common.sh
if [[ "$GITHUB_WORKFLOW" ]]; then

View File

@@ -1,5 +1,20 @@
#!/bin/bash
# build-common.sh will generate the following variables:
# $GENERATE_ARCHIVES
# $BUILD_DEBUG
# $BUILD_RELEASE
# Typically a build script (build.sh) would source this script. For example,
# source `dirname $0`/../common/build-common.sh
# Usage: the first argument selects the build type:
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
if [[ ! "$TARGET" ]]; then
if [[ "$1" ]]; then
TARGET=$1

19
build/common/ci-check.sh Normal file
View File

@@ -0,0 +1,19 @@
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
if [[ "$GITHUB_WORKFLOW" ]]; then
echo "Running workflow $GITHUB_WORKFLOW (event: $GITHUB_EVENT_NAME, action: $GITHUB_ACTION)"
fi

View File

@@ -1,5 +0,0 @@
#!/bin/bash
if [[ "$GITHUB_WORKFLOW" ]]; then
echo "Running workflow $GITHUB_WORKFLOW (event: $GITHUB_EVENT_NAME, action: $GITHUB_ACTION)"
fi

22
build/common/get-emscripten.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
if [ -d "./emsdk" ]; then
echo "emsdk folder found. Assume emsdk has been installed."
cd emsdk
./emsdk activate latest
source ./emsdk_env.sh
export EMSDK="$PWD"
cd ..
exit 0
fi
# Install emscripten.
EMSDK_VERSION=${GITHUB_EMSDK_VERSION-3.1.60}
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/${EMSDK_VERSION}.zip > emsdk.zip
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
export EMSDK="$PWD"
cd ..

View File

@@ -14,9 +14,9 @@
#!/usr/bin/bash
set -x
set -e
if [[ "$GITHUB_WORKFLOW" ]]; then
set -e
set -x
fi
OS_NAME=$(uname -s)
@@ -35,7 +35,7 @@ source ${ORIG_DIR}/venv/bin/activate
NEEDED_PYTHON_DEPS=("mako" "setuptools" "pyyaml")
for cmd in "${NEEDED_PYTHON_DEPS[@]}"; do
if ! python3 -m pip show "${cmd}" >/dev/null 2>&1; then
if ! python3 -m pip show -q "${cmd}" >/dev/null 2>&1; then
python3 -m pip install ${cmd}
fi
done
@@ -145,6 +145,6 @@ deactivate
popd
if [[ "$GITHUB_WORKFLOW" ]]; then
set +e
set +x
fi
set +x
set +e

View File

@@ -3,4 +3,5 @@ GITHUB_CMAKE_VERSION=3.19.5
GITHUB_NINJA_VERSION=1.10.2
GITHUB_MESA_VERSION=24.2.1
GITHUB_LLVM_VERSION=16
GITHUB_NDK_VERSION=27.0.11718014
GITHUB_NDK_VERSION=27.0.11718014
GITHUB_EMSDK_VERSION=3.1.60

View File

@@ -1,35 +1,11 @@
#!/bin/bash
# Usage: the first argument selects the build type:
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
source `dirname $0`/../common/ci-check.sh
set -e
set -x
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null
# If we're generating an archive for release or continuous builds, then we'll also build for the

View File

@@ -1,38 +1,11 @@
#!/bin/bash
# Usage: the first argument selects the build type:
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
source `dirname $0`/../common/ci-check.sh
set -e
set -x
# build-common.sh will generate the following variables:
# $GENERATE_ARCHIVES
# $BUILD_DEBUG
# $BUILD_RELEASE
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null
./build.sh -c $RUN_TESTS $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE

View File

@@ -1,34 +1,11 @@
#!/bin/bash
# Usage: the first argument selects the build type:
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
source `dirname $0`/../common/ci-check.sh
set -e
set -x
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null
./build.sh -c $RUN_TESTS $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE

View File

@@ -1,34 +1,10 @@
#!/bin/bash
# Usage: the first argument selects the build type:
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
source `dirname $0`/../common/ci-check.sh
set -e
set -x
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/ci-common.sh
source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null

View File

@@ -1,11 +0,0 @@
#!/bin/bash
# Install emscripten.
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.60.zip > emsdk.zip
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
export EMSDK="$PWD"
cd ..

View File

@@ -1023,8 +1023,12 @@ samplerCubemap | Cubemap texture
[Table [materialParamsTypes]: Material parameter types]
Samplers
: Sampler types can also specify a `format` which can be either `int` or `float` (defaults to
`float`).
: Sampler types can specify additional options:
- `format`: either `int` or `float` (defaults to `float`).
- `stages`: array of strings containing the list of shader stages this
sampler can be accessed from. Each entry must be either `vertex` or
`fragment` (defaults to both).
Arrays
: A parameter can define an array of values by appending `[size]` after the type name, where

View File

@@ -18,6 +18,8 @@
- [Metal](./notes/metal_debugging.md)
- [Vulkan](./notes/vulkan_debugging.md)
- [SPIR-V](./notes/spirv_debugging.md)
- [Running with ASAN and UBSAN](./notes/asan_ubsan.md)
- [Using Instruments on macOS](./notes/instruments.md)
- [Libraries](./notes/libs.md)
- [bluegl](./dup/bluegl.md)
- [bluevk](./dup/bluevk.md)

View File

@@ -0,0 +1,41 @@
# Running with ASAN/UBSAN
## Enabling
When building though build.sh, pass the `-b` flag. This sets the cmake variable
`FILAMENT_ENABLE_ASAN_UBSAN=ON` which eventually passes `"-fsanitize=address -fsanitize=undefined"`
to all compile and link operations.
If building through CMake directly, or an IDE like CLion that doesn't use build.sh, instead pass
`-DFILAMENT_ENABLE_ASAN_UBSAN=ON` to cmake in order to get the same result.
## Getting memory leak detection on Mac
Memory leak detection isn't enabled by default on MacOS. There are two issues to address, first is
using a version of clang that supports memory leak detection and second is enabling it at runtime.
The version of clang distributed by Apple (with a version like "Apple clang version 16.0.0") doesn't
currently support leak detection at all. Instead you will need to get or build a different LLVM,
such as the one distributed through homebrew and get CMake to use that instead.
Then during runtime you'll need to have the environment variable `ASAN_OPTIONS` include the option
`detect_leaks=1`. Multiple `ASAN_OPTIONS` values are concatenated with `:`.
## Getting memory leak output in CLion
### Setting variables
Under `Settings | Build, Execution, Deployment | Dynamic Analysis Tools | Sanitizers` there is an
ASAN Settings field that overrides whatever other `ASAN_OPTIONS` you might set elsewhere, so you
must use that instead of setting it through your Run/Debug Configuration.
To pass `-DFILAMENT_ENABLE_ASAN_UBSAN=ON` to CMake you'll want to create a new CMake Profile and
pass it as a CMake argument.
### Avoiding losing output
CMake will consume ASAN output and display it through a separate "Sanitizers" tab. Unfortunately
certain leak detection errors that interrupt the executable seem to not show up in this tab, but are
still removed from the user-visible console output. If this is happening and you need to see the
unfiltered console output you'll need to go to `Settings | Build, Execution, Deployment | Dynamic
Analysis Tools | Sanitizers` and uncheck "Use visual representation for Sanitizer's output".

View File

@@ -0,0 +1,36 @@
# Using Instruments on macOS
When running a binary under Instruments on macOS, you may run into the following issue when
launching or attaching to an executable:
```
Failed to gain authorization
Recovery Suggestion: Target binary needs to be debuggable and signed with 'get-task-allow'
```
This is a security precaution; the solution is to code sign the binary with the
`com.apple.security.get-task-allow` entitlement.
1. Create an `entitlements.plist` file with the following contents:
```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>
```
2. Run the following command:
```
codesign -s - --entitlements entitlements.plist <binary>
```
Replace `<binary>` with the name of the binary, for example: `out/cmake-debug/samples/gltf_viewer`.
Afterwards, you should be able to successfully launch and attach to the executable using
Instruments.

View File

@@ -5,18 +5,6 @@ set(TARGET backend)
set(PUBLIC_HDR_DIR include)
set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR})
# ==================================================================================================
# Compilation options
# ==================================================================================================
#
set(BACKEND_SANITIZATION "" CACHE STRING "Sanitization option")
set_property(CACHE BACKEND_SANITIZATION PROPERTY STRINGS ";ASAN")
set(BACKEND_SANITIZERS)
if (BACKEND_SANITIZATION STREQUAL "ASAN")
set(BACKEND_SANITIZERS -fsanitize=address)
endif()
# ==================================================================================================
# Sources and headers
# ==================================================================================================
@@ -484,7 +472,6 @@ target_compile_options(${TARGET} PRIVATE
${OSMESA_COMPILE_FLAGS}
$<$<CONFIG:Release>:${OPTIMIZATION_FLAGS}>
$<$<AND:$<PLATFORM_ID:Darwin>,$<CONFIG:Release>>:${DARWIN_OPTIMIZATION_FLAGS}>
${BACKEND_SANITIZERS}
)
if (FILAMENT_SUPPORTS_METAL)
@@ -495,8 +482,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING}>:FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING>)
endif()
target_link_options(${TARGET} PRIVATE ${BACKEND_SANITIZERS})
target_link_libraries(${TARGET} PRIVATE
${OSMESA_LINKER_FLAGS}
$<$<AND:$<PLATFORM_ID:Linux>,$<CONFIG:Release>>:${LINUX_LINKER_OPTIMIZATION_FLAGS}>
@@ -566,8 +551,6 @@ if (APPLE AND NOT IOS)
test/test_RenderExternalImage.cpp)
add_library(backend_test STATIC ${BACKEND_TEST_SRC})
target_link_libraries(backend_test PUBLIC ${BACKEND_TEST_LIBS})
target_compile_options(backend_test PRIVATE ${BACKEND_SANITIZERS})
target_link_options(backend_test PRIVATE ${BACKEND_SANITIZERS})
set(BACKEND_TEST_DEPS
OSDependent
@@ -606,7 +589,6 @@ if (APPLE AND NOT IOS)
# linker from removing "unused" symbols.
target_link_libraries(backend_test_mac PRIVATE -force_load backend_test)
set_target_properties(backend_test_mac PROPERTIES FOLDER Tests)
target_link_options(backend_test_mac PRIVATE ${BACKEND_SANITIZERS})
# This is needed after XCode 15.3
set_target_properties(backend_test_mac PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
@@ -616,8 +598,6 @@ endif()
if (LINUX)
add_executable(backend_test_linux test/linux_runner.cpp ${BACKEND_TEST_SRC})
target_compile_options(backend_test_linux PRIVATE ${BACKEND_SANITIZERS})
target_link_options(backend_test_linux PRIVATE ${BACKEND_SANITIZERS})
target_link_libraries(backend_test_linux PRIVATE ${BACKEND_TEST_LIBS})
set_target_properties(backend_test_linux PROPERTIES FOLDER Tests)
endif()

View File

@@ -20,10 +20,15 @@
#define TNT_FILAMENT_BACKEND_BUFFERDESCRIPTOR_H
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <utility>
#include <stddef.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
class CallbackHandler;
@@ -89,8 +94,8 @@ public:
* @param callback A callback used to release the CPU buffer from this BufferDescriptor
* @param user An opaque user pointer passed to the callback function when it's called
*/
BufferDescriptor(void const* buffer, size_t size,
Callback callback = nullptr, void* user = nullptr) noexcept
BufferDescriptor(void const* buffer, size_t const size,
Callback const callback = nullptr, void* user = nullptr) noexcept
: buffer(const_cast<void*>(buffer)), size(size), mCallback(callback), mUser(user) {
}
@@ -98,11 +103,12 @@ public:
* Creates a BufferDescriptor that references a CPU memory-buffer
* @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes
* @param handler A custom handler for the callback
* @param callback A callback used to release the CPU buffer from this BufferDescriptor
* @param user An opaque user pointer passed to the callback function when it's called
*/
BufferDescriptor(void const* buffer, size_t size,
CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept
BufferDescriptor(void const* buffer, size_t const size,
CallbackHandler* handler, Callback const callback, void* user = nullptr) noexcept
: buffer(const_cast<void*>(buffer)), size(size),
mCallback(callback), mUser(user), mHandler(handler) {
}
@@ -116,8 +122,9 @@ public:
*
* @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes
* @param data A pointer to the data
* @param handler Handler to use to dispatch the callback, or nullptr for the default handler
* @return a new BufferDescriptor
* @return A new BufferDescriptor
*/
template<typename T, void(T::*method)(void const*, size_t)>
static BufferDescriptor make(void const* buffer, size_t size, T* data,
@@ -164,7 +171,7 @@ public:
* @param callback The new callback function
* @param user An opaque user pointer passed to the callbeck function when it's called
*/
void setCallback(Callback callback, void* user = nullptr) noexcept {
void setCallback(Callback const callback, void* user = nullptr) noexcept {
this->mCallback = callback;
this->mUser = user;
this->mHandler = nullptr;
@@ -176,7 +183,7 @@ public:
* @param callback The new callback function
* @param user An opaque user pointer passed to the callbeck function when it's called
*/
void setCallback(CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept {
void setCallback(CallbackHandler* handler, Callback const callback, void* user = nullptr) noexcept {
mCallback = callback;
mUser = user;
mHandler = handler;

View File

@@ -19,8 +19,6 @@
#include <backend/DriverApiForward.h>
#include <utils/ostream.h>
#include <initializer_list>
#include <memory>
@@ -28,6 +26,10 @@
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
void* allocateFromCommandStream(DriverApi& driver, size_t size, size_t alignment) noexcept;

View File

@@ -25,12 +25,11 @@
#include <backend/PresentCallable.h>
#include <utils/BitmaskEnum.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/StaticString.h>
#include <utils/debug.h>
#include <math/vec4.h>
@@ -41,6 +40,10 @@
#include <stddef.h>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
/**
* Types and enums used by filament's driver.
*

View File

@@ -17,9 +17,6 @@
#ifndef TNT_FILAMENT_BACKEND_HANDLE_H
#define TNT_FILAMENT_BACKEND_HANDLE_H
#if !defined(NDEBUG)
#include <utils/ostream.h>
#endif
#include <utils/debug.h>
#include <type_traits> // FIXME: STL headers are not allowed in public headers
@@ -27,6 +24,10 @@
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
struct HwBufferObject;

View File

@@ -20,12 +20,14 @@
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/ostream.h>
#include <array>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
//! \privatesection

View File

@@ -24,11 +24,14 @@
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <stddef.h>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
/**

View File

@@ -20,12 +20,10 @@
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h>
#include <utils/ostream.h>
#include <backend/DriverEnums.h>
#include <array>
#include <unordered_map>
#include <tuple>
#include <utility>
#include <variant>
@@ -33,6 +31,10 @@
#include <stddef.h>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
class Program {

View File

@@ -19,22 +19,26 @@
#include <backend/Handle.h>
#include <utils/ostream.h>
#include <utility>
#include <stddef.h>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
//! \privatesection
struct TargetBufferInfo {
// note: the parameters of this constructor are not in the order of this structure's fields
TargetBufferInfo(Handle<HwTexture> handle, uint8_t level, uint16_t layer) noexcept
: handle(handle), level(level), layer(layer) {
TargetBufferInfo(Handle<HwTexture> handle, uint8_t const level, uint16_t const layer) noexcept
: handle(std::move(handle)), level(level), layer(layer) {
}
TargetBufferInfo(Handle<HwTexture> handle, uint8_t level) noexcept
TargetBufferInfo(Handle<HwTexture> handle, uint8_t const level) noexcept
: handle(handle), level(level) {
}
@@ -70,11 +74,11 @@ private:
TargetBufferInfo mInfos[MAX_SUPPORTED_RENDER_TARGET_COUNT];
public:
TargetBufferInfo const& operator[](size_t i) const noexcept {
TargetBufferInfo const& operator[](size_t const i) const noexcept {
return mInfos[i];
}
TargetBufferInfo& operator[](size_t i) noexcept {
TargetBufferInfo& operator[](size_t const i) noexcept {
return mInfos[i];
}

View File

@@ -39,18 +39,40 @@ public:
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
int getOSVersion() const noexcept override { return 0; }
/**
* Optionally initializes the Metal platform by acquiring resources necessary for rendering.
*
* This method attempts to acquire a Metal device and command queue, returning true if both are
* successfully obtained, or false otherwise. Typically, these objects are acquired when
* the Metal backend is initialized. This method allows clients to check for their availability
* earlier.
*
* Calling initialize() is optional and safe to do so multiple times. After initialize() returns
* true, subsequent calls will continue to return true but have no effect.
*
* initialize() must be called from the main thread.
*
* @returns true if the device and command queue have been successfully obtained; false
* otherwise.
*/
bool initialize() noexcept;
/**
* Obtain the preferred Metal device object for the backend to use.
*
* On desktop platforms, there may be multiple GPUs suitable for rendering, and this method is
* free to decide which one to use. On mobile systems with a single GPU, implementations should
* simply return the result of MTLCreateSystemDefaultDevice();
*
* createDevice is called by the Metal backend from the backend thread.
*/
virtual void createDevice(MetalDevice& outDevice) noexcept;
/**
* Create a command submission queue on the Metal device object.
*
* createCommandQueue is called by the Metal backend from the backend thread.
*
* @param device The device which was returned from createDevice()
*/
virtual void createCommandQueue(
@@ -60,6 +82,8 @@ public:
* Obtain a MTLCommandBuffer enqueued on this Platform's MTLCommandQueue. The command buffer is
* guaranteed to execute before all subsequent command buffers created either by Filament, or
* further calls to this method.
*
* createAndEnqueueCommandBuffer must be called from the main thread.
*/
void createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept;
@@ -68,6 +92,8 @@ public:
*
* Each frame rendered requires a CAMetalDrawable texture, which is presented on-screen at the
* completion of each frame. These are limited and provided round-robin style by the system.
*
* setDrawableFailureBehavior must be called from the main thread.
*/
enum class DrawableFailureBehavior : uint8_t {
/**

View File

@@ -27,8 +27,11 @@
#include <utils/Hash.h>
#include <utils/PrivateImplementation.h>
#include <cstddef>
#include <functional>
#include <tuple>
#include <unordered_set>
#include <string>
#include <stddef.h>
#include <stdint.h>

View File

@@ -17,6 +17,7 @@
#include <backend/Platform.h>
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <atomic>
#include <utility>

View File

@@ -19,6 +19,8 @@
#include "MetalContext.h"
#include <utils/Panic.h>
namespace filament {
namespace backend {

View File

@@ -721,15 +721,25 @@ const char* toString(DescriptorFlags flags) {
void MetalDriver::createDescriptorSetLayoutR(
Handle<HwDescriptorSetLayout> dslh, DescriptorSetLayout&& info) {
#if FILAMENT_METAL_DEBUG_LOG == 1
const char* labelStr = "";
std::visit([&labelStr](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, utils::CString> || std::is_same_v<T, utils::StaticString>) {
labelStr = arg.c_str();
}
}, info.label);
std::sort(info.bindings.begin(), info.bindings.end(),
[](const auto& a, const auto& b) { return a.binding < b.binding; });
DEBUG_LOG("createDescriptorSetLayoutR(dslh = %d, info = {\n", dslh.getId());
DEBUG_LOG("createDescriptorSetLayoutR(dslh = %d, info = { label = %s,\n", dslh.getId(),
labelStr);
for (size_t i = 0; i < info.bindings.size(); i++) {
DEBUG_LOG(" {binding = %d, type = %s, count = %d, stage = %s, flags = %s},\n",
info.bindings[i].binding, toString(info.bindings[i].type), info.bindings[i].count,
toString(info.bindings[i].stageFlags), toString(info.bindings[i].flags));
}
DEBUG_LOG("})\n");
#endif
construct_handle<MetalDescriptorSetLayout>(dslh, std::move(info));
}

View File

@@ -1382,6 +1382,9 @@ id<MTLArgumentEncoder> MetalDescriptorSetLayout::getArgumentEncoderSlow(id<MTLDe
break;
}
}
if (arguments.count == 0) {
return nil;
}
return [device newArgumentEncoderWithArguments:arguments];
}
@@ -1442,6 +1445,9 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
id<MTLArgumentEncoder> encoder =
layout->getArgumentEncoder(context.device, stage, textureTypes);
if (!encoder) {
return nil;
}
{
ScopedAllocationTimer timer("descriptor_set");

View File

@@ -20,18 +20,19 @@
#include <Metal/Metal.h>
#include "private/backend/Driver.h"
#include "backend/Program.h"
#include <backend/DriverEnums.h>
#include <backend/Program.h>
#include <utils/bitset.h>
#include <utils/FixedCapacityVector.h>
#include <memory>
#include <tsl/robin_map.h>
#include <utils/Hash.h>
#include <utils/Invocable.h>
#include <tsl/robin_map.h>
#include <memory>
namespace filament {
namespace backend {

View File

@@ -24,14 +24,22 @@
#import <Foundation/Foundation.h>
#include <atomic>
#include <mutex>
namespace filament::backend {
struct PlatformMetalImpl {
std::mutex mLock; // locks mDevice and mCommandQueue
id<MTLDevice> mDevice = nil;
id<MTLCommandQueue> mCommandQueue = nil;
// read form driver thread, read/written to from client thread
std::atomic<PlatformMetal::DrawableFailureBehavior> mDrawableFailureBehavior =
PlatformMetal::DrawableFailureBehavior::PANIC;
// These methods must be called with mLock held
void createDeviceImpl(MetalDevice& outDevice);
void createCommandQueueImpl(MetalDevice& device, MetalCommandQueue& outCommandQueue);
};
Platform* createDefaultMetalPlatform() {
@@ -48,7 +56,59 @@ Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::Dri
return MetalDriverFactory::create(this, driverConfig);
}
bool PlatformMetal::initialize() noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
MetalDevice device{};
pImpl->createDeviceImpl(device);
if (device.device == nil) {
return false;
}
MetalCommandQueue commandQueue{};
pImpl->createCommandQueueImpl(device, commandQueue);
if (commandQueue.commandQueue == nil) {
return false;
}
return true;
}
void PlatformMetal::createDevice(MetalDevice& outDevice) noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
pImpl->createDeviceImpl(outDevice);
}
void PlatformMetal::createCommandQueue(
MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
pImpl->createCommandQueueImpl(device, outCommandQueue);
}
void PlatformMetal::createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
id<MTLCommandBuffer> commandBuffer = [pImpl->mCommandQueue commandBuffer];
[commandBuffer enqueue];
outCommandBuffer.commandBuffer = commandBuffer;
}
void PlatformMetal::setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept {
pImpl->mDrawableFailureBehavior = behavior;
}
PlatformMetal::DrawableFailureBehavior PlatformMetal::getDrawableFailureBehavior() const noexcept {
return pImpl->mDrawableFailureBehavior;
}
// -------------------------------------------------------------------------------------------------
void PlatformMetalImpl::createDeviceImpl(MetalDevice& outDevice) {
if (mDevice) {
outDevice.device = mDevice;
return;
}
id<MTLDevice> result;
#if !defined(FILAMENT_IOS)
@@ -74,27 +134,17 @@ void PlatformMetal::createDevice(MetalDevice& outDevice) noexcept {
<< utils::io::endl;
outDevice.device = result;
mDevice = result;
}
void PlatformMetal::createCommandQueue(
MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept {
pImpl->mCommandQueue = [device.device newCommandQueue];
pImpl->mCommandQueue.label = @"Filament";
outCommandQueue.commandQueue = pImpl->mCommandQueue;
}
void PlatformMetal::createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept {
id<MTLCommandBuffer> commandBuffer = [pImpl->mCommandQueue commandBuffer];
[commandBuffer enqueue];
outCommandBuffer.commandBuffer = commandBuffer;
}
void PlatformMetal::setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept {
pImpl->mDrawableFailureBehavior = behavior;
}
PlatformMetal::DrawableFailureBehavior PlatformMetal::getDrawableFailureBehavior() const noexcept {
return pImpl->mDrawableFailureBehavior;
void PlatformMetalImpl::createCommandQueueImpl(MetalDevice& device, MetalCommandQueue& outCommandQueue) {
if (mCommandQueue) {
outCommandQueue.commandQueue = mCommandQueue;
return;
}
mCommandQueue = [device.device newCommandQueue];
mCommandQueue.label = @"Filament";
outCommandQueue.commandQueue = mCommandQueue;
}
} // namespace filament

View File

@@ -542,6 +542,13 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
} else if (strstr(renderer, "Intel")) {
// Intel GPU
bugs->vao_doesnt_store_element_array_buffer_binding = true;
if (strstr(renderer, "Mesa")) {
// Mesa Intel driver on Linux/Android
// Renderer of the form [Mesa Intel(R) HD Graphics 505 (APL 3)]
// b/405252622
bugs->disable_invalidate_framebuffer = true;
}
} else if (strstr(renderer, "PowerVR")) {
// PowerVR GPU
// On PowerVR (Rogue GE8320) glFlush doesn't seem to do anything, in particular,

View File

@@ -54,7 +54,7 @@ struct PlatformCocoaGLImpl {
CVOpenGLTextureCacheRef mTextureCache = nullptr;
std::unique_ptr<CocoaExternalImage::SharedGl> mExternalImageSharedGl;
void updateOpenGLContext(NSView *nsView, bool resetView, bool clearView);
struct ExternalImageCocoaGL : public Platform::ExternalImage {
struct ExternalImageCocoaGL final : public Platform::ExternalImage {
CVPixelBufferRef cvBuffer;
protected:
~ExternalImageCocoaGL() noexcept final;

View File

@@ -142,6 +142,10 @@ public:
return mPortabilitySubsetFeatures.imageView2DOn3DImage == VK_TRUE;
}
inline bool isUnifiedMemoryArchitecture() const noexcept {
return mIsUnifiedMemoryArchitecture;
}
private:
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
@@ -164,6 +168,7 @@ private:
bool mDebugUtilsSupported = false;
bool mLazilyAllocatedMemorySupported = false;
bool mProtectedMemorySupported = false;
bool mIsUnifiedMemoryArchitecture = false;
fvkutils::VkFormatList mDepthStencilFormats;
fvkutils::VkFormatList mBlittableDepthStencilFormats;

View File

@@ -631,6 +631,21 @@ fvkutils::VkFormatList findBlittableDepthStencilFormats(VkPhysicalDevice device)
return ret;
}
/**
* Check if the GPU has a unified memory architecture.
*/
bool hasUnifiedMemoryArchitecture(VkPhysicalDeviceMemoryProperties memoryProperties) noexcept {
// Try to identify if the platform is running on a Unified Memory Architecture by inspecting the
// memory heap flags, if they are all VK_MEMORY_HEAP_DEVICE_LOCAL_BIT it's UMA, otherwise not
// enough information to make a decision, so default to false.
for (uint32_t i = 0; i < memoryProperties.memoryHeapCount; ++i) {
if ((memoryProperties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) == 0) {
return false;
}
}
return true;
}
}// anonymous namespace
using SwapChainPtr = VulkanPlatform::SwapChainPtr;
@@ -864,6 +879,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
}
}
context.mIsUnifiedMemoryArchitecture = hasUnifiedMemoryArchitecture(context.mMemoryProperties);
#ifdef NDEBUG
// If we are in release build, we should not have turned on debug extensions
FILAMENT_CHECK_POSTCONDITION(!context.mDebugUtilsSupported && !context.mDebugMarkersSupported)

View File

@@ -19,6 +19,7 @@
#include <backend/DriverEnums.h>
#include <utils/BitmaskEnum.h>
#include <utils/Panic.h>
#include <webgpu/webgpu_cpp.h>
@@ -502,7 +503,6 @@ size_t WebGPUDescriptorSet::countEntitiesWithDynamicOffsets() const {
return mEntriesByBindingWithDynamicOffsets.count();
}
// From createTextureR
WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
wgpu::Device const& device) noexcept {
@@ -516,6 +516,7 @@ WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat forma
// First, the texture aspect, starting with the defaults/basic configuration
mUsage = fToWGPUTextureUsage(usage);
mFormat = fToWGPUTextureFormat(format);
mAspect = fToWGPUTextureViewAspect(usage, format);
wgpu::TextureDescriptor textureDescriptor{
.label = getUserTextureLabel(target),
.usage = mUsage,
@@ -560,13 +561,12 @@ WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat forma
mTexView = makeTextureView(0, levels, target);
}
// From createTextureViewR
WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept {
mTexture = src->mTexture;
mTexView = makeTextureView(baseLevel, levelCount, target);
}
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(const TextureUsage& fUsage) {
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(TextureUsage const& fUsage) {
wgpu::TextureUsage retUsage = wgpu::TextureUsage::None;
// Basing this mapping off of VulkanTexture.cpp's getUsage func and suggestions from Gemini
@@ -614,8 +614,9 @@ wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(const TextureUsage& fUsage)
// PROTECTED
return retUsage;
}
wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(const TextureFormat& fUsage) {
switch (fUsage) {
wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(TextureFormat const& fFormat) {
switch (fFormat) {
case filament::backend::TextureFormat::R8:
return wgpu::TextureFormat::R8Unorm;
case filament::backend::TextureFormat::R8_SNORM:
@@ -854,24 +855,57 @@ wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(const TextureFormat& fUsag
}
}
wgpu::TextureAspect WGPUTexture::fToWGPUTextureViewAspect(TextureUsage const& fUsage,
TextureFormat const& fFormat) {
const bool isDepth = any(fUsage & TextureUsage::DEPTH_ATTACHMENT);
const bool isStencil = any(fUsage & TextureUsage::STENCIL_ATTACHMENT);
const bool isColor = any(fUsage & TextureUsage::COLOR_ATTACHMENT);
const bool isSample = (fUsage == TextureUsage::SAMPLEABLE);
if (isDepth && !isColor && !isStencil) {
return wgpu::TextureAspect::DepthOnly;
}
if (isStencil && !isColor && !isDepth) {
return wgpu::TextureAspect::StencilOnly;
}
if (fFormat == filament::backend::TextureFormat::DEPTH32F ||
fFormat == filament::backend::TextureFormat::DEPTH24 ||
fFormat == filament::backend::TextureFormat::DEPTH16) {
return wgpu::TextureAspect::DepthOnly;
}
if (fFormat == filament::backend::TextureFormat::STENCIL8) {
return wgpu::TextureAspect::StencilOnly;
}
if (fFormat == filament::backend::TextureFormat::DEPTH24_STENCIL8 ||
fFormat == filament::backend::TextureFormat::DEPTH32F_STENCIL8) {
if (isSample) {
return wgpu::TextureAspect::DepthOnly;
}
}
return wgpu::TextureAspect::All;
}
wgpu::TextureView WGPUTexture::makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount,
SamplerType target) {
// starting with the defaults/basic configuration
wgpu::TextureViewDescriptor textureViewDescriptor{
.label = getUserTextureViewLabel(target),
.format = mFormat,
// dimension depends on target and is set below
.baseMipLevel = baseLevel,
.mipLevelCount = levelCount,
// baseArrayLayer is required, making a guess
// TODO: check if this baseArrayLayer assumption is correct
.baseArrayLayer = 0,
.arrayLayerCount = mArrayLayerCount,
// Have not found an analog to aspect in other drivers, but ALL should be unrestrictive.
// TODO Can we make this better?
.aspect = wgpu::TextureAspect::All,
.aspect = mAspect,
.usage = mUsage
};
// adjust for specific cases
switch (target) {
case SamplerType::SAMPLER_2D:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;

View File

@@ -74,8 +74,8 @@ public:
private:
// TODO: can we do better in terms on heap management.
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout {};
std::vector<std::vector<wgpu::VertexAttribute>> mAttributes {};
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout{};
std::vector<std::vector<wgpu::VertexAttribute>> mAttributes{};
};
struct WGPUVertexBuffer : public HwVertexBuffer {
@@ -145,8 +145,8 @@ public:
[[nodiscard]] uint32_t const* setDynamicOffsets(uint32_t const* offsets);
[[nodiscard]] bool getIsLocked() const { return mBindGroup != nullptr; }
[[nodiscard]] size_t countEntitiesWithDynamicOffsets() const;
private:
private:
static wgpu::Buffer sDummyUniformBuffer;
static wgpu::Texture sDummyTexture;
static wgpu::TextureView sDummyTextureView;
@@ -161,10 +161,10 @@ private:
// Also storing the wgpu ObjectBase takes care of ownership challenges in theory
wgpu::BindGroupLayout mLayout = nullptr;
static constexpr uint8_t INVALID_INDEX = MAX_DESCRIPTOR_COUNT + 1;
std::array<uint8_t, MAX_DESCRIPTOR_COUNT> mEntryIndexByBinding {};
std::array<uint8_t, MAX_DESCRIPTOR_COUNT> mEntryIndexByBinding{};
std::vector<wgpu::BindGroupEntry> mEntriesSortedByBinding;
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingWithDynamicOffsets {};
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingAdded {};
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingWithDynamicOffsets{};
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingAdded{};
std::vector<uint32_t> mDynamicOffsets;
wgpu::BindGroup mBindGroup = nullptr;
};
@@ -177,11 +177,14 @@ public:
WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
const wgpu::Texture& getTexture() const { return mTexture; }
const wgpu::TextureView& getTexView() const { return mTexView; }
[[nodiscard]] const wgpu::Texture& getTexture() const { return mTexture; }
[[nodiscard]] const wgpu::TextureView& getTexView() const { return mTexView; }
// Public to allow checking for support of a texture format
static wgpu::TextureFormat fToWGPUTextureFormat(const filament::backend::TextureFormat& fUsage);
static wgpu::TextureFormat fToWGPUTextureFormat(
filament::backend::TextureFormat const& fFormat);
static wgpu::TextureAspect fToWGPUTextureViewAspect(
filament::backend::TextureUsage const& fUsage,
filament::backend::TextureFormat const& fFormat);
private:
wgpu::TextureView makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount,
@@ -192,9 +195,10 @@ private:
wgpu::Texture mTexture = nullptr;
wgpu::TextureUsage mUsage = wgpu::TextureUsage::None;
wgpu::TextureFormat mFormat = wgpu::TextureFormat::Undefined;
wgpu::TextureAspect mAspect = wgpu::TextureAspect::Undefined;
uint32_t mArrayLayerCount = 1;
wgpu::TextureView mTexView = nullptr;
wgpu::TextureUsage fToWGPUTextureUsage(const filament::backend::TextureUsage& fUsage);
wgpu::TextureUsage fToWGPUTextureUsage(filament::backend::TextureUsage const& fUsage);
};
struct WGPURenderPrimitive : public HwRenderPrimitive {
@@ -252,7 +256,7 @@ private:
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
math::uint2 attachmentSize = {};
std::vector<wgpu::RenderPassColorAttachment> colorAttachments {};
std::vector<wgpu::RenderPassColorAttachment> colorAttachments{};
};
}// namespace filament::backend

View File

@@ -45,6 +45,7 @@ namespace test {
Backend BackendTest::sBackend = Backend::NOOP;
OperatingSystem BackendTest::sOperatingSystem = OperatingSystem::OTHER;
bool BackendTest::sIsMobilePlatform = false;
std::vector<std::string> BackendTest::sFailedImages;
void BackendTest::init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform) {
sBackend = backend;
@@ -63,11 +64,12 @@ BackendTest::~BackendTest() {
flushAndWait();
mImageExpectations->evaluate();
// Note: Don't terminate the driver for OpenGL, as it wipes away the context and removes the buffer from the screen.
if (sBackend == Backend::OPENGL) {
return;
if (sBackend != Backend::OPENGL) {
driver->terminate();
delete driver;
}
driver->terminate();
delete driver;
recordFailedImages();
}
void BackendTest::initializeDriver() {
@@ -167,8 +169,24 @@ bool BackendTest::matchesEnvironment(OperatingSystem operatingSystem) {
return sOperatingSystem == operatingSystem;
}
bool BackendTest::matchesEnvironment(OperatingSystem operatingSystem, Backend backend) {
return matchesEnvironment(operatingSystem) && matchesEnvironment(backend);
void BackendTest::markImageAsFailure(std::string failedImageName) {
sFailedImages.emplace_back(std::move(failedImageName));
}
void BackendTest::recordFailedImages() {
if (!sFailedImages.empty()) {
std::string failedImages;
for (auto& failedTestImageName: sFailedImages) {
if (failedImages.empty()) {
failedImages = failedTestImageName;
} else {
failedImages.append(",");
failedImages.append(failedTestImageName);
}
}
RecordProperty("FailedImages", failedImages);
}
sFailedImages.clear();
}
class Environment : public ::testing::Environment {

View File

@@ -38,6 +38,9 @@ public:
static OperatingSystem sOperatingSystem;
static bool sIsMobilePlatform;
// Takes the name of the image that wasn't correct, without the .png suffix
static void markImageAsFailure(std::string failedImageName);
protected:
BackendTest();
@@ -73,8 +76,13 @@ protected:
static bool matchesEnvironment(Backend backend);
static bool matchesEnvironment(OperatingSystem operatingSystem);
static bool matchesEnvironment(OperatingSystem operatingSystem, Backend backend);
private:
// Adds all the images that failed an ImageExpectation to the XML metadata for the current tests
// case. Add --gtest_output=xml as a command line argument to generate a test_detail.xml file in
// the directory where the tests are run.
static void recordFailedImages();
static std::vector<std::string> sFailedImages;
filament::backend::Driver* driver = nullptr;
filament::backend::CommandBufferQueue commandBufferQueue;

View File

@@ -21,6 +21,7 @@
#include "utils/Hash.h"
#include <fstream>
#include "BackendTest.h"
#include "backend/PixelBufferDescriptor.h"
#include "private/backend/DriverApi.h"
@@ -32,6 +33,8 @@
#endif
namespace test {
ScreenshotParams::ScreenshotParams(int width, int height, std::string fileName,
uint32_t expectedHash, bool isSrgb)
: mWidth(width),
@@ -80,6 +83,10 @@ std::string ScreenshotParams::expectedFilePath() const {
return absl::StrFormat("%s/%s", expectedDirectoryPath(), expectedFileName());
}
const std::string ScreenshotParams::filePrefix() const {
return mFileName;
}
ImageExpectation::ImageExpectation(const char* fileName, int lineNumber,
filament::backend::DriverApi& api, ScreenshotParams params,
filament::backend::RenderTargetHandle renderTarget)
@@ -113,7 +120,11 @@ void ImageExpectation::compareImage() const {
#ifndef FILAMENT_IOS
LoadedPng loadedImage(mParams.expectedFilePath());
uint32_t loadedImageHash = loadedImage.hash();
EXPECT_THAT(actualHash, testing::Eq(loadedImageHash)) << mParams.expectedFileName();
auto compareToImageMatcher = testing::Eq(loadedImageHash);
if (!testing::Matches(compareToImageMatcher)(actualHash)) {
BackendTest::markImageAsFailure(mParams.filePrefix());
}
EXPECT_THAT(actualHash, compareToImageMatcher) << mParams.expectedFileName();
#endif
// For builds that can't load PNGs (currently iOS only) use the expected hash.
EXPECT_THAT(actualHash, testing::Eq(mParams.expectedHash())) << mParams.expectedFileName();
@@ -200,6 +211,10 @@ uint32_t RenderTargetDump::hash() const {
return mInternal->hash();
}
const std::vector<unsigned char>& RenderTargetDump::bytes() const {
return mInternal->bytes;
}
bool RenderTargetDump::bytesFilled() const {
return mInternal->bytesFilled;
}
@@ -237,3 +252,5 @@ uint32_t LoadedPng::hash() const {
const std::vector<unsigned char>& LoadedPng::bytes() const {
return mBytes;
}
} // namespace test

View File

@@ -35,6 +35,8 @@ do { \
screenshotParams); \
} while (0)
namespace test {
/**
* Stores user-provided configuration values for an image expectation
*/
@@ -54,6 +56,7 @@ public:
static std::string expectedDirectoryPath();
std::string expectedFileName() const;
std::string expectedFilePath() const;
const std::string filePrefix() const;
private:
int mWidth;
@@ -82,6 +85,12 @@ public:
* @return The hash of the stored bytes.
*/
uint32_t hash() const;
/**
* Gets the bytes of the render target. The hash should usually be preferable for comparisons
* but this is available for debugging.
* @return The stored bytes.
*/
const std::vector<unsigned char>& bytes() const;
/**
* Thread safe as this is backed by an atomic.
* Once this returns true it will never return false.
@@ -153,4 +162,6 @@ private:
std::vector<std::unique_ptr<ImageExpectation>> mExpectations;
};
} // namespace test
#endif //TNT_IMAGE_EXPECTATIONS_H

View File

@@ -17,7 +17,6 @@
#include "ShaderGenerator.h"
#include <GlslangToSpv.h>
#include <SPVRemapper.h>
#include <spirv_glsl.hpp>
#include <spirv_msl.hpp>

View File

@@ -32,13 +32,24 @@ do {
} \
} while (false)
#define NONFATAL_FAIL_IF(skipEnvironment, rationale) \
do { \
SkipEnvironment skip(skipEnvironment); \
if (skip.matches()) { \
ADD_FAILURE() \
<< "Failing test as the " << skip.describe() << "\n" \
<< " This test has a known failure where " \
<< rationale; \
} \
} while (false)
#define FAIL_IF(skipEnvironment, rationale) \
do { \
SkipEnvironment skip(skipEnvironment); \
if (skip.matches()) { \
GTEST_FAIL() \
<< "Failing test as the " << skip.describe() << "\n" \
<< " This test should be able to succeed but it needs to fail early because" \
<< " This test should be able to succeed but it needs to fail early because " \
<< rationale; \
} \
} while (false)

View File

@@ -1,62 +1,153 @@
import os, shutil, argparse, typing
def match_sufffix(file_name: str, suffix: str, accepted_prefixes: typing.List[str]) -> str:
"""
Check if the file name is one of the searched for ones with the given suffix and if so return it.
:param accepted_prefixes: If None accepts any prefix
:return: file_name with the suffix removed or "" if it doesn't match. This does mean a string that
is just the suffix is considered to not match as it will return the empty string.
"""
if file_name.endswith(suffix):
prefix = file_name.removesuffix(suffix)
if accepted_prefixes is None or prefix in accepted_prefixes:
return prefix
return ""
import os, shutil, argparse, typing, xml.etree.ElementTree, subprocess, platform
def replace_file_names(path: str, removed: str, replacement: str = "", output_path: str = "",
prefixes: typing.List[str] = None):
if not output_path:
output_path = path
for file_name in os.listdir(path=path):
prefix = match_sufffix(file_name, removed, prefixes)
if prefix:
# Remove the prefix from the list so that prefixes is the list of intended but not yet found
# files.
if prefixes is not None:
prefixes.remove(prefix)
new_file_name = prefix + replacement
new_file_path = os.path.join(output_path, new_file_name)
old_file_path = os.path.join(path, file_name)
print(f'{old_file_path} to {new_file_path}')
shutil.move(old_file_path, new_file_path)
if prefixes is not None:
for unfound_prefix in prefixes:
print(f'Failed to find {unfound_prefix}_actual.png')
class TestResults(object):
ACTUAL_SUFFIX = '_actual.png'
EXPECTED_SUFFIX = '.png'
def __init__(self, results_directory: str, source_expected_directory: str):
self.results_directory = results_directory
self.actual_directory = os.path.join(self.results_directory, 'images', 'actual_images')
self.expected_directory = os.path.join(self.results_directory, 'images', 'expected_images')
self.source_expected_directory = source_expected_directory
def get_latest_failed_images(self) -> typing.List[str]:
failed_images = []
xml_tree = xml.etree.ElementTree.parse(
os.path.join(self.results_directory, 'test_detail.xml'))
testsuites = xml_tree.getroot()
for testsuite in testsuites.findall('testsuite'):
for testcase in testsuite.findall('testcase'):
for properties in testcase.findall('properties'):
for property in properties.findall('property'):
if property.get('name') == 'FailedImages':
failed_images.extend(property.get('value').split(','))
return failed_images
def handle_failed_image(self, failed_image):
self.show_images(failed_image)
print(f'Update {failed_image}\'s expected image? y/n')
while True:
user_input = input()
if user_input == 'y':
self.move_actual_to_source([failed_image])
break
elif user_input == 'n':
break
def handle_all_failed_images(self):
for failed_image in self.get_latest_failed_images():
self.handle_failed_image(failed_image)
def show_images(self, failed_image):
# TODO: Test more on non-mac systems
open_command: str
os_name = platform.system().lower()
if 'windows' in os_name:
open_command = 'start'
elif 'osx' in os_name or 'darwin' in os_name:
open_command = 'open'
else:
open_command = 'xdg-open'
subprocess.run(
[open_command,
os.path.join(self.actual_directory, failed_image + TestResults.ACTUAL_SUFFIX)])
subprocess.run(
[open_command,
os.path.join(self.expected_directory, failed_image + TestResults.EXPECTED_SUFFIX)])
def move_actual_to_source(self, file_prefixes: typing.List[str]):
replace_file_names(path=self.actual_directory, removed=TestResults.ACTUAL_SUFFIX,
replacement=TestResults.EXPECTED_SUFFIX,
output_path=self.source_expected_directory, prefixes=file_prefixes)
def batch_move(self, prefixes: typing.Optional[typing.List[str]] = None):
replace_file_names(path=self.actual_directory, removed=TestResults.ACTUAL_SUFFIX,
replacement=TestResults.EXPECTED_SUFFIX,
output_path=self.source_expected_directory, prefixes=prefixes)
def match_suffix(file_name: str, suffix: str, accepted_prefixes: typing.List[str]) -> str:
"""
Check if the file name is one of the searched for ones with the given suffix and if so return
it.
:param accepted_prefixes: If None accepts any prefix
:return: file_name with the suffix removed or "" if it doesn't match. This does mean a string
that is just the suffix is considered to not match as it will return the empty string.
"""
if file_name.endswith(suffix):
prefix = file_name.removesuffix(suffix)
if accepted_prefixes is None or prefix in accepted_prefixes:
return prefix
return ''
def replace_file_names(path: str, removed: str, replacement: str = '', output_path: str = '',
prefixes: typing.Optional[typing.List[str]] = None):
if not output_path:
output_path = path
for file_name in os.listdir(path=path):
prefix = match_suffix(file_name, removed, prefixes)
if prefix:
# Remove the prefix from the list so that prefixes is the list of intended but not yet
# found files.
if prefixes is not None:
prefixes.remove(prefix)
new_file_name = prefix + replacement
new_file_path = os.path.join(output_path, new_file_name)
old_file_path = os.path.join(path, file_name)
print(f'{old_file_path} to {new_file_path}')
shutil.copyfile(old_file_path, new_file_path)
if prefixes is not None:
for unfound_prefix in prefixes:
print(f'Failed to find {unfound_prefix}_actual.png')
if __name__ == "__main__":
parser = argparse.ArgumentParser(prog='Backend Test File Renamer',
description='Moves actual generated test images to the expected '
'images directory, to update the test requirements. '
'test_cases accepts multiple arguments that should '
'be the name of the expected image file without the '
'.png suffix. Also --all can be passed to copy all '
'images.\n'
'Remember to sync CMake after running this to move '
'the new expected images to the binary directory.')
parser.add_argument('-i', '--input_path')
parser.add_argument('-o', '--output_path', default="./expected_images")
parser.add_argument('-t', '--test_cases', action='extend', nargs='*')
parser.add_argument('-a', '--all', action='store_true')
parser = argparse.ArgumentParser(prog='Backend Test File Renamer',
description='Moves actual generated test images to the '
'expected images directory, to update the test '
'requirements. test_cases accepts multiple '
'arguments that should be the name of the '
'expected image file without the .png suffix. '
'Also --all can be passed to copy all images.\n'
'Remember to sync CMake after running this to '
'move the new expected images to the binary '
'directory.')
parser.add_argument('-r', '--results_path',
help='The path with the generated images directory, which should be where '
'the test binary was run.')
parser.add_argument('-s', '--source_expected_path', default="./expected_images",
help='The directory that updated expected images should be written to, '
'which should be the source directory copy.')
# The mutually exclusive options for how to process the actual images
parser.add_argument('-b', '--batch', action='extend', nargs='*',
help='If true copy all actual images to the source expected image '
'directory.')
parser.add_argument('-a', '--all', action='store_true',
help='If true, visually compare all generated images.')
parser.add_argument('-t', '--tests', action='store_true',
help='If true use a test_detail.xml file that exists in the results_path '
'directory to visually compare all images that failed a test.')
parser.add_argument('-c', '--compare', action='extend', nargs='*',
help='A list of image names to visually compare (without the .png suffix).')
args = parser.parse_args()
input_path = "."
if args.input_path:
input_path = args.input_path
args = parser.parse_args()
if not args.results_path:
raise AssertionError("No result path provided")
results_path = args.results_path
prefixes = args.test_cases
if args.all:
prefixes = None
results = TestResults(results_directory=results_path,
source_expected_directory=args.source_expected_path)
replace_file_names(path=input_path, output_path=args.output_path, removed="_actual.png",
replacement=".png", prefixes=prefixes)
if args.all:
results.batch_move()
elif args.tests:
results.handle_all_failed_images()
elif args.compare:
for file_prefix in args.compare:
results.show_images(file_prefix)
else:
results.batch_move(args.batch)

View File

@@ -260,6 +260,8 @@ TEST_F(BlitTest, ColorMinify) {
}
TEST_F(BlitTest, ColorResolve) {
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Nothing is drawn, see b/417229577");
auto& api = getDriverApi();
constexpr int kSrcTexWidth = 256;
@@ -490,7 +492,7 @@ TEST_F(BlitTest, BlitRegion) {
}
TEST_F(BlitTest, BlitRegionToSwapChain) {
FAIL_IF(Backend::VULKAN, "Crashes due to not finding color attachment");
FAIL_IF(Backend::VULKAN, "Crashes due to not finding color attachment, see b/417481493");
auto& api = getDriverApi();
mCleanup.addPostCall([&]() { executeCommands(); });

View File

@@ -20,6 +20,7 @@
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
namespace test {
@@ -159,6 +160,8 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
// This test renders two triangles in two separate draw calls. Between the draw calls, a uniform
// buffer object is partially updated.
TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"All values including alpha are written as 0, see b/417254943");
auto& api = getDriverApi();
Cleanup cleanup(api);

View File

@@ -26,6 +26,7 @@ namespace test {
TEST_F(BackendTest, FrameScheduledCallback) {
SKIP_IF(Backend::OPENGL, "Frame callbacks are unsupported in OpenGL");
SKIP_IF(Backend::VULKAN, "Frame callbacks are unsupported in Vulkan, see b/417254479");
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -85,6 +86,7 @@ TEST_F(BackendTest, FrameScheduledCallback) {
TEST_F(BackendTest, FrameCompletedCallback) {
SKIP_IF(Backend::OPENGL, "Frame callbacks are unsupported in OpenGL");
SKIP_IF(Backend::VULKAN, "Frame callbacks are unsupported in Vulkan, see b/417254479");
auto& api = getDriverApi();
Cleanup cleanup(api);

View File

@@ -99,6 +99,8 @@ struct MaterialParams {
// The problems are caused by both uploading and rendering into the same texture, since the OpenGL
// backend's readPixels does not work correctly with textures that have image data uploaded.
TEST_F(BackendTest, FeedbackLoops) {
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Image is unexpectedly darker, see b/417226296");
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL),
"OpenGL image is upside down due to readPixels failing for texture with uploaded image "
"data");

View File

@@ -212,7 +212,7 @@ static SamplerFormat getSamplerFormat(TextureFormat textureFormat) {
}
TEST_F(LoadImageTest, UpdateImage2D) {
FAIL_IF(Backend::VULKAN, "Multiple test cases crash");
FAIL_IF(Backend::VULKAN, "Multiple test cases crash, see b/417481434");
// All of these test cases should result in the same rendered image, and thus the same hash.
static const uint32_t expectedHash = 3644679986;
@@ -485,6 +485,9 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
}
TEST_F(LoadImageTest, UpdateImage3D) {
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Checkerboard not drawn, possibly due to using wrong z value of 3d texture, "
"see b/417254499");
auto& api = getDriverApi();
Cleanup cleanup(api);
api.startCapture();

View File

@@ -81,7 +81,8 @@ void main() {
TEST_F(BackendTest, PushConstants) {
SKIP_IF(Backend::OPENGL, "Push constants not supported on OpenGL");
FAIL_IF(Backend::VULKAN, "Crashing due to no program set when setting push constants");
FAIL_IF(Backend::VULKAN,
"Crashing due to no program set when setting push constants, see b/417477740");
auto& api = getDriverApi();

View File

@@ -20,6 +20,7 @@
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
@@ -74,6 +75,8 @@ public:
};
TEST_F(ReadPixelsTest, ReadPixels) {
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Two cases fail, see b/417255941 and b/417255943");
// These test scenarios use a known hash of the result pixel buffer to decide pass / fail,
// asserting an exact pixel-for-pixel match. So far, rendering on macOS and iPhone have had
// deterministic results. Take this test with a grain of salt, however, as other platform / GPU

View File

@@ -20,6 +20,7 @@
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
@@ -30,6 +31,8 @@ using namespace filament;
using namespace filament::backend;
TEST_F(BackendTest, ScissorViewportRegion) {
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Affected area in wrong corner, see b/417229118");
auto& api = getDriverApi();
constexpr int kSrcTexWidth = 1024;

View File

@@ -117,6 +117,8 @@ public:
};
TEST_F(BasicStencilBufferTest, StencilBuffer) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Stencil not supported, see b/417230776");
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -139,6 +141,8 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
}
TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Stencil not supported, see b/417230776");
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -162,6 +166,8 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
}
TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Stencil not supported, see b/417230776");
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL), "Stencil isn't applied");
auto& api = getDriverApi();
Cleanup cleanup(api);

View File

@@ -21,7 +21,6 @@
#include <backend/CallbackHandler.h>
#include <backend/DriverEnums.h>
#include <backend/PresentCallable.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
@@ -35,7 +34,7 @@ class Engine;
/**
* A swap chain represents an Operating System's *native* renderable surface.
*
* Typically it's a native window or a view. Because a SwapChain is initialized from a
* Typically, it's a native window or a view. Because a SwapChain is initialized from a
* native object, it is given to filament as a `void *`, which must be of the proper type
* for each platform filament is running on.
*
@@ -158,7 +157,7 @@ public:
/**
* Requests a SwapChain with an alpha channel.
*/
static const uint64_t CONFIG_TRANSPARENT = backend::SWAP_CHAIN_CONFIG_TRANSPARENT;
static constexpr uint64_t CONFIG_TRANSPARENT = backend::SWAP_CHAIN_CONFIG_TRANSPARENT;
/**
* This flag indicates that the swap chain may be used as a source surface
@@ -168,13 +167,13 @@ public:
* @see
* Renderer.copyFrame()
*/
static const uint64_t CONFIG_READABLE = backend::SWAP_CHAIN_CONFIG_READABLE;
static constexpr uint64_t CONFIG_READABLE = backend::SWAP_CHAIN_CONFIG_READABLE;
/**
* Indicates that the native X11 window is an XCB window rather than an XLIB window.
* This is ignored on non-Linux platforms and in builds that support only one X11 API.
*/
static const uint64_t CONFIG_ENABLE_XCB = backend::SWAP_CHAIN_CONFIG_ENABLE_XCB;
static constexpr uint64_t CONFIG_ENABLE_XCB = backend::SWAP_CHAIN_CONFIG_ENABLE_XCB;
/**
* Indicates that the native window is a CVPixelBufferRef.
@@ -186,7 +185,7 @@ public:
* Filament. Filament will call CVPixelBufferRetain during Engine::createSwapChain, and
* CVPixelBufferRelease when the swap chain is destroyed.
*/
static const uint64_t CONFIG_APPLE_CVPIXELBUFFER =
static constexpr uint64_t CONFIG_APPLE_CVPIXELBUFFER =
backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER;
/**
@@ -303,6 +302,7 @@ public:
*
* @param handler Handler to dispatch the callback or nullptr for the default handler.
* @param callback Callback called when the frame is scheduled.
* @param flags
*
* @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other
* backends ignore the callback (which will never be called) and proceed normally.
@@ -314,7 +314,7 @@ public:
FrameScheduledCallback&& callback = {}, uint64_t flags = 0);
/**
* Returns whether or not this SwapChain currently has a FrameScheduledCallback set.
* Returns whether this SwapChain currently has a FrameScheduledCallback set.
*
* @return true, if the last call to setFrameScheduledCallback set a callback
*

View File

@@ -16,13 +16,22 @@
#include "AtlasAllocator.h"
#include <utils/compiler.h>
#include <utils/algorithm.h>
#include <utils/debug.h>
#include <utils/QuadTree.h>
#include <algorithm>
#include <utility>
#include <stddef.h>
#include <stdint.h>
namespace filament {
using namespace utils;
static inline constexpr std::pair<uint8_t, uint8_t> unmorton(uint16_t const m) noexcept {
static constexpr std::pair<uint8_t, uint8_t> unmorton(uint16_t const m) noexcept {
uint32_t r = (m | (uint32_t(m) << 15u)) & 0x55555555u;
r = (r | (r >> 1u)) & 0x33333333u;
r = (r | (r >> 2u)) & 0x0f0f0f0fu;
@@ -165,8 +174,8 @@ AtlasAllocator::NodeId AtlasAllocator::allocateInLayer(size_t const maxHeight) n
NodeId found{ -1, 0 };
QuadTree::traverse(candidate.l, candidate.code,
[this, n, &found](NodeId const& curr) -> QuadTree::TraversalResult {
size_t const i = index(curr.l, curr.code);
Node& node = mQuadTree[i];
size_t const j = index(curr.l, curr.code);
Node& node = mQuadTree[j];
if (curr.l == n) {
found = curr;
assert_invariant(!node.hasChildren());

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.59.4"
spec.version = "1.60.0"
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.59.4/filament-v1.59.4-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.60.0/filament-v1.60.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -102,7 +102,8 @@ project in Xcode to see changes take effect.
## Building iOS Samples with ASan / UBSan
1. Turn on ASan / UBSan in Filament's top-level CMakeLists.txt by uncommenting the following line:
1. Turn on ASan / UBSan in Filament's top-level CMakeLists.txt by passing
`-DFILAMENT_ENABLE_ASAN_UBSAN=1` to trigger the following line:
```
set(EXTRA_SANITIZE_OPTIONS "-fsanitize=undefined -fsanitize=address")

View File

@@ -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 = 59;
static constexpr size_t MATERIAL_VERSION = 60;
/**
* Supported shading models

View File

@@ -52,6 +52,7 @@ public:
using Precision = backend::Precision;
using SamplerParams = backend::SamplerParams;
using Binding = backend::descriptor_binding_t;
using ShaderStageFlags = backend::ShaderStageFlags;
struct SamplerInfo { // NOLINT(cppcoreguidelines-pro-type-member-init)
utils::CString name; // name of this sampler
@@ -61,6 +62,7 @@ public:
Format format; // format of this sampler
Precision precision; // precision of this sampler
bool multisample; // multisample capable
ShaderStageFlags stages; // stages the sampler can be accessed from
};
using SamplerInfoList = utils::FixedCapacityVector<SamplerInfo>;
@@ -82,6 +84,8 @@ public:
Format format; // format of this sampler
Precision precision; // precision of this sampler
bool multisample = false; // multisample capable
ShaderStageFlags stages =
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS; // shader stages using this sampler
};
// Give a name to this sampler interface block
@@ -91,8 +95,8 @@ public:
// Add a sampler
Builder& add(std::string_view samplerName, Binding binding, Type type, Format format,
Precision precision = Precision::MEDIUM,
bool multisample = false) noexcept;
Precision precision = Precision::MEDIUM, bool multisample = false,
ShaderStageFlags stages = ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) noexcept;
// Add multiple samplers
Builder& add(std::initializer_list<ListEntry> list) noexcept;

View File

@@ -49,13 +49,13 @@ SamplerInterfaceBlock::Builder::stageFlags(backend::ShaderStageFlags stageFlags)
return *this;
}
SamplerInterfaceBlock::Builder& SamplerInterfaceBlock::Builder::add(
std::string_view samplerName, Binding binding, Type type, Format format,
Precision precision, bool multisample) noexcept {
SamplerInterfaceBlock::Builder& SamplerInterfaceBlock::Builder::add(std::string_view samplerName,
Binding binding, Type type, Format format, Precision precision, bool multisample,
ShaderStageFlags stages) noexcept {
mEntries.push_back({
{ samplerName.data(), samplerName.size() }, // name
{ }, // uniform name
binding, type, format, precision, multisample });
binding, type, format, precision, multisample, stages });
return *this;
}
@@ -66,7 +66,7 @@ SamplerInterfaceBlock SamplerInterfaceBlock::Builder::build() {
SamplerInterfaceBlock::Builder& SamplerInterfaceBlock::Builder::add(
std::initializer_list<ListEntry> list) noexcept {
for (auto& e : list) {
add(e.name, e.binding, e.type, e.format, e.precision, e.multisample);
add(e.name, e.binding, e.type, e.format, e.precision, e.multisample, e.stages);
}
return *this;
}
@@ -91,6 +91,7 @@ SamplerInterfaceBlock::SamplerInterfaceBlock(Builder const& builder) noexcept
size_t const i = std::distance(builder.mEntries.data(), &e);
SamplerInfo& info = samplersInfoList[i];
info = e;
info.stages &= builder.mStageFlags;
info.uniformName = generateUniformName(mName.c_str(), e.name.c_str());
infoMap[{ info.name.data(), info.name.size() }] = i; // info.name.c_str() guaranteed constant
}

View File

@@ -56,6 +56,7 @@ set(PRIVATE_HDRS
src/MetalArgumentBuffer.h
src/ShaderMinifier.h
src/SpirvFixup.h
src/SpirvRemapWrapper.h
src/sca/ASTHelpers.h
src/sca/GLSLTools.h
src/sca/builtinResource.h)
@@ -71,7 +72,8 @@ set(SRCS
src/sca/GLSLTools.cpp
src/GLSLPostProcessor.cpp
src/ShaderMinifier.cpp
src/SpirvFixup.cpp)
src/SpirvFixup.cpp
src/SpirvRemapWrapper.cpp)
# ==================================================================================================
# Include and target definitions

View File

@@ -35,6 +35,7 @@ using OutputTarget = MaterialBuilder::OutputTarget;
using OutputQualifier = MaterialBuilder::VariableQualifier;
using OutputType = MaterialBuilder::OutputType;
using ConstantType = MaterialBuilder::ConstantType;
using ShaderStageType = MaterialBuilder::ShaderStageFlags;
// Convenience methods to convert std::string to Enum and also iterate over Enum values.
class Enums {
@@ -79,6 +80,7 @@ private:
static std::unordered_map<std::string, OutputQualifier> mStringToOutputQualifier;
static std::unordered_map<std::string, OutputType> mStringToOutputType;
static std::unordered_map<std::string, ConstantType> mStringToConstantType;
static std::unordered_map<std::string, ShaderStageType> mStringToShaderStageType;
};
template<typename T>

View File

@@ -254,6 +254,7 @@ public:
using FeatureLevel = filament::backend::FeatureLevel;
using StereoscopicType = filament::backend::StereoscopicType;
using ShaderStage = filament::backend::ShaderStage;
using ShaderStageFlags = filament::backend::ShaderStageFlags;
enum class VariableQualifier : uint8_t {
OUT
@@ -322,9 +323,9 @@ public:
*/
MaterialBuilder& parameter(const char* name, SamplerType samplerType,
SamplerFormat format = SamplerFormat::FLOAT,
ParameterPrecision precision = ParameterPrecision::DEFAULT,
bool multisample = false,
const char* transformName = "") noexcept;
ParameterPrecision precision = ParameterPrecision::DEFAULT, bool multisample = false,
const char* transformName = "",
ShaderStageFlags stages = ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) noexcept;
MaterialBuilder& buffer(filament::BufferInterfaceBlock bib) noexcept;
@@ -656,8 +657,10 @@ public:
Parameter() noexcept: parameterType(INVALID) {}
// Sampler
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p, bool ms, const char* tn)
: name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER), multisample(ms), transformName(tn) { }
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p,
bool ms, const char* tn, ShaderStageFlags s)
: name(paramName), size(1), precision(p), samplerType(t), format(f),
parameterType(SAMPLER), multisample(ms), transformName(tn), stages(s) { }
// Uniform
Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p)
@@ -676,6 +679,7 @@ public:
SamplerFormat format;
bool multisample;
utils::CString transformName;
ShaderStageFlags stages;
enum {
INVALID,
UNIFORM,

View File

@@ -175,4 +175,15 @@ std::unordered_map<std::string, ConstantType>& Enums::getMap<ConstantType>() noe
return mStringToConstantType;
};
std::unordered_map<std::string, ShaderStageType> Enums::mStringToShaderStageType = {
{ "fragment", ShaderStageType::FRAGMENT },
{ "vertex", ShaderStageType::VERTEX },
{ "compute", ShaderStageType::COMPUTE },
};
template <>
std::unordered_map<std::string, ShaderStageType>& Enums::getMap<ShaderStageType>() noexcept {
return mStringToShaderStageType;
};
} // namespace filamat

View File

@@ -17,7 +17,6 @@
#include "GLSLPostProcessor.h"
#include <GlslangToSpv.h>
#include <SPVRemapper.h>
#include <spirv-tools/libspirv.hpp>
#include <spirv_glsl.hpp>
@@ -145,12 +144,12 @@ DescriptorSetLayout getPerMaterialDescriptorSet(SamplerInterfaceBlock const& sib
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
+PerMaterialBindingPoints::MATERIAL_PARAMS, DescriptorFlags::NONE, 0 });
for (auto const& sampler : samplers) {
layout.bindings.push_back(DescriptorSetLayoutBinding {
(sampler.type == SamplerInterfaceBlock::Type::SAMPLER_EXTERNAL) ?
DescriptorType::SAMPLER_EXTERNAL : DescriptorType::SAMPLER,
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, sampler.binding,
DescriptorFlags::NONE, 0 });
for (auto const& sampler: samplers) {
layout.bindings.push_back(DescriptorSetLayoutBinding{
(sampler.type == SamplerInterfaceBlock::Type::SAMPLER_EXTERNAL)
? DescriptorType::SAMPLER_EXTERNAL
: DescriptorType::SAMPLER,
sampler.stages, sampler.binding, DescriptorFlags::NONE, 0 });
}
return layout;
@@ -289,24 +288,8 @@ GLSLPostProcessor::GLSLPostProcessor(MaterialBuilder::Optimization optimization,
: mOptimization(optimization),
mPrintShaders(flags & PRINT_SHADERS),
mGenerateDebugInfo(flags & GENERATE_DEBUG_INFO) {
// SPIRV error handler registration needs to occur only once. To avoid a race we do it up here
// in the constructor, which gets invoked before MaterialBuilder kicks off jobs.
spv::spirvbin_t::registerErrorHandler([](const std::string& str) {
slog.e << str << io::endl;
});
// Similar to above, we need to do a no-op remap to init a static table in the remapper before
// the jobs start using remap().
spv::spirvbin_t remapper(0);
// We need to provide at least a valid header to not crash.
SpirvBlob spirv {
0x07230203,// MAGIC
0, // VERSION
0, // GENERATOR
0, // BOUND
0 // SCHEMA, must be 0
};
remapper.remap(spirv, 0);
// This should occur only once, to avoid races.
SpirvRemapWrapperSetUp();
}
GLSLPostProcessor::~GLSLPostProcessor() = default;
@@ -988,8 +971,7 @@ void GLSLPostProcessor::optimizeSpirv(OptimizerPtr optimizer, SpirvBlob& spirv)
}
// Remove dead module-level objects: functions, types, vars
spv::spirvbin_t remapper(0);
remapper.remap(spirv, spv::spirvbin_base_t::DCE_ALL);
SpirvRemapWrapperRemap(spirv);
}
void GLSLPostProcessor::fixupClipDistance(

View File

@@ -23,6 +23,7 @@
#include <private/filament/SamplerInterfaceBlock.h>
#include "ShaderMinifier.h"
#include "SpirvRemapWrapper.h"
#include <spirv-tools/optimizer.hpp>

View File

@@ -302,7 +302,8 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, UniformType const
MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType,
SamplerFormat format, ParameterPrecision precision, bool multisample, const char* transformName) noexcept {
SamplerFormat format, ParameterPrecision precision, bool multisample,
const char* transformName, ShaderStageFlags stages) noexcept {
FILAMENT_CHECK_PRECONDITION(!multisample ||
(format != SamplerFormat::SHADOW &&
(samplerType == SamplerType::SAMPLER_2D ||
@@ -311,7 +312,7 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType sample
" as long as type is not SHADOW";
FILAMENT_CHECK_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT) << "Too many parameters";
mParameters[mParameterCount++] = { name, samplerType, format, precision, multisample, transformName };
mParameters[mParameterCount++] = { name, samplerType, format, precision, multisample, transformName, stages };
return *this;
}
@@ -637,8 +638,8 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept {
auto const& param = mParameters[i];
assert_invariant(!param.isSubpass());
if (param.isSampler()) {
sbb.add({ param.name.data(), param.name.size() },
binding, param.samplerType, param.format, param.precision, param.multisample);
sbb.add({ param.name.data(), param.name.size() }, binding, param.samplerType,
param.format, param.precision, param.multisample, param.stages);
if (!param.transformName.empty()) {
ibb.add({{{ param.transformName.data(), param.transformName.size() }, uint8_t(binding),
0, UniformType::MAT3, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 }});

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2017 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 "SpirvRemapWrapper.h"
#include <SPVRemapper.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <string>
namespace filamat {
void SpirvRemapWrapperSetUp() {
// SPIRV error handler registration should occur only once.
// Construct this SpirvRemapWrapper object only once.
spv::spirvbin_t::registerErrorHandler([](const std::string& str) {
utils::slog.e << str << utils::io::endl;
});
// Similar to above, we need to do a no-op remap to init a static
// table in the remapper before the jobs start using remap().
spv::spirvbin_t remapper(0);
// We need to provide at least a valid header to not crash.
std::vector<uint32_t> spirv {
0x07230203,// MAGIC
0, // VERSION
0, // GENERATOR
0, // BOUND
0 // SCHEMA, must be 0
};
remapper.remap(spirv, 0);
}
void SpirvRemapWrapperRemap(std::vector<uint32_t>& spirv) {
// Remove dead module-level objects: functions, types, vars
spv::spirvbin_t remapper(0);
remapper.remap(spirv, spv::spirvbin_base_t::DCE_ALL);
}
} // namespace filamat

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2017 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_SPIRVREMAPWRAPPER_H
#define TNT_SPIRVREMAPWRAPPER_H
#include <cstdint>
#include <vector>
namespace filamat {
void SpirvRemapWrapperSetUp();
void SpirvRemapWrapperRemap(std::vector<uint32_t>& spirv);
} // namespace filamat
#endif //TNT_SPIRVREMAPWRAPPER_H

View File

@@ -233,7 +233,7 @@ void MaterialDescriptorSetLayoutChunk::flatten(Flattener& f) {
} else {
f.writeUint8(uint8_t(DescriptorType::SAMPLER));
}
f.writeUint8(uint8_t(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT));
f.writeUint8(uint8_t(entry.stages));
f.writeUint8(entry.binding);
f.writeUint8(uint8_t(DescriptorFlags::NONE));
f.writeUint16(0);

View File

@@ -26,6 +26,7 @@
#include <filamat/MaterialBuilder.h>
#include <utils/JobSystem.h>
#include <utils/Panic.h>
#include <memory>

View File

@@ -21,6 +21,7 @@
#include <utils/JobSystem.h>
#include <utils/Log.h>
#include <utils/Panic.h>
using namespace filament;
using namespace filament::math;

View File

@@ -24,6 +24,7 @@
#include <geometry/TangentSpaceMesh.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/StructureOfArrays.h>
#include <cstring>

View File

@@ -22,6 +22,7 @@
#include <string_view>
#include <utils/Log.h>
#include <utils/Panic.h>
using namespace utils;
using namespace std::literals;

View File

@@ -65,6 +65,8 @@ set(SRCS
src/CyclicBarrier.cpp
src/EntityManager.cpp
src/EntityManagerImpl.h
src/FixedCapacityVectorBase.cpp
src/Invocable.cpp
src/JobSystem.cpp
src/Log.cpp
src/NameComponentManager.cpp

View File

@@ -20,9 +20,9 @@
// NOTE: this header should not include STL headers
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <string_view>
#include <utility>
#include <assert.h>
#include <stddef.h>
@@ -31,6 +31,9 @@
#include <string.h>
namespace utils {
namespace io {
class ostream;
}
//! \privatesection
struct hashCStrings {
@@ -38,7 +41,7 @@ struct hashCStrings {
typedef size_t result_type;
result_type operator()(argument_type cstr) const noexcept {
size_t hash = 5381;
while (int const c = *cstr++) {
while (int const c = static_cast<unsigned char>(*cstr++)) {
hash = (hash * 33u) ^ size_t(c);
}
return hash;
@@ -97,11 +100,7 @@ public:
return *this;
}
~CString() noexcept {
if (mData) {
free(mData - 1);
}
}
~CString() noexcept;
void swap(CString& other) noexcept {
// don't use std::swap(), we don't want an STL dependency in this file
@@ -110,8 +109,8 @@ public:
other.mCStr = temp;
}
const_pointer c_str() const noexcept { return mCStr; }
pointer c_str() noexcept { return mCStr; }
const_pointer c_str() const noexcept { return const_cast<CString*>(this)->c_str(); }
const_pointer c_str_safe() const noexcept { return mData ? c_str() : ""; }
const_pointer data() const noexcept { return c_str(); }
pointer data() noexcept { return c_str(); }
@@ -119,33 +118,89 @@ public:
size_type length() const noexcept { return size(); }
bool empty() const noexcept { return size() == 0; }
iterator begin() noexcept { return mCStr; }
iterator begin() noexcept { return c_str(); }
iterator end() noexcept { return begin() + length(); }
const_iterator begin() const noexcept { return data(); }
const_iterator end() const noexcept { return begin() + length(); }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end(); }
CString& replace(size_type pos, size_type len, const CString& str) noexcept;
CString& insert(size_type pos, const CString& str) noexcept { return replace(pos, 0, str); }
CString& append(const CString& str) noexcept { return insert(length(), str); }
// replace
template<size_t N>
CString& replace(size_type const pos,
size_type const len, const StringLiteral<N>& str) & noexcept {
return replace(pos, len, str, N - 1);
}
const_reference operator[](size_type pos) const noexcept {
CString& replace(size_type const pos, size_type const len, const CString& str) & noexcept {
return replace(pos, len, str.c_str_safe(), str.size());
}
template<size_t N>
CString&& replace(size_type const pos,
size_type const len, const StringLiteral<N>& str) && noexcept {
return std::move(replace(pos, len, str));
}
CString&& replace(size_type const pos, size_type const len, const CString& str) && noexcept {
return std::move(replace(pos, len, str));
}
// insert
template<size_t N>
CString& insert(size_type const pos, const StringLiteral<N>& str) & noexcept {
return replace(pos, 0, str);
}
CString& insert(size_type const pos, const CString& str) & noexcept {
return replace(pos, 0, str);
}
template<size_t N>
CString&& insert(size_type const pos, const StringLiteral<N>& str) && noexcept {
return std::move(*this).replace(pos, 0, str);
}
CString&& insert(size_type const pos, const CString& str) && noexcept {
return std::move(*this).replace(pos, 0, str);
}
// append
template<size_t N>
CString& append(const StringLiteral<N>& str) & noexcept {
return insert(length(), str);
}
CString& append(const CString& str) & noexcept {
return insert(length(), str);
}
template<size_t N>
CString&& append(const StringLiteral<N>& str) && noexcept {
return std::move(*this).insert(length(), str);
}
CString&& append(const CString& str) && noexcept {
return std::move(*this).insert(length(), str);
}
const_reference operator[](size_type const pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
reference operator[](size_type pos) noexcept {
reference operator[](size_type const pos) noexcept {
assert(pos < size());
return begin()[pos];
}
const_reference at(size_type pos) const noexcept {
const_reference at(size_type const pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
reference at(size_type pos) noexcept {
reference at(size_type const pos) noexcept {
assert(pos < size());
return begin()[pos];
}
@@ -171,7 +226,7 @@ public:
}
// placement new declared as "throw" to avoid the compiler's null-check
inline void* operator new(size_t, void* ptr) {
void* operator new(size_t, void* ptr) {
assert(ptr);
return ptr;
}
@@ -185,6 +240,8 @@ public:
};
private:
CString& replace(size_type pos, size_type len, char const* str, size_t l) & noexcept;
#if !defined(NDEBUG)
friend io::ostream& operator<<(io::ostream& out, const CString& rhs);
#endif
@@ -225,7 +282,7 @@ private:
}
};
// implement this for your type for automatic conversion to CString. Failing to do so leads
// Implement this for your type for automatic conversion to CString. Failing to do so leads
// to a compile-time failure.
template<typename T>
CString to_string(T value) noexcept;
@@ -249,7 +306,7 @@ public:
pointer c_str() noexcept { return mData; }
private:
value_type mData[N] = {0};
value_type mData[N] = {};
};
} // namespace utils

View File

@@ -23,9 +23,11 @@
#include <utils/CString.h>
#include <utils/compiler.h>
#include <utils/ostream.h>
namespace utils {
namespace io {
class ostream;
}
/**
* CallStack captures the current's thread call stack.
@@ -88,23 +90,23 @@ public:
bool operator <(const CallStack& rhs) const;
inline bool operator >(const CallStack& rhs) const {
bool operator >(const CallStack& rhs) const {
return rhs < *this;
}
inline bool operator !=(const CallStack& rhs) const {
bool operator !=(const CallStack& rhs) const {
return *this < rhs || rhs < *this;
}
inline bool operator >=(const CallStack& rhs) const {
bool operator >=(const CallStack& rhs) const {
return !operator <(rhs);
}
inline bool operator <=(const CallStack& rhs) const {
bool operator <=(const CallStack& rhs) const {
return !operator >(rhs);
}
inline bool operator ==(const CallStack& rhs) const {
bool operator ==(const CallStack& rhs) const {
return !operator !=(rhs);
}

View File

@@ -19,8 +19,8 @@
#include <utils/compiler.h>
#include <utils/compressed_pair.h>
#include <utils/Panic.h>
#include <algorithm>
#include <initializer_list>
#include <iterator>
#include <limits>
@@ -40,6 +40,11 @@
namespace utils {
class FixedCapacityVectorBase {
protected:
UTILS_NORETURN static void capacityCheckFailed(size_t capacity, size_t size);
};
/**
* FixedCapacityVector is (almost) a drop-in replacement for std::vector<> except it has a
* fixed capacity decided at runtime. The vector storage is never reallocated unless reserve()
@@ -56,7 +61,7 @@ namespace utils {
* the optional value argument, e.g. FixedCapacityVector<int>(4, 0) or foo.resize(4, 0).
*/
template<typename T, typename A = std::allocator<T>, bool CapacityCheck = true>
class UTILS_PUBLIC FixedCapacityVector {
class UTILS_PUBLIC FixedCapacityVector : protected FixedCapacityVectorBase {
public:
using allocator_type = A;
using value_type = T;
@@ -266,7 +271,7 @@ public:
mSize = 0;
}
void resize(size_type count) {
void resize(size_type const count) {
assertCapacityForSize(count);
if constexpr(std::is_trivially_constructible_v<value_type> &&
std::is_trivially_destructible_v<value_type>) {
@@ -277,12 +282,12 @@ public:
}
}
void resize(size_type count, const_reference v) {
void resize(size_type const count, const_reference v) {
assertCapacityForSize(count);
resize_non_trivial(count, v);
}
void swap(FixedCapacityVector& other) {
void swap(FixedCapacityVector& other) noexcept {
using std::swap;
swap(mData, other.mData);
swap(mSize, other.mSize);
@@ -326,16 +331,16 @@ private:
return mCapacityAllocator.second();
}
iterator assertCapacityForSize(size_type s) {
iterator assertCapacityForSize(size_type const s) {
if constexpr(CapacityCheck || FILAMENT_FORCE_CAPACITY_CHECK) {
FILAMENT_CHECK_PRECONDITION(capacity() >= s)
<< "capacity exceeded: requested size " << (unsigned long)s
<< "u, available capacity " << (unsigned long)capacity() << "u.";
if (UTILS_VERY_UNLIKELY(capacity() < s)) {
capacityCheckFailed(capacity(), s);
}
}
return end();
}
inline void construct(iterator first, iterator last) noexcept {
void construct(iterator const first, iterator const last) noexcept {
// we check for triviality here so that the implementation could be non-inline
if constexpr(!std::is_trivially_constructible_v<value_type>) {
construct_non_trivial(first, last);
@@ -358,7 +363,7 @@ private:
}
inline void destroy(iterator first, iterator last) noexcept {
void destroy(iterator const first, iterator const last) noexcept {
// we check for triviality here so that the implementation could be non-inline
if constexpr(!std::is_trivially_destructible_v<value_type>) {
destroy_non_trivial(first, last);
@@ -419,7 +424,7 @@ private:
explicit SizeTypeWrapper(TYPE value) noexcept : value(value) { }
SizeTypeWrapper& operator=(TYPE rhs) noexcept { value = rhs; return *this; }
SizeTypeWrapper& operator=(SizeTypeWrapper& rhs) noexcept = delete;
operator TYPE() const noexcept { return value; }
operator TYPE() const noexcept { return value; } // NOLINT(*-explicit-constructor)
};
pointer mData{};

View File

@@ -14,10 +14,8 @@
* limitations under the License.
*/
#ifndef TNT_UTILS_INVOKABLE_H
#define TNT_UTILS_INVOKABLE_H
#include <utils/ostream.h>
#ifndef TNT_UTILS_INVOCABLE_H
#define TNT_UTILS_INVOCABLE_H
#include <type_traits>
#include <utility>
@@ -25,6 +23,9 @@
#include <assert.h>
namespace utils {
namespace io {
class ostream;
}
/*
* Invocable is a move-only general purpose function wrapper. Instances can
@@ -50,11 +51,16 @@ template<typename Fn, typename R, typename... Args>
using EnableIfFnMatchesInvocable = std::enable_if_t<true, int>;
#endif
class InvocableBase {
protected:
static io::ostream& printInvocable(io::ostream& out, const char* name);
};
template<typename Signature>
class Invocable;
template<typename R, typename... Args>
class Invocable<R(Args...)> {
class Invocable<R(Args...)> : protected InvocableBase {
public:
// Creates an Invocable that does not contain a functor.
// Will evaluate to false.
@@ -85,7 +91,7 @@ public:
private:
#if !defined(NDEBUG)
friend io::ostream& operator<<(io::ostream& out, const Invocable&) {
return out << "Invocable<>"; // TODO: is there a way to do better here?
return printInvocable(out, "Invocable<>"); // TODO: is there a way to do better here?
}
#endif
void* mInvocable = nullptr;
@@ -156,4 +162,4 @@ Invocable<R(Args...)>::operator bool() const noexcept {
} // namespace utils
#endif // TNT_UTILS_INVOKABLE_H
#endif // TNT_UTILS_INVOCABLE_H

View File

@@ -353,7 +353,7 @@ public:
* The TPanic<> class implements the std::exception protocol as well as the Panic
* interface common to all exceptions thrown by the framework.
*/
template <typename T>
template <typename>
class UTILS_PUBLIC TPanic : public Panic {
public:
// std::exception protocol
@@ -398,12 +398,11 @@ public:
* @see PANIC_PRECONDITION, PANIC_POSTCONDITION, PANIC_ARITHMETIC
* @see setMode()
*/
static inline void panic(
static void panic(
char const* function, char const* file, int line, char const* literal,
std::string reason) UTILS_NORETURN;
protected:
private:
/**
* Creates a Panic with extra information about the error-site.
* @param function the name of the function where the error was detected
@@ -415,11 +414,14 @@ protected:
TPanic(char const* function, char const* file, int line, char const* literal,
std::string reason);
friend class PreconditionPanic;
friend class PostconditionPanic;
friend class ArithmeticPanic;
protected:
~TPanic() override;
private:
void buildMessage();
char const* const mFile = nullptr; // file where the panic happened
char const* const mFunction = nullptr; // function where the panic happened
int const mLine = -1; // line where the panic happened
@@ -443,7 +445,7 @@ void panicLog(
* ASSERT_PRECONDITION uses this Panic to report a precondition failure.
* @see ASSERT_PRECONDITION
*/
class UTILS_PUBLIC PreconditionPanic : public TPanic<PreconditionPanic> {
class UTILS_PUBLIC PreconditionPanic final : public TPanic<PreconditionPanic> {
// Programming error, can be avoided
// e.g.: invalid arguments
using TPanic<PreconditionPanic>::TPanic;
@@ -457,9 +459,9 @@ class UTILS_PUBLIC PreconditionPanic : public TPanic<PreconditionPanic> {
* ASSERT_POSTCONDITION uses this Panic to report a postcondition failure.
* @see ASSERT_POSTCONDITION
*/
class UTILS_PUBLIC PostconditionPanic : public TPanic<PostconditionPanic> {
class UTILS_PUBLIC PostconditionPanic final : public TPanic<PostconditionPanic> {
// Usually only detectable at runtime
// e.g.: dead-lock would occur, arithmetic errors
// e.g.: deadlock would occur, arithmetic errors
using TPanic<PostconditionPanic>::TPanic;
friend class TPanic<PostconditionPanic>;
constexpr static auto type = "Postcondition";
@@ -471,7 +473,7 @@ class UTILS_PUBLIC PostconditionPanic : public TPanic<PostconditionPanic> {
* ASSERT_ARITHMETIC uses this Panic to report an arithmetic (postcondition) failure.
* @see ASSERT_ARITHMETIC
*/
class UTILS_PUBLIC ArithmeticPanic : public TPanic<ArithmeticPanic> {
class UTILS_PUBLIC ArithmeticPanic final : public TPanic<ArithmeticPanic> {
// A common case of post-condition error
// e.g.: underflow, overflow, internal computations errors
using TPanic<ArithmeticPanic>::TPanic;
@@ -519,11 +521,11 @@ public:
PanicStream& operator<<(const void* value) noexcept;
PanicStream& operator<<(const char* string) noexcept;
PanicStream& operator<<(const unsigned char* string) noexcept;
PanicStream& operator<<(const char* value) noexcept;
PanicStream& operator<<(const unsigned char* value) noexcept;
PanicStream& operator<<(std::string const& s) noexcept;
PanicStream& operator<<(std::string_view const& s) noexcept;
PanicStream& operator<<(std::string const& value) noexcept;
PanicStream& operator<<(std::string_view const& value) noexcept;
protected:
io::sstream mStream;

View File

@@ -19,11 +19,8 @@
#include <utils/compiler.h>
#include <type_traits>
#include <string_view>
#include <stddef.h>
namespace utils {
/**
@@ -41,7 +38,7 @@ public:
using const_iterator = std::string_view::const_iterator;
// Constructor from string literal
template <size_t M>
template<unsigned int M>
constexpr StaticString(const char (&str)[M]) noexcept : mString(str, M - 1) {} // NOLINT(*-explicit-constructor)
constexpr StaticString() noexcept = default;
@@ -61,7 +58,7 @@ public:
return mString[pos];
}
constexpr const_reference at(size_type pos) const {
constexpr const_reference at(size_type const pos) const {
return mString[pos];
}

View File

@@ -20,27 +20,30 @@
#include <utils/ostream.h>
#include <algorithm>
#include <cstdlib>
#include <memory>
namespace utils {
UTILS_NOINLINE
CString::CString(const char* cstr, size_t length) {
CString::CString(const char* cstr, size_t const length) {
if (length && cstr) {
Data* p = (Data*)malloc(sizeof(Data) + length + 1);
p->length = (size_type)length;
mCStr = (value_type*)(p + 1);
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + length + 1));
p->length = size_type(length);
mCStr = reinterpret_cast<value_type*>(p + 1);
// we don't use memcpy here to avoid a call to libc, the generated code is pretty good.
std::uninitialized_copy_n(cstr, length, mCStr);
mCStr[length] = '\0';
}
}
CString::CString(size_t length) {
CString::CString(size_t const length) {
if (length) {
Data* p = (Data*)malloc(sizeof(Data) + length + 1);
p->length = (size_type)length;
mCStr = (value_type*)(p + 1);
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + length + 1));
p->length = size_type(length);
mCStr = reinterpret_cast<value_type*>(p + 1);
std::fill_n(mCStr, length, 0);
mCStr[length] = '\0';
}
@@ -58,24 +61,30 @@ CString& CString::operator=(const CString& rhs) {
if (this != &rhs) {
auto *const p = mData ? mData - 1 : nullptr;
new(this) CString(rhs);
free(p);
std::free(p);
}
return *this;
}
CString& CString::replace(size_type pos, size_type len, const CString& str) noexcept {
CString::~CString() noexcept {
if (mData) {
std::free(mData - 1);
}
}
CString& CString::replace(size_type const pos, size_type len, char const* str, size_t const l) & noexcept {
assert(pos <= size());
len = std::min(len, size() - pos);
// The new size of the string, after the replacement.
const size_type newSize = size() - len + str.size();
const size_type newSize = size() - len + l;
// Allocate enough memory to hold the new string.
Data* p = (Data*) malloc(sizeof(Data) + newSize + 1);
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + newSize + 1));
assert(p);
p->length = newSize;
value_type* newStr = (value_type*) (p + 1);
value_type* newStr = reinterpret_cast<value_type*>(p + 1);
const value_type* beginning = mCStr;
const value_type* replacementStart = mCStr + pos;
@@ -84,7 +93,7 @@ CString& CString::replace(size_type pos, size_type len, const CString& str) noex
value_type* ptr = newStr;
ptr = std::uninitialized_copy(beginning, replacementStart, ptr);
ptr = std::uninitialized_copy_n(str.c_str_safe(), str.length(), ptr);
ptr = std::uninitialized_copy_n(str, l, ptr);
ptr = std::uninitialized_copy(replacementEnd, end, ptr);
// null-terminator
@@ -92,14 +101,14 @@ CString& CString::replace(size_type pos, size_type len, const CString& str) noex
std::swap(mCStr, newStr);
if (newStr) {
free((Data*) newStr - 1);
std::free(reinterpret_cast<Data*>(newStr) - 1);
}
return *this;
}
#if !defined(NDEBUG)
io::ostream& operator<<(io::ostream& out, const utils::CString& rhs) {
io::ostream& operator<<(io::ostream& out, const CString& rhs) {
return out << rhs.c_str_safe();
}
#endif

View File

@@ -0,0 +1,36 @@
/*
* 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 <utils/FixedCapacityVector.h>
#include <utils/compiler.h>
#include <utils/Panic.h>
#include <stddef.h>
#include <stdlib.h>
namespace utils {
void FixedCapacityVectorBase::capacityCheckFailed(size_t const capacity, size_t const size) {
UTILS_ASSUME(capacity < size);
FILAMENT_CHECK_PRECONDITION(capacity >= size)
<< "capacity exceeded: requested size " << size
<< "u, available capacity " << capacity << "u.";
// In practice, we will never reach this.
abort();
}
} // namespace utils

View File

@@ -0,0 +1,24 @@
/*
* 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 <utils/Invocable.h>
#include <utils/ostream.h>
namespace utils {
io::ostream& InvocableBase::printInvocable(io::ostream& out, const char* name) {
return out << name;
}
} // namespace utils

View File

@@ -18,6 +18,7 @@
#include "ostream_.h"
#include <utils/CallStack.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/ostream.h>
@@ -68,7 +69,7 @@ public:
getCallback().call(panic);
}
void set(Panic::PanicHandlerCallback handler, void* user) noexcept {
void set(Panic::PanicHandlerCallback const handler, void* user) noexcept {
std::lock_guard const lock(mLock);
mCallBack = { handler, user };
}
@@ -96,7 +97,7 @@ static std::string sprintfToString(const char* format, va_list args) noexcept {
return s;
}
static inline std::string sprintfToString(const char* format, ...) noexcept {
static std::string sprintfToString(const char* format, ...) noexcept {
va_list args;
va_start(args, format);
std::string const s{ sprintfToString(format, args) };
@@ -120,14 +121,14 @@ static std::string buildPanicString(
Panic::~Panic() noexcept = default;
void Panic::setPanicHandler(PanicHandlerCallback handler, void* user) noexcept {
void Panic::setPanicHandler(PanicHandlerCallback const handler, void* user) noexcept {
UserPanicHandler::get().set(handler, user);
}
// ------------------------------------------------------------------------------------------------
template<typename T>
TPanic<T>::TPanic(const char* function, const char* file, int line, char const* literal,
TPanic<T>::TPanic(const char* function, const char* file, int const line, char const* literal,
std::string reason)
: mFile(file),
mFunction(function),
@@ -188,13 +189,13 @@ void TPanic<T>::log() const noexcept {
}
UTILS_ALWAYS_INLINE
inline static const char* formatFile(char const* file) noexcept {
static const char* formatFile(char const* file) noexcept {
const char * p = std::strstr(file, "filament/");
return p ? p : file;
}
template<typename T>
void TPanic<T>::panic(char const* function, char const* file, int line, char const* literal,
void TPanic<T>::panic(char const* function, char const* file, int const line, char const* literal,
const char* format, ...) {
va_list args;
va_start(args, format);
@@ -233,7 +234,7 @@ void TPanic<T>::panic(char const* function, char const* file, int line, char con
namespace details {
void panicLog(char const* function, char const* file, int line, const char* format, ...) noexcept {
void panicLog(char const* function, char const* file, int const line, const char* format, ...) noexcept {
va_list args;
va_start(args, format);
std::string const reason{ sprintfToString(format, args) };
@@ -249,79 +250,79 @@ void panicLog(char const* function, char const* file, int line, const char* form
PanicStream::PanicStream(
char const* function,
char const* file,
int line,
int const line,
char const* condition) noexcept
: mFunction(function), mFile(file), mLine(line), mLiteral(condition) {
}
PanicStream::~PanicStream() = default;
PanicStream& PanicStream::operator<<(short value) noexcept {
PanicStream& PanicStream::operator<<(short const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned short value) noexcept {
PanicStream& PanicStream::operator<<(unsigned short const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(char value) noexcept {
PanicStream& PanicStream::operator<<(char const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned char value) noexcept {
PanicStream& PanicStream::operator<<(unsigned char const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(int value) noexcept {
PanicStream& PanicStream::operator<<(int const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned int value) noexcept {
PanicStream& PanicStream::operator<<(unsigned int const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(long value) noexcept {
PanicStream& PanicStream::operator<<(long const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned long value) noexcept {
PanicStream& PanicStream::operator<<(unsigned long const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(long long int value) noexcept {
PanicStream& PanicStream::operator<<(long long int const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned long long int value) noexcept {
PanicStream& PanicStream::operator<<(unsigned long long int const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(float value) noexcept {
PanicStream& PanicStream::operator<<(float const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(double value) noexcept {
PanicStream& PanicStream::operator<<(double const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(long double value) noexcept {
PanicStream& PanicStream::operator<<(long double const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(bool value) noexcept {
PanicStream& PanicStream::operator<<(bool const value) noexcept {
mStream << value;
return *this;
}

View File

@@ -18,6 +18,8 @@
#define TNT_UTILS_OSTREAM__H
#include <utils/ostream.h>
#include <utility>
#include <mutex>
namespace utils::io {

Some files were not shown because too many files have changed in this diff Show More