Compare commits

..

1 Commits

Author SHA1 Message Date
Benjamin Doherty
031cd302dd Capture command callstacks for debugging 2023-10-30 14:42:24 -07:00
715 changed files with 28192 additions and 32907 deletions

View File

@@ -1,7 +0,0 @@
;;; Directory Local Variables -*- no-byte-compile: t -*-
;;; For more information see (info "(emacs) Directory Variables")
((c++-mode . ((c-file-style . "filament")
(apheleia-inhibit . t)))
(c-mode . ((c-file-style . "filament")
(apheleia-inhibit . t))))

View File

@@ -1,11 +0,0 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
[*.{c,cpp,h,inc,kt,java,js,md}]
indent_style = space
indent_size = 4
max_line_length = 100

View File

@@ -29,6 +29,10 @@ jobs:
with:
name: filamat-android-full
path: out/filamat-android-release.aar
- uses: actions/upload-artifact@v1.0.0
with:
name: filamat-android-lite
path: out/filamat-android-lite-release.aar
- uses: actions/upload-artifact@v1.0.0
with:
name: gltfio-android-release

View File

@@ -1,30 +0,0 @@
name: CocoaPods Deploy
# This must be run after the iOS release job has finished, and the iOS release
# asset has been uploaded to Github.
on:
workflow_dispatch:
inputs:
release_tag:
description: 'Release tag to deploy (e.g., v1.42.2)'
required: true
default: 'v1.42.2'
jobs:
cocoapods-deploy:
name: cocoapods-deploy
runs-on: macos-latest
steps:
- name: Check out iOS/CocoaPods directory
uses: Bhacaz/checkout-files@49fc3050859046bf4f4873678d46099985640e89
with:
files: ios/CocoaPods
token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.event.inputs.release_tag }}
- name: Move podspec to root
run: mv ios/CocoaPods/*.podspec .
- name: Install CocoaPods
run: gem install cocoapods
- uses: michaelhenry/deploy-to-cocoapods-github-action@745686ab065f90596e0d5cfcf97bb2416d94262e
env:
COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}

View File

@@ -1,33 +0,0 @@
name: Npm Deploy
on:
workflow_dispatch:
inputs:
release_tag:
description: 'Release tag to deploy (e.g., v1.42.2)'
required: true
default: 'v1.42.2'
jobs:
npm-deploy:
name: npm-deploy
runs-on: macos-latest
steps:
- uses: actions/checkout@v3.3.0
with:
ref: ${{ github.event.inputs.release_tag }}
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v3
with:
node-version: '18.x'
registry-url: 'https://registry.npmjs.org'
- name: Run build script
run: |
cd build/web && printf "y" | ./build.sh release
- name: Deploy to npm
run: |
cd out/cmake-webgl-release/web/filament-js
npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -124,6 +124,7 @@ jobs:
cd ../..
mv out/filament-android-release.aar out/filament-${TAG}-android.aar
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
mv out/filamat-android-lite-release.aar out/filamat-${TAG}-lite-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
- name: Sign sample-gltf-viewer
@@ -205,7 +206,8 @@ jobs:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
build\windows\build-github.bat release
move out\filament-windows.tgz out\filament-$Env:TAG-windows.tgz
cd ..\..
move out\filament-windows.tgz out\filament-%TAG%-windows.tgz
shell: cmd
- uses: actions/github-script@v6
env:

2
.gitignore vendored
View File

@@ -16,5 +16,3 @@ settings.json
test*.png
test*.json
results
/compile_commands.json
/.cache

View File

@@ -40,27 +40,25 @@ inside the Filament source tree.
To trigger an incremental debug build:
```shell
./build.sh debug
```
$ ./build.sh debug
```
To trigger an incremental release build:
```shell
./build.sh release
```
$ ./build.sh release
```
To trigger both incremental debug and release builds:
```shell
./build.sh debug release
```
$ ./build.sh debug release
```
If build fails for some reasons, it may leave the `out/` directory in a broken state. You can
force a clean build by adding the `-c` flag in that case.
To install the libraries and executables in `out/debug/` and `out/release/`, add the `-i` flag.
The script offers more features described by executing `build.sh -h`.
You can force a clean build by adding the `-c` flag. The script offers more features described
by executing `build.sh -h`.
### Filament-specific CMake Options
@@ -78,9 +76,9 @@ The following CMake options are boolean options specific to Filament:
To turn an option on or off:
```shell
cd <cmake-build-directory>
cmake . -DOPTION=ON # Replace OPTION with the option name, set to ON / OFF
```
$ cd <cmake-build-directory>
$ cmake . -DOPTION=ON # Replace OPTION with the option name, set to ON / OFF
```
Options can also be set with the CMake GUI.
@@ -104,38 +102,38 @@ script.
If you'd like to run `cmake` directly rather than using the build script, it can be invoked as
follows, with some caveats that are explained further down.
```shell
mkdir out/cmake-release
cd out/cmake-release
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
```
$ mkdir out/cmake-release
$ cd out/cmake-release
$ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
```
Your Linux distribution might default to `gcc` instead of `clang`, if that's the case invoke
`cmake` with the following command:
```shell
mkdir out/cmake-release
cd out/cmake-release
```
$ mkdir out/cmake-release
$ cd out/cmake-release
# Or use a specific version of clang, for instance /usr/bin/clang-14
CC=/usr/bin/clang CXX=/usr/bin/clang++ CXXFLAGS=-stdlib=libc++ \
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
$ CC=/usr/bin/clang CXX=/usr/bin/clang++ CXXFLAGS=-stdlib=libc++ \
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
```
You can also export the `CC` and `CXX` environment variables to always point to `clang`. Another
solution is to use `update-alternatives` to both change the default compiler, and point to a
specific version of clang:
```shell
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-14 100
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-14 100
update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100
update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100
```
$ update-alternatives --install /usr/bin/clang clang /usr/bin/clang-14 100
$ update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-14 100
$ update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100
$ update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100
```
Finally, invoke `ninja`:
```shell
ninja
```
$ ninja
```
This will build Filament, its tests and samples, and various host tools.
@@ -145,8 +143,8 @@ This will build Filament, its tests and samples, and various host tools.
To compile Filament you must have the most recent version of Xcode installed and you need to
make sure the command line tools are setup by running:
```shell
xcode-select --install
```
$ xcode-select --install
```
If you wish to run the Vulkan backend instead of the default Metal backend, you must install
@@ -154,11 +152,11 @@ the LunarG SDK, enable "System Global Components", and reboot your machine.
Then run `cmake` and `ninja` to trigger a build:
```shell
mkdir out/cmake-release
cd out/cmake-release
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
ninja
```
$ mkdir out/cmake-release
$ cd out/cmake-release
$ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
$ ninja
```
### iOS
@@ -166,20 +164,20 @@ ninja
The easiest way to build Filament for iOS is to use `build.sh` and the
`-p ios` flag. For instance to build the debug target:
```shell
./build.sh -p ios debug
```
$ ./build.sh -p ios debug
```
See [ios/samples/README.md](./ios/samples/README.md) for more information.
### Windows
#### Building on Windows with Visual Studio 2019 or later
#### Building on Windows with Visual Studio 2019
Install the following components:
- [Visual Studio 2019 or later](https://www.visualstudio.com/downloads)
- [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/)
- [Visual Studio 2019](https://www.visualstudio.com/downloads)
- [Windows 10 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk)
- [Python 3.7](https://www.python.org/ftp/python/3.7.0/python-3.7.0.exe)
- [CMake 3.14 or later](https://github.com/Kitware/CMake/releases/download/v3.14.7/cmake-3.14.7-win64-x64.msi)
@@ -193,10 +191,10 @@ using `fsutil.exe file queryCaseSensitiveInfo`.
Next, open `x64 Native Tools Command Prompt for VS 2019`, create a working directory, and run
CMake in it:
```bat
mkdir out
cd out
cmake ..
```
> mkdir out
> cd out
> cmake ..
```
Open the generated solution file `TNT.sln` in Visual Studio.
@@ -206,15 +204,15 @@ target in the _Solution Explorer_ and choose _Build_ to build a specific target.
For example, build the `material_sandbox` sample and run it from the `out` directory with:
```bat
samples\Debug\material_sandbox.exe ..\assets\models\monkey\monkey.obj
```
> samples\Debug\material_sandbox.exe ..\assets\models\monkey\monkey.obj
```
You can also use CMake to invoke the build without opening Visual Studio. For example, from the
`out` folder run the following command.
```bat
cmake --build . --target gltf_viewer --config Release
```
> cmake --build . --target gltf_viewer --config Release
```
### Android
@@ -239,8 +237,8 @@ To build Android on Windows machines, see [android/Windows.md](android/Windows.m
The easiest way to build Filament for Android is to use `build.sh` and the
`-p android` flag. For instance to build the release target:
```shell
./build.sh -p android release
```
$ ./build.sh -p android release
```
Run `build.sh -h` for more information.
@@ -250,23 +248,23 @@ Run `build.sh -h` for more information.
Invoke CMake in a build directory of your choice, inside of filament's directory. The commands
below show how to build Filament for ARM 64-bit (`aarch64`).
```shell
mkdir out/android-build-release-aarch64
cd out/android-build-release-aarch64
cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=../../build/toolchain-aarch64-linux-android.cmake \
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../android-release/filament ../..
```
$ mkdir out/android-build-release-aarch64
$ cd out/android-build-release-aarch64
$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=../../build/toolchain-aarch64-linux-android.cmake \
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../android-release/filament ../..
```
And then invoke `ninja`:
```shell
ninja install
```
$ ninja install
```
or
```shell
ninja install/strip
```
$ ninja install/strip
```
This will generate Filament's Android binaries in `out/android-release`. This location is important
@@ -298,8 +296,8 @@ AAR.
Alternatively you can build the AAR from the command line by executing the following in the
`android/` directory:
```shell
./gradlew -Pcom.google.android.filament.dist-dir=../../out/android-release/filament assembleRelease
```
$ ./gradlew -Pcom.google.android.filament.dist-dir=../../out/android-release/filament assembleRelease
```
The `-Pcom.google.android.filament.dist-dir` can be used to specify a different installation
@@ -313,7 +311,7 @@ sure to add the newly created module as a dependency to your application.
If you do not wish to include all supported ABIs, make sure to create the appropriate flavors in
your Gradle build file. For example:
```gradle
```
flavorDimensions 'cpuArch'
productFlavors {
arm8 {
@@ -355,7 +353,7 @@ started, follow the instructions for building Filament on your platform ([macOS]
Next, you need to install the Emscripten SDK. The following instructions show how to install the
same version that our continuous builds use.
```shell
```
cd <your chosen parent folder for the emscripten SDK>
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.15.zip > emsdk.zip
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
@@ -366,7 +364,7 @@ source ./emsdk_env.sh
After this you can invoke the [easy build](#easy-build) script as follows:
```shell
```
export EMSDK=<your chosen home for the emscripten SDK>
./build.sh -p webgl release
```
@@ -376,7 +374,7 @@ creates a `samples` folder that can be used as the root of a simple static web s
cannot open the HTML directly from the filesystem due to CORS. We recommend using the emrun tool
to create a quick localhost server:
```shell
```
emrun out/cmake-webgl-release/web/samples --no_browser --port 8000
```
@@ -397,7 +395,7 @@ Some of the samples accept FBX/OBJ meshes while others rely on the `filamesh` fi
generate a `filamesh ` file from an FBX/OBJ asset, run the `filamesh` tool
(`./tools/filamesh/filamesh` in your build directory):
```shell
```
filamesh ./assets/models/monkey/monkey.obj monkey.filamesh
```
@@ -407,7 +405,7 @@ files for the IBL (which are PNGs containing `R11F_G11F_B10F` data) or a path to
containing two `.ktx` files (one for the IBL itself, one for the skybox). To generate an IBL
simply use this command:
```shell
```
cmgen -f ktx -x ./ibls/ my_ibl.exr
```
@@ -429,9 +427,9 @@ value is the desired roughness between 0 and 1.
To generate the documentation you must first install `doxygen` and `graphviz`, then run the
following commands:
```shell
cd filament/filament
doxygen docs/doxygen/filament.doxygen
```
$ cd filament/filament
$ doxygen docs/doxygen/filament.doxygen
```
Finally simply open `docs/html/index.html` in your web browser.
@@ -441,7 +439,7 @@ Finally simply open `docs/html/index.html` in your web browser.
To try out Filament's Vulkan support with SwiftShader, first build SwiftShader and set the
`SWIFTSHADER_LD_LIBRARY_PATH` variable to the folder that contains `libvk_swiftshader.dylib`:
```shell
```
git clone https://github.com/google/swiftshader.git
cd swiftshader/build
cmake .. && make -j
@@ -456,7 +454,7 @@ Continuous testing turnaround can be quite slow if you need to build SwiftShader
provide an Ubuntu-based Docker image that has it already built. The Docker image also includes
everything necessary for building Filament. You can fetch and run the image as follows:
```shell
```
docker pull ghcr.io/filament-assets/swiftshader
docker run -it ghcr.io/filament-assets/swiftshader
```

View File

@@ -3,14 +3,6 @@
# ==================================================================================================
cmake_minimum_required(VERSION 3.19)
# ==================================================================================================
# Toolchain configuration
# ==================================================================================================
if (APPLE AND NOT IOS)
# This must be set before project() is called
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15 CACHE STRING "")
endif()
# ==================================================================================================
# Project declaration
# ==================================================================================================
@@ -41,10 +33,6 @@ option(FILAMENT_LINUX_IS_MOBILE "Treat Linux as Mobile" OFF)
option(FILAMENT_ENABLE_ASAN_UBSAN "Enable Address and Undefined Behavior Sanitizers" OFF)
option(FILAMENT_ENABLE_TSAN "Enable Thread Sanitizer" OFF)
option(FILAMENT_ENABLE_FEATURE_LEVEL_0 "Enable Feature Level 0" ON)
set(FILAMENT_NDK_VERSION "" CACHE STRING
"Android NDK version or version prefix to be used when building for Android."
)
@@ -69,9 +57,6 @@ set(FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB "8" CACHE STRING
"Size of the Metal handle arena, default 8."
)
# Enable exceptions by default in spirv-cross.
set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS OFF)
# ==================================================================================================
# CMake policies
# ==================================================================================================
@@ -232,21 +217,6 @@ if (WIN32)
# we don't need them on CI.
string(REPLACE "/INCREMENTAL" "/INCREMENTAL:NO" ${LinkerFlag} ${${LinkerFlag}})
endforeach()
# We turn off compile-time optimizations for CI, as options that speed up the compile-time
# (e.g. /MP) might increase memory usage, leading to instabilities on limited CI machines.
option(FILAMENT_SHORTEN_MSVC_COMPILATION "Shorten compile-time in Visual Studio" OFF)
else()
option(FILAMENT_SHORTEN_MSVC_COMPILATION "Shorten compile-time in Visual Studio" ON)
endif()
if (MSVC)
if (FILAMENT_SHORTEN_MSVC_COMPILATION)
# enable multi-processor compilation
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
# disable run-time STL checks to improve tools (e.g. matc) performance
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_ITERATOR_DEBUG_LEVEL=0")
endif()
endif()
endif()
@@ -359,7 +329,6 @@ endif()
if (CYGWIN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti")
set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON)
endif()
if (MSVC)
@@ -396,7 +365,6 @@ endif()
# saved by -fno-exception and 10 KiB saved by -fno-rtti).
if (ANDROID OR IOS OR WEBGL)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-exceptions -fno-rtti")
set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON)
if (ANDROID OR WEBGL)
# Omitting unwind info prevents the generation of readable stack traces in crash reports on iOS
@@ -408,7 +376,6 @@ endif()
# std::visit, which is not supported on iOS 11.0 when exceptions are enabled.
if (IOS)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-exceptions")
set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON)
endif()
# With WebGL, we disable RTTI even for debug builds because we pass emscripten::val back and forth
@@ -428,13 +395,7 @@ endif()
if (FILAMENT_ENABLE_ASAN_UBSAN)
set(EXTRA_SANITIZE_OPTIONS "-fsanitize=address -fsanitize=undefined")
endif()
if (FILAMENT_ENABLE_TSAN)
set(EXTRA_SANITIZE_OPTIONS "-fsanitize=thread")
endif()
if (ANDROID)
# keep STL debug infos (mimics what the NDK does)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-limit-debug-info")
endif()
if (NOT MSVC AND NOT WEBGL)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fstack-protector")
endif()
@@ -554,11 +515,6 @@ if (FILAMENT_SUPPORTS_METAL)
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a metal)
endif()
# Disable ESSL 1.0 code generation.
if (NOT FILAMENT_ENABLE_FEATURE_LEVEL_0)
set(MATC_API_FLAGS ${MATC_API_FLAGS} -1)
endif()
# Enable debug info (preserves names in SPIR-V)
if (FILAMENT_ENABLE_MATDBG)
set(MATC_OPT_FLAGS ${MATC_OPT_FLAGS} -d)
@@ -766,8 +722,6 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN)
add_subdirectory(${LIBRARIES}/bluevk)
add_subdirectory(${EXTERNAL}/vkmemalloc/tnt)
set(SPIRV_HEADERS_SKIP_EXAMPLES ON)
add_subdirectory(${EXTERNAL}/spirv-headers)
endif()
set(FILAMENT_SAMPLES_BINARY_DIR ${PROJECT_BINARY_DIR}/samples)

View File

@@ -27,7 +27,7 @@ again.
## Code Style
See [CODE_STYLE.md](/CODE_STYLE.md)
See [CodeStyle.md](/CODE_STYLE.md)
## Code reviews

View File

@@ -7,3 +7,9 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- Fix possible NPE when updating fog options from Java/Kotlin
- The `emissive` property was not applied properly to `MASKED` materials, and could cause
dark fringes to appear (recompile materials)
- Allow glTF materials with transmission/volume extensions to choose their alpha mode
instead of forcing `MASKED`

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.49.3'
implementation 'com.google.android.filament:filament-android:1.42.0'
}
```
@@ -50,8 +50,8 @@ 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.49.3'
```
pod 'Filament', '~> 1.42.0'
```
### Snapshots

View File

@@ -128,15 +128,3 @@ Navigate to [Filament's release
workflow](https://github.com/google/filament/actions/workflows/release.yml). Hit the _Run workflow_
dropdown. Modify _Platform to build_ and _Release tag to build_, then hit _Run workflow_. This will
initiate a new release run.
## 11. Kick off the npm and CocoaPods release jobs
Navigate to [Filament's npm deploy
workflow](https://github.com/google/filament/actions/workflows/npm-deploy.yml).
Hit the _Run workflow_ dropdown. Modify _Release tag to deploy_ to the tag corresponding to this
release (for example, v1.42.2).
Navigate to [Filament's CocoaPods deploy
workflow](https://github.com/google/filament/actions/workflows/cocopods-deploy.yml).
Hit the _Run workflow_ dropdown. Modify _Release tag to deploy_ to the tag corresponding to this
release (for example, v1.42.2).

View File

@@ -7,93 +7,8 @@ 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.49.3
- matc: Generate stereo variants for FL0 materials [⚠️ **Recompile materials**]
## v1.49.2
## v1.49.1
## v1.49.0
- matc: Fix ESSL 1.0 codegen when using external samplers [⚠️ **Recompile materials**]
## v1.48.0
- matc: New option `-1` to disable generation of ESSL 1.0 code in Feature Level 0 materials
- matc: Support optimizations for ESSL 1.0 code [⚠️ **Recompile materials**]
## v1.47.0
- engine: Support up to 4 side-by-side stereoscopic eyes, configurable at Engine creation time. See
`Engine::Config::stereoscopicEyeCount`. [⚠️ **Recompile Materials**]
## v1.46.0
- engine: Allow instantiating Engine at a given feature level via `Engine::Builder::featureLevel`
- matc: Enable `GL_OES_standard_derivatives` extension in ESSL 1.0 shaders
- matc: Fix code generation of double sided and masked materials in ESSL 1.0 shaders
- filagui: Add support for feature level 0
- matc: Add support for post-process materials in feature level 0
- engine: Add `Material::getFeatureLevel()`
- engine: Add missing `Material::getReflectionMode()` method in Java
- engine: Support basic usage of post-processing materials on feature level 0
- engine: Fix critical GLES 2.0 bugs
- engine: Add `FILAMENT_ENABLE_FEATURE_LEVEL_0` build-time option optionally allow building Filament
without FL0 support.
## v1.45.1
- engine: Added parameter for configuring JobSystem thread count
- engine: In Java, introduce Engine.Builder
- gltfio: fix ubershader index for transmission&volume material
- engine: New tone mapper: `AgXTonemapper`.
- matinfo: Add support for viewing ESSL 1.0 shaders
- engine: Add `Renderer::getClearOptions()` [b/243846268]
- engine: Fix stable shadows (again) when an IBL rotation is used
## v1.45.0
- materials: fix alpha masked materials when MSAA is turned on [⚠️ **Recompile materials**]
- materials: better support materials with custom depth [**Recompile Materials**]
- engine: fade shadows at shadowFar distance instead of hard cutoff [⚠️ **New Material Version**]
## v1.44.0
- engine: add support for skinning with more than four bones per vertex.
- engine: remove `BloomOptions::anamorphism` which wasn't working well in most cases [**API CHANGE**]
- engine: new API to return a Material's supported variants, C++ only (b/297456590)
- build: fix emscripten-1.3.46 build
- engine: materials built for feature level 0 can now also be loaded in higher feature levels [⚠️
**New Material Version**]
## v1.43.1
## v1.43.0
- gltfio: Fix possible change of scale sign when decomposing transform matrix for animation
- engine: Fixes "stable" shadows (see b/299310624)
## v1.42.2
- Fix possible NPE when updating fog options from Java/Kotlin
- The `emissive` property was not applied properly to `MASKED` materials, and could cause
dark fringes to appear (recompile materials)
- Allow glTF materials with transmission/volume extensions to choose their alpha mode
instead of forcing `MASKED`
- Fix a crash in gltfio when not using ubershaders
- Use flatmat for mat parameter in jsbinding
- Fix TextureFlags for sheenRoughnessMap when textures of sheenRoughnessMap and sheenColorMap is same
- Directional shadows can now be transformed (b/297095805)
## v1.42.1
- Fix potential `EXC_BAD_ACCESS` with Metal backend: b/297059776
- `setFrameCompletedCallback` now takes a `backend::CallbackHandler`.
## v1.42.0
- engine: add preliminary support for instanced stereoscopic rendering [⚠️ **Recompile materials**]

View File

@@ -81,14 +81,14 @@ buildscript {
ext.versions = [
'jdk': 17,
'minSdk': 19,
'targetSdk': 34,
'compileSdk': 34,
'kotlin': '1.9.21',
'kotlin_coroutines': '1.7.3',
'targetSdk': 33,
'compileSdk': 33,
'kotlin': '1.9.0',
'kotlin_coroutines': '1.7.2',
'buildTools': '34.0.0',
'ndk': '26.1.10909125',
'androidx_core': '1.12.0',
'androidx_annotations': '1.7.0'
'ndk': '25.1.8937393',
'androidx_core': '1.10.1',
'androidx_annotations': '1.6.0'
]
ext.deps = [
@@ -104,7 +104,7 @@ buildscript {
]
dependencies {
classpath 'com.android.tools.build:gradle:8.2.0'
classpath 'com.android.tools.build:gradle:8.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
}

View File

@@ -6,6 +6,9 @@ option(FILAMENT_ENABLE_MATDBG "Enables Material debugger" OFF)
set(FILAMENT_DIR ${FILAMENT_DIST_DIR})
set(FILAMAT_FLAVOR "filamat")
if(FILAMAT_LITE)
set(FILAMAT_FLAVOR "filamat_lite")
endif()
if (FILAMENT_SUPPORTS_VULKAN)
message("Library filamat ignores Vulkan settings")

View File

@@ -1,8 +1,29 @@
android {
namespace 'com.google.android.filament.filamat'
flavorDimensions "functionality"
productFlavors {
full {
dimension "functionality"
}
lite {
dimension "functionality"
externalNativeBuild {
cmake {
arguments.add("-DFILAMAT_LITE=ON")
}
}
}
}
publishing {
singleVariant("release") {
singleVariant("fullRelease") {
withSourcesJar()
withJavadocJar()
}
singleVariant("liteRelease") {
withSourcesJar()
withJavadocJar()
}
@@ -18,9 +39,14 @@ apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
afterEvaluate { project ->
publishing {
publications {
release(MavenPublication) {
fullRelease(MavenPublication) {
artifactId = POM_ARTIFACT_ID_FULL
from components.release
from components.fullRelease
}
liteRelease(MavenPublication) {
artifactId = POM_ARTIFACT_ID_LITE
from components.liteRelease
}
}
}

View File

@@ -25,8 +25,15 @@
using namespace filament;
using namespace utils;
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Engine_nCreateEngine(JNIEnv*, jclass, jlong backend,
jlong sharedContext) {
return (jlong) Engine::create((Engine::Backend) backend, nullptr, (void*) sharedContext);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nDestroyEngine(JNIEnv*, jclass, jlong nativeEngine) {
Java_com_google_android_filament_Engine_nDestroyEngine(JNIEnv*, jclass,
jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
Engine::destroy(&engine);
}
@@ -384,13 +391,6 @@ Java_com_google_android_filament_Engine_nFlushAndWait(JNIEnv*, jclass,
engine->flushAndWait();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nFlush(JNIEnv*, jclass,
jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
engine->flush();
}
// Managers...
extern "C" JNIEXPORT jlong JNICALL
@@ -435,13 +435,6 @@ Java_com_google_android_filament_Engine_nIsAutomaticInstancingEnabled(JNIEnv*, j
return (jboolean)engine->isAutomaticInstancingEnabled();
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Engine_nGetMaxStereoscopicEyes(JNIEnv*, jclass, jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
return (jlong) engine->getMaxStereoscopicEyes();
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Engine_nGetSupportedFeatureLevel(JNIEnv *, jclass,
jlong nativeEngine) {
@@ -461,57 +454,4 @@ Java_com_google_android_filament_Engine_nGetActiveFeatureLevel(JNIEnv *, jclass,
jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
return (jint)engine->getActiveFeatureLevel();
}
extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_Engine_nCreateBuilder(JNIEnv*,
jclass) {
Engine::Builder* builder = new Engine::Builder{};
return (jlong) builder;
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nDestroyBuilder(JNIEnv*,
jclass, jlong nativeBuilder) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
delete builder;
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderBackend(
JNIEnv*, jclass, jlong nativeBuilder, jlong backend) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
builder->backend((Engine::Backend) backend);
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderConfig(JNIEnv*,
jclass, jlong nativeBuilder, jlong commandBufferSizeMB, jlong perRenderPassArenaSizeMB,
jlong driverHandleArenaSizeMB, jlong minCommandBufferSizeMB, jlong perFrameCommandsSizeMB,
jlong jobSystemThreadCount, jlong stereoscopicEyeCount) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
Engine::Config config = {
.commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
.perRenderPassArenaSizeMB = (uint32_t) perRenderPassArenaSizeMB,
.driverHandleArenaSizeMB = (uint32_t) driverHandleArenaSizeMB,
.minCommandBufferSizeMB = (uint32_t) minCommandBufferSizeMB,
.perFrameCommandsSizeMB = (uint32_t) perFrameCommandsSizeMB,
.jobSystemThreadCount = (uint32_t) jobSystemThreadCount,
.stereoscopicEyeCount = (uint8_t) stereoscopicEyeCount,
};
builder->config(&config);
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderFeatureLevel(
JNIEnv*, jclass, jlong nativeBuilder, jint ordinal) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
builder->featureLevel((Engine::FeatureLevel)ordinal);
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderSharedContext(
JNIEnv*, jclass, jlong nativeBuilder, jlong sharedContext) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
builder->sharedContext((void*) sharedContext);
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
return (jlong) builder->build();
}
}

View File

@@ -38,7 +38,7 @@ Java_com_google_android_filament_EntityManager_nCreateArray(JNIEnv* env, jclass,
// (which it is), but still.
em->create((size_t) n, reinterpret_cast<Entity *>(entities));
env->ReleaseIntArrayElements(entities_, entities, JNI_ABORT);
env->ReleaseIntArrayElements(entities_, entities, 0);
}
extern "C" JNIEXPORT jint JNICALL

View File

@@ -79,8 +79,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
jfloat shadowFarHint, jboolean stable, jboolean lispsm,
jfloat polygonOffsetConstant, jfloat polygonOffsetSlope,
jboolean screenSpaceContactShadows, jint stepCount,
jfloat maxShadowDistance, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius,
jfloatArray transform) {
jfloat maxShadowDistance, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius) {
LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder;
LightManager::ShadowOptions shadowOptions {
.mapSize = (uint32_t)mapSize,
@@ -103,18 +102,12 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
},
.shadowBulbRadius = shadowBulbRadius
};
jfloat *nativeSplits = env->GetFloatArrayElements(splitPositions, NULL);
const jsize splitCount = std::min((jsize) 3, env->GetArrayLength(splitPositions));
std::copy_n(nativeSplits, splitCount, shadowOptions.cascadeSplitPositions);
for (jsize i = 0; i < splitCount; i++) {
shadowOptions.cascadeSplitPositions[i] = nativeSplits[i];
}
env->ReleaseFloatArrayElements(splitPositions, nativeSplits, 0);
jfloat* nativeTransform = env->GetFloatArrayElements(transform, NULL);
std::copy_n(nativeTransform,
std::min(4, env->GetArrayLength(transform)),
shadowOptions.transform.xyzw.v);
env->ReleaseFloatArrayElements(transform, nativeTransform, 0);
builder->shadowOptions(shadowOptions);
}

View File

@@ -19,7 +19,6 @@
#include <filament/Material.h>
#include "common/NioUtils.h"
#include "common/CallbackUtils.h"
using namespace filament;
@@ -106,22 +105,6 @@ Java_com_google_android_filament_Material_nGetRefractionType(JNIEnv*, jclass,
return (jint) material->getRefractionType();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetReflectionMode(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint) material->getReflectionMode();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetFeatureLevel(JNIEnv*, jclass,
jlong nativeMaterial) {
Material* material = (Material*) nativeMaterial;
return (jint) material->getFeatureLevel();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Material_nGetVertexDomain(JNIEnv*, jclass,
@@ -272,17 +255,3 @@ Java_com_google_android_filament_Material_nHasParameter(JNIEnv* env, jclass,
env->ReleaseStringUTFChars(name_, name);
return (jboolean) hasParameter;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_Material_nCompile(JNIEnv *env, jclass clazz,
jlong nativeMaterial, jint priority, jint variants, jobject handler, jobject runnable) {
Material* material = (Material*) nativeMaterial;
JniCallback* jniCallback = JniCallback::make(env, handler, runnable);
material->compile(
(Material::CompilerPriorityQueue) priority,
(UserVariantFilterBit) variants,
jniCallback->getHandler(), [jniCallback](Material*){
JniCallback::postToJavaAndDestroy(jniCallback);
});
}

View File

@@ -205,7 +205,7 @@ Java_com_google_android_filament_MaterialInstance_nSetIntParameterArray(JNIEnv *
break;
}
env->ReleaseIntArrayElements(v_, v, JNI_ABORT);
env->ReleaseIntArrayElements(v_, v, 0);
env->ReleaseStringUTFChars(name_, name);
}

View File

@@ -71,13 +71,6 @@ Java_com_google_android_filament_Scene_nRemoveEntities(JNIEnv *env, jclass type,
env->ReleaseIntArrayElements(entities, (jint*) nativeEntities, JNI_ABORT);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Scene_nGetEntityCount(JNIEnv *env, jclass type,
jlong nativeScene) {
Scene* scene = (Scene*) nativeScene;
return (jint) scene->getEntityCount();
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Scene_nGetRenderableCount(JNIEnv *env, jclass type,
jlong nativeScene) {
@@ -98,22 +91,3 @@ Java_com_google_android_filament_Scene_nHasEntity(JNIEnv *env, jclass type, jlon
Entity entity = Entity::import(entityId);
return (jboolean) scene->hasEntity(entity);
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Scene_nGetEntities(JNIEnv *env, jclass ,
jlong nativeScene, jintArray outArray, jint length) {
Scene const* const scene = (Scene*) nativeScene;
if (length < scene->getEntityCount()) {
// should not happen because we already checked on the java side
return JNI_FALSE;
}
jint *out = (jint *) env->GetIntArrayElements(outArray, nullptr);
scene->forEach([out, length, i = 0](Entity entity)mutable {
if (i < length) { // this is just paranoia here
out[i++] = (jint) entity.getId();
}
});
env->ReleaseIntArrayElements(outArray, (jint*) out, 0);
return JNI_TRUE;
}

View File

@@ -47,11 +47,6 @@ Java_com_google_android_filament_ToneMapper_nCreateFilmicToneMapper(JNIEnv*, jcl
return (jlong) new FilmicToneMapper();
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_ToneMapper_nCreateAgxToneMapper(JNIEnv*, jclass, jint look) {
return (jlong) new AgxToneMapper(AgxToneMapper::AgxLook(look));
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_ToneMapper_nCreateGenericToneMapper(JNIEnv*, jclass,
jfloat contrast, jfloat midGrayIn, jfloat midGrayOut, jfloat hdrMax) {

View File

@@ -282,7 +282,7 @@ Java_com_google_android_filament_View_nSetSSCTOptions(JNIEnv *, jclass, jlong na
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetBloomOptions(JNIEnv*, jclass,
jlong nativeView, jlong nativeTexture,
jfloat dirtStrength, jfloat strength, jint resolution, jint levels,
jfloat dirtStrength, jfloat strength, jint resolution, jfloat anamorphism, jint levels,
jint blendMode, jboolean threshold, jboolean enabled, jfloat highlight,
jboolean lensFlare, jboolean starburst, jfloat chromaticAberration, jint ghostCount,
jfloat ghostSpacing, jfloat ghostThreshold, jfloat haloThickness, jfloat haloRadius,
@@ -294,6 +294,7 @@ Java_com_google_android_filament_View_nSetBloomOptions(JNIEnv*, jclass,
.dirtStrength = dirtStrength,
.strength = strength,
.resolution = (uint32_t)resolution,
.anamorphism = anamorphism,
.levels = (uint8_t)levels,
.blendMode = (View::BloomOptions::BlendMode)blendMode,
.threshold = (bool)threshold,
@@ -480,17 +481,6 @@ Java_com_google_android_filament_View_nIsStencilBufferEnabled(JNIEnv *, jclass,
return view->isStencilBufferEnabled();
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetStereoscopicOptions(JNIEnv *, jclass, jlong nativeView,
jboolean enabled) {
View* view = (View*) nativeView;
View::StereoscopicOptions options {
.enabled = (bool) enabled
};
view->setStereoscopicOptions(options);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetGuardBandOptions(JNIEnv *, jclass,

View File

@@ -111,8 +111,6 @@ public class Engine {
private long mNativeObject;
private Config mConfig;
@NonNull private final TransformManager mTransformManager;
@NonNull private final LightManager mLightManager;
@NonNull private final RenderableManager mRenderableManager;
@@ -152,219 +150,16 @@ public class Engine {
FEATURE_LEVEL_0,
/** OpenGL ES 3.0 features (default) */
FEATURE_LEVEL_1,
/** OpenGL ES 3.1 features + 16 textures units + cubemap arrays */
FEATURE_LEVEL_2,
/** OpenGL ES 3.1 features + 31 textures units + cubemap arrays */
FEATURE_LEVEL_3,
FEATURE_LEVEL_2
};
/**
* Constructs <code>Engine</code> objects using a builder pattern.
*/
public static class Builder {
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
private final BuilderFinalizer mFinalizer;
private final long mNativeBuilder;
private Config mConfig;
public Builder() {
mNativeBuilder = nCreateBuilder();
mFinalizer = new BuilderFinalizer(mNativeBuilder);
}
/**
* Sets the {@link Backend} for the Engine.
*
* @param backend Driver backend to use
* @return A reference to this Builder for chaining calls.
*/
public Builder backend(Backend backend) {
nSetBuilderBackend(mNativeBuilder, backend.ordinal());
return this;
}
/**
* Sets a sharedContext for the Engine.
*
* @param sharedContext A platform-dependant OpenGL context used as a shared context
* when creating filament's internal context. On Android this parameter
* <b>must be</b> an instance of {@link android.opengl.EGLContext}.
* @return A reference to this Builder for chaining calls.
*/
public Builder sharedContext(Object sharedContext) {
if (Platform.get().validateSharedContext(sharedContext)) {
nSetBuilderSharedContext(mNativeBuilder,
Platform.get().getSharedContextNativeHandle(sharedContext));
return this;
}
throw new IllegalArgumentException("Invalid shared context " + sharedContext);
}
/**
* Configure the Engine with custom parameters.
*
* @param config A {@link Config} object
* @return A reference to this Builder for chaining calls.
*/
public Builder config(Config config) {
mConfig = config;
nSetBuilderConfig(mNativeBuilder, config.commandBufferSizeMB,
config.perRenderPassArenaSizeMB, config.driverHandleArenaSizeMB,
config.minCommandBufferSizeMB, config.perFrameCommandsSizeMB,
config.jobSystemThreadCount, config.stereoscopicEyeCount);
return this;
}
/**
* Sets the initial featureLevel for the Engine.
*
* @param featureLevel The feature level at which initialize Filament.
* @return A reference to this Builder for chaining calls.
*/
public Builder featureLevel(FeatureLevel featureLevel) {
nSetBuilderFeatureLevel(mNativeBuilder, featureLevel.ordinal());
return this;
}
/**
* Creates an instance of Engine
*
* @return A newly created <code>Engine</code>, or <code>null</code> if the GPU driver couldn't
* be initialized, for instance if it doesn't support the right version of OpenGL or
* OpenGL ES.
*
* @exception IllegalStateException can be thrown if there isn't enough memory to
* allocate the command buffer.
*/
public Engine build() {
long nativeEngine = nBuilderBuild(mNativeBuilder);
if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine");
return new Engine(nativeEngine, mConfig);
}
private static class BuilderFinalizer {
private final long mNativeObject;
BuilderFinalizer(long nativeObject) {
mNativeObject = nativeObject;
}
@Override
public void finalize() {
try {
super.finalize();
} catch (Throwable t) { // Ignore
} finally {
nDestroyBuilder(mNativeObject);
}
}
}
}
/**
* Parameters for customizing the initialization of {@link Engine}.
*/
public static class Config {
// #defines in Engine.h
private static final long FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB = 3;
private static final long FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB = 2;
private static final long FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB = 1;
private static final long FILAMENT_COMMAND_BUFFER_SIZE_IN_MB =
FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB * 3;
/**
* Size in MiB of the low-level command buffer arena.
*
* Each new command buffer is allocated from here. If this buffer is too small the program
* might terminate or rendering errors might occur.
*
* This is typically set to minCommandBufferSizeMB * 3, so that up to 3 frames can be
* batched-up at once.
*
* This value affects the application's memory usage.
*/
public long commandBufferSizeMB = FILAMENT_COMMAND_BUFFER_SIZE_IN_MB;
/**
* Size in MiB of the per-frame data arena.
*
* This is the main arena used for allocations when preparing a frame.
* e.g.: Froxel data and high-level commands are allocated from this arena.
*
* If this size is too small, the program will abort on debug builds and have undefined
* behavior otherwise.
*
* This value affects the application's memory usage.
*/
public long perRenderPassArenaSizeMB = FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB;
/**
* Size in MiB of the backend's handle arena.
*
* Backends will fallback to slower heap-based allocations when running out of space and
* log this condition.
*
* If 0, then the default value for the given platform is used
*
* This value affects the application's memory usage.
*/
public long driverHandleArenaSizeMB = 0;
/**
* Minimum size in MiB of a low-level command buffer.
*
* This is how much space is guaranteed to be available for low-level commands when a new
* buffer is allocated. If this is too small, the engine might have to stall to wait for
* more space to become available, this situation is logged.
*
* This value does not affect the application's memory usage.
*/
public long minCommandBufferSizeMB = FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB;
/**
* Size in MiB of the per-frame high level command buffer.
*
* This buffer is related to the number of draw calls achievable within a frame, if it is
* too small, the program will abort on debug builds and have undefined behavior otherwise.
*
* It is allocated from the 'per-render-pass arena' above. Make sure that at least 1 MiB is
* left in the per-render-pass arena when deciding the size of this buffer.
*
* This value does not affect the application's memory usage.
*/
public long perFrameCommandsSizeMB = FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB;
/**
* Number of threads to use in Engine's JobSystem.
*
* Engine uses a utils::JobSystem to carry out paralleization of Engine workloads. This
* value sets the number of threads allocated for JobSystem. Configuring this value can be
* helpful in CPU-constrained environments where too many threads can cause contention of
* CPU and reduce performance.
*
* The default value is 0, which implies that the Engine will use a heuristic to determine
* the number of threads to use.
*/
public long jobSystemThreadCount = 0;
/**
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
* between 1 and Engine#getMaxStereoscopicEyes() (inclusive).
*
* @see View#setStereoscopicOptions
* @see Engine#getMaxStereoscopicEyes
*/
public long stereoscopicEyeCount = 2;
}
private Engine(long nativeEngine, Config config) {
private Engine(long nativeEngine) {
mNativeObject = nativeEngine;
mTransformManager = new TransformManager(nGetTransformManager(nativeEngine));
mLightManager = new LightManager(nGetLightManager(nativeEngine));
mRenderableManager = new RenderableManager(nGetRenderableManager(nativeEngine));
mEntityManager = new EntityManager(nGetEntityManager(nativeEngine));
mConfig = config;
}
/**
@@ -382,7 +177,9 @@ public class Engine {
*/
@NonNull
public static Engine create() {
return new Builder().build();
long nativeEngine = nCreateEngine(0, 0);
if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine");
return new Engine(nativeEngine);
}
/**
@@ -402,9 +199,9 @@ public class Engine {
*/
@NonNull
public static Engine create(@NonNull Backend backend) {
return new Builder()
.backend(backend)
.build();
long nativeEngine = nCreateEngine(backend.ordinal(), 0);
if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine");
return new Engine(nativeEngine);
}
/**
@@ -426,9 +223,13 @@ public class Engine {
*/
@NonNull
public static Engine create(@NonNull Object sharedContext) {
return new Builder()
.sharedContext(sharedContext)
.build();
if (Platform.get().validateSharedContext(sharedContext)) {
long nativeEngine = nCreateEngine(0,
Platform.get().getSharedContextNativeHandle(sharedContext));
if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine");
return new Engine(nativeEngine);
}
throw new IllegalArgumentException("Invalid shared context " + sharedContext);
}
/**
@@ -495,23 +296,17 @@ public class Engine {
}
/**
* Activate all features of a given feature level. If an explicit feature level is not specified
* at Engine initialization time via {@link Builder#featureLevel}, the default feature level is
* {@link FeatureLevel#FEATURE_LEVEL_0} on devices not compatible with GLES 3.0; otherwise, the
* default is {@link FeatureLevel::FEATURE_LEVEL_1}. The selected feature level must not be
* higher than the value returned by {@link #getActiveFeatureLevel} and it's not possible lower
* the active feature level. Additionally, it is not possible to modify the feature level at all
* if the Engine was initialized at {@link FeatureLevel#FEATURE_LEVEL_0}.
* Activate all features of a given feature level. By default FeatureLevel::FEATURE_LEVEL_1 is
* active. The selected feature level must not be higher than the value returned by
* getActiveFeatureLevel() and it's not possible lower the active feature level.
*
* @param featureLevel the feature level to activate. If featureLevel is lower than {@link
* #getActiveFeatureLevel}, the current (higher) feature level is kept. If
* featureLevel is higher than {@link #getSupportedFeatureLevel}, or if the
* engine was initialized at feature level 0, an exception is thrown, or the
* program is terminated if exceptions are disabled.
* @param featureLevel the feature level to activate. If featureLevel is lower than
* getActiveFeatureLevel(), the current (higher) feature level is kept.
* If featureLevel is higher than getSupportedFeatureLevel(), an exception
* is thrown, or the program is terminated if exceptions are disabled.
*
* @return the active feature level.
*
* @see Builder#featureLevel
* @see #getSupportedFeatureLevel
* @see #getActiveFeatureLevel
*/
@@ -557,37 +352,6 @@ public class Engine {
return nIsAutomaticInstancingEnabled(getNativeObject());
}
/**
* Retrieves the configuration settings of this {@link Engine}.
*
* This method returns the configuration object that was supplied to the Engine's {@link
* Builder#config} method during the creation of this Engine. If the {@link Builder::config}
* method was not explicitly called (or called with null), this method returns the default
* configuration settings.
*
* @return a {@link Config} object with this Engine's configuration
* @see Builder#config
*/
@NonNull
public Config getConfig() {
if (mConfig == null) {
mConfig = new Config();
}
return mConfig;
}
/**
* Returns the maximum number of stereoscopic eyes supported by Filament. The actual number of
* eyes rendered is set at Engine creation time with the {@link
* Engine#Config#stereoscopicEyeCount} setting.
*
* @return the max number of stereoscopic eyes supported
* @see Engine#Config#stereoscopicEyeCount
*/
public long getMaxStereoscopicEyes() {
return nGetMaxStereoscopicEyes(getNativeObject());
}
// SwapChain
@@ -1124,18 +888,6 @@ public class Engine {
nFlushAndWait(getNativeObject());
}
/**
* Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) but does not wait
* for commands to be either executed or the hardware finished.
*
* <p>This is typically used after creating a lot of objects to start draining the command
* queue which has a limited size.</p>
*/
public void flush() {
nFlush(getNativeObject());
}
@UsedByReflection("TextureHelper.java")
public long getNativeObject() {
if (mNativeObject == 0) {
@@ -1162,6 +914,7 @@ public class Engine {
}
}
private static native long nCreateEngine(long backend, long sharedContext);
private static native void nDestroyEngine(long nativeEngine);
private static native long nGetBackend(long nativeEngine);
private static native long nCreateSwapChain(long nativeEngine, Object nativeWindow, long flags);
@@ -1208,7 +961,6 @@ public class Engine {
private static native boolean nIsValidSwapChain(long nativeEngine, long nativeSwapChain);
private static native void nDestroyEntity(long nativeEngine, int entity);
private static native void nFlushAndWait(long nativeEngine);
private static native void nFlush(long nativeEngine);
private static native long nGetTransformManager(long nativeEngine);
private static native long nGetLightManager(long nativeEngine);
private static native long nGetRenderableManager(long nativeEngine);
@@ -1216,19 +968,7 @@ public class Engine {
private static native long nGetEntityManager(long nativeEngine);
private static native void nSetAutomaticInstancingEnabled(long nativeEngine, boolean enable);
private static native boolean nIsAutomaticInstancingEnabled(long nativeEngine);
private static native long nGetMaxStereoscopicEyes(long nativeEngine);
private static native int nGetSupportedFeatureLevel(long nativeEngine);
private static native int nSetActiveFeatureLevel(long nativeEngine, int ordinal);
private static native int nGetActiveFeatureLevel(long nativeEngine);
private static native long nCreateBuilder();
private static native void nDestroyBuilder(long nativeBuilder);
private static native void nSetBuilderBackend(long nativeBuilder, long backend);
private static native void nSetBuilderConfig(long nativeBuilder, long commandBufferSizeMB,
long perRenderPassArenaSizeMB, long driverHandleArenaSizeMB,
long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount,
long stereoscopicEyeCount);
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
private static native long nBuilderBuild(long nativeBuilder);
}

View File

@@ -258,7 +258,6 @@ public class LightManager {
* shadows that are too far and wouldn't contribute to the scene much, improving
* performance and quality. This value is always positive.
* Use 0.0f to use the camera far distance.
* This only affect directional lights.
*/
public float shadowFar = 0.0f;
@@ -369,17 +368,6 @@ public class LightManager {
* enabled. (2cm by default).
*/
public float shadowBulbRadius = 0.02f;
/**
* Transforms the shadow direction. Must be a unit quaternion.
* The default is identity.
* Ignored if the light type isn't directional. For artistic use. Use with caution.
* The quaternion is stored as the imaginary part in the first 3 elements and the real
* part in the last element of the transform array.
*/
@NonNull
@Size(min = 4, max = 4)
public float[] transform = { 0.0f, 0.0f, 0.0f, 1.0f };
}
public static class ShadowCascades {
@@ -518,7 +506,7 @@ public class LightManager {
options.polygonOffsetConstant, options.polygonOffsetSlope,
options.screenSpaceContactShadows,
options.stepCount, options.maxShadowDistance,
options.elvsm, options.blurWidth, options.shadowBulbRadius, options.transform);
options.elvsm, options.blurWidth, options.shadowBulbRadius);
return this;
}
@@ -1181,7 +1169,7 @@ public class LightManager {
boolean stable, boolean lispsm,
float polygonOffsetConstant, float polygonOffsetSlope,
boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance,
boolean elvsm, float blurWidth, float shadowBulbRadius, float[] transform);
boolean elvsm, float blurWidth, float shadowBulbRadius);
private static native void nBuilderCastLight(long nativeBuilder, boolean enabled);
private static native void nBuilderPosition(long nativeBuilder, float x, float y, float z);
private static native void nBuilderDirection(long nativeBuilder, float x, float y, float z);

View File

@@ -18,11 +18,9 @@ package com.google.android.filament;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Size;
import com.google.android.filament.proguard.UsedByNative;
import com.google.android.filament.Engine.FeatureLevel;
import java.nio.Buffer;
import java.util.ArrayList;
@@ -48,8 +46,6 @@ public class Material {
static final BlendingMode[] sBlendingModeValues = BlendingMode.values();
static final RefractionMode[] sRefractionModeValues = RefractionMode.values();
static final RefractionType[] sRefractionTypeValues = RefractionType.values();
static final ReflectionMode[] sReflectionModeValues = ReflectionMode.values();
static final FeatureLevel[] sFeatureLevelValues = FeatureLevel.values();
static final VertexDomain[] sVertexDomainValues = VertexDomain.values();
static final CullingMode[] sCullingModeValues = CullingMode.values();
static final VertexBuffer.VertexAttribute[] sVertexAttributeValues =
@@ -185,18 +181,6 @@ public class Material {
THIN
}
/**
* Supported reflection modes
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/lighting:reflections">
* Lighting: reflections</a>
*/
public enum ReflectionMode {
DEFAULT,
SCREEN_SPACE
}
/**
* Supported types of vertex domains
*
@@ -239,31 +223,6 @@ public class Material {
FRONT_AND_BACK
}
public enum CompilerPriorityQueue {
HIGH,
LOW
}
public static class UserVariantFilterBit {
/** Directional lighting */
public static int DIRECTIONAL_LIGHTING = 0x01;
/** Dynamic lighting */
public static int DYNAMIC_LIGHTING = 0x02;
/** Shadow receiver */
public static int SHADOW_RECEIVER = 0x04;
/** Skinning */
public static int SKINNING = 0x08;
/** Fog */
public static int FOG = 0x10;
/** Variance shadow maps */
public static int VSM = 0x20;
/** Screen-space reflections */
public static int SSR = 0x40;
/** Instanced stereo rendering */
public static int STE = 0x80;
public static int ALL = 0xFF;
}
@UsedByNative("Material.cpp")
public static class Parameter {
private static final Type[] sTypeValues = Type.values();
@@ -378,55 +337,6 @@ public class Material {
}
}
/**
* Asynchronously ensures that a subset of this Material's variants are compiled. After issuing
* several compile() calls in a row, it is recommended to call {@link Engine#flush}
* such that the backend can start the compilation work as soon as possible.
* The provided callback is guaranteed to be called on the main thread after all specified
* variants of the material are compiled. This can take hundreds of milliseconds.
*<p>
* If all the material's variants are already compiled, the callback will be scheduled as
* soon as possible, but this might take a few dozen millisecond, corresponding to how
* many previous frames are enqueued in the backend. This also varies by backend. Therefore,
* it is recommended to only call this method once per material shortly after creation.
*</p>
*<p>
* If the same variant is scheduled for compilation multiple times, the first scheduling
* takes precedence; later scheduling are ignored.
*</p>
*<p>
* caveat: A consequence is that if a variant is scheduled on the low priority queue and later
* scheduled again on the high priority queue, the later scheduling is ignored.
* Therefore, the second callback could be called before the variant is compiled.
* However, the first callback, if specified, will trigger as expected.
*</p>
*<p>
* The callback is guaranteed to be called. If the engine is destroyed while some material
* variants are still compiling or in the queue, these will be discarded and the corresponding
* callback will be called. In that case however the Material pointer passed to the callback
* is guaranteed to be invalid (either because it's been destroyed by the user already, or,
* because it's been cleaned-up by the Engine).
*</p>
*<p>
* {@link UserVariantFilterBit#ALL} should be used with caution. Only variants that an application
* needs should be included in the variants argument. For example, the STE variant is only used
* for stereoscopic rendering. If an application is not planning to render in stereo, this bit
* should be turned off to avoid unnecessary material compilations.
*</p>
* @param priority Which priority queue to use, LOW or HIGH.
* @param variants Variants to include to the compile command.
* @param handler An {@link java.util.concurrent.Executor Executor}. On Android this can also be a {@link android.os.Handler Handler}.
* @param callback callback called on the main thread when the compilation is done on
* by backend.
*/
public void compile(@NonNull CompilerPriorityQueue priority,
int variants,
@Nullable Object handler,
@Nullable Runnable callback) {
nCompile(getNativeObject(), priority.ordinal(), variants, handler, callback);
}
/**
* Creates a new instance of this material. Material instances should be freed using
* {@link Engine#destroyMaterialInstance(MaterialInstance)}.
@@ -527,28 +437,6 @@ public class Material {
return EnumCache.sRefractionTypeValues[nGetRefractionType(getNativeObject())];
}
/**
* Returns the reflection mode of this material.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/lighting:reflections">
* Lighting: reflections</a>
*/
public ReflectionMode getReflectionMode() {
return EnumCache.sReflectionModeValues[nGetReflectionMode(getNativeObject())];
}
/**
* Returns the minimum required feature level for this material.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/general:featurelevel">
* General: featureLevel</a>
*/
public FeatureLevel getFeatureLevel() {
return EnumCache.sFeatureLevelValues[nGetFeatureLevel(getNativeObject())];
}
/**
* Returns the vertex domain of this material.
*
@@ -1028,7 +916,6 @@ public class Material {
private static native long nCreateInstanceWithName(long nativeMaterial, @NonNull String name);
private static native long nGetDefaultInstance(long nativeMaterial);
private static native void nCompile(long nativeMaterial, int priority, int variants, Object handler, Runnable runnable);
private static native String nGetName(long nativeMaterial);
private static native int nGetShading(long nativeMaterial);
private static native int nGetInterpolation(long nativeMaterial);
@@ -1045,8 +932,6 @@ public class Material {
private static native float nGetSpecularAntiAliasingThreshold(long nativeMaterial);
private static native int nGetRefractionMode(long nativeMaterial);
private static native int nGetRefractionType(long nativeMaterial);
private static native int nGetReflectionMode(long nativeMaterial);
private static native int nGetFeatureLevel(long nativeMaterial);
private static native int nGetParameterCount(long nativeMaterial);

View File

@@ -16,7 +16,6 @@
package com.google.android.filament;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
@@ -147,29 +146,18 @@ public class Scene {
}
/**
* Returns the total number of Entities in the <code>Scene</code>, whether alive or not.
* Returns the number of {@link RenderableManager} components in the <code>Scene</code>.
*
* @return the total number of Entities in the <code>Scene</code>.
*/
public int getEntityCount() {
return nGetEntityCount(getNativeObject());
}
/**
* Returns the number of active (alive) {@link RenderableManager} components in the
* <code>Scene</code>.
*
* @return number of {@link RenderableManager} components in the <code>Scene</code>.
* @return number of {@link RenderableManager} components in the <code>Scene</code>..
*/
public int getRenderableCount() {
return nGetRenderableCount(getNativeObject());
}
/**
* Returns the number of active (alive) {@link LightManager} components in the
* <code>Scene</code>.
* Returns the number of {@link LightManager} components in the <code>Scene</code>.
*
* @return number of {@link LightManager} components in the <code>Scene</code>.
* @return number of {@link LightManager} components in the <code>Scene</code>..
*/
public int getLightCount() {
return nGetLightCount(getNativeObject());
@@ -191,52 +179,6 @@ public class Scene {
return mNativeObject;
}
/**
* Returns the list of all entities in the Scene. If outArray is provided and large enough,
* it is used to store the list and returned, otherwise a new array is allocated and returned.
* @param outArray an array to store the list of entities in the scene.
* @return outArray if it was used or a newly allocated array.
* @see #getEntityCount
*/
public int[] getEntities(@Nullable int[] outArray) {
int c = getEntityCount();
if (outArray == null || outArray.length < c) {
outArray = new int[c];
}
boolean success = nGetEntities(getNativeObject(), outArray, outArray.length);
if (!success) {
throw new IllegalStateException("Error retriving Scene's entities");
}
return outArray;
}
/**
* Returns the list of all entities in the Scene in a newly allocated array.
* @return an array containing the list of all entities in the scene.
* @see #getEntityCount
*/
public int[] getEntities() {
return getEntities(null);
}
public interface EntityProcessor {
void process(@Entity int entity);
}
/**
* Invokes user functor on each entity in the scene.
*
* It is not allowed to add or remove an entity from the scene within the functor.
*
* @param entityProcessor User provided functor called for each entity in the scene
*/
public void forEach(@NonNull EntityProcessor entityProcessor) {
int[] entities = getEntities(null);
for (int entity : entities) {
entityProcessor.process(entity);
}
}
void clearNativeObject() {
mNativeObject = 0;
}
@@ -247,9 +189,7 @@ public class Scene {
private static native void nAddEntities(long nativeScene, int[] entities);
private static native void nRemove(long nativeScene, int entity);
private static native void nRemoveEntities(long nativeScene, int[] entities);
private static native int nGetEntityCount(long nativeScene);
private static native int nGetRenderableCount(long nativeScene);
private static native int nGetLightCount(long nativeScene);
private static native boolean nHasEntity(long nativeScene, int entity);
private static native boolean nGetEntities(long nativeScene, int[] outArray, int length);
}

View File

@@ -103,31 +103,6 @@ public class SwapChain {
*/
public static final long CONFIG_SRGB_COLORSPACE = 0x10;
/**
* Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer.
*
* This flag is necessary when using View::setStencilBufferEnabled and rendering directly into
* the SwapChain (when post-processing is disabled).
*
* The specific format of the stencil buffer depends on platform support. The following pixel
* formats are tried, in order of preference:
*
* Depth only (without CONFIG_HAS_STENCIL_BUFFER):
* - DEPTH32F
* - DEPTH24
*
* Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER):
* - DEPTH32F_STENCIL8
* - DEPTH24F_STENCIL8
*
* Note that enabling the stencil buffer may hinder depth precision and should only be used if
* necessary.
*
* @see View#setStencilBufferEnabled
* @see View#setPostProcessingEnabled
*/
public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20;
SwapChain(long nativeSwapChain, Object surface) {
mNativeObject = nativeSwapChain;
mSurface = surface;

View File

@@ -849,10 +849,6 @@ public class Texture {
public static final int SAMPLEABLE = 0x10;
/** Texture can be used as a subpass input */
public static final int SUBPASS_INPUT = 0x20;
/** Texture can be used the source of a blit() */
public static final int BLIT_SRC = 0x40;
/** Texture can be used the destination of a blit() */
public static final int BLIT_DST = 0x80;
/** by default textures are <code>UPLOADABLE</code> and <code>SAMPLEABLE</code>*/
public static final int DEFAULT = UPLOADABLE | SAMPLEABLE;
}

View File

@@ -100,45 +100,6 @@ public class ToneMapper {
}
}
/**
* AgX tone mapping operator.
*/
public static class Agx extends ToneMapper {
public enum AgxLook {
/**
* Base contrast with no look applied
*/
NONE,
/**
* A punchy and more chroma laden look for sRGB displays
*/
PUNCHY,
/**
* A golden tinted, slightly washed look for BT.1886 displays
*/
GOLDEN
}
/**
* Builds a new AgX tone mapper with no look applied.
*/
public Agx() {
this(AgxLook.NONE);
}
/**
* Builds a new AgX tone mapper.
*
* @param look: an optional creative adjustment to contrast and saturation
*/
public Agx(AgxLook look) {
super(nCreateAgxToneMapper(look.ordinal()));
}
}
/**
* Generic tone mapping operator that gives control over the tone mapping
* curve. This operator can be used to control the aesthetics of the final
@@ -233,7 +194,6 @@ public class ToneMapper {
private static native long nCreateACESToneMapper();
private static native long nCreateACESLegacyToneMapper();
private static native long nCreateFilmicToneMapper();
private static native long nCreateAgxToneMapper(int look);
private static native long nCreateGenericToneMapper(
float contrast, float midGrayIn, float midGrayOut, float hdrMax);

View File

@@ -75,7 +75,6 @@ public class View {
private AmbientOcclusionOptions mAmbientOcclusionOptions;
private BloomOptions mBloomOptions;
private FogOptions mFogOptions;
private StereoscopicOptions mStereoscopicOptions;
private RenderTarget mRenderTarget;
private BlendMode mBlendMode;
private DepthOfFieldOptions mDepthOfFieldOptions;
@@ -905,7 +904,7 @@ public class View {
mBloomOptions = options;
nSetBloomOptions(getNativeObject(), options.dirt != null ? options.dirt.getNativeObject() : 0,
options.dirtStrength, options.strength, options.resolution,
options.levels, options.blendMode.ordinal(),
options.anamorphism, options.levels, options.blendMode.ordinal(),
options.threshold, options.enabled, options.highlight,
options.lensFlare, options.starburst, options.chromaticAberration,
options.ghostCount, options.ghostSpacing, options.ghostThreshold,
@@ -1033,8 +1032,7 @@ public class View {
* </p>
*
* <p>
* If post-processing is disabled, then the SwapChain must have the CONFIG_HAS_STENCIL_BUFFER
* flag set in order to use the stencil buffer.
* Post-processing must be enabled in order to use the stencil buffer.
* </p>
*
* <p>
@@ -1056,51 +1054,6 @@ public class View {
return nIsStencilBufferEnabled(getNativeObject());
}
/**
* Sets the stereoscopic rendering options for this view.
*
* <p>
* Currently, only one type of stereoscopic rendering is supported: side-by-side.
* Side-by-side stereo rendering splits the viewport into two halves: a left and right half.
* Eye 0 will render to the left half, while Eye 1 will render into the right half.
* </p>
*
* <p>
* Currently, the following features are not supported with stereoscopic rendering:
* - post-processing
* - shadowing
* - punctual lights
* </p>
*
* <p>
* Stereo rendering depends on device and platform support. To check if stereo rendering is
* supported, use {@link Engine#isStereoSupported()}. If stereo rendering is not supported, then
* the stereoscopic options have no effect.
* </p>
*
* @param options The stereoscopic options to use on this view
* @see #getStereoscopicOptions
*/
public void setStereoscopicOptions(@NonNull StereoscopicOptions options) {
mStereoscopicOptions = options;
nSetStereoscopicOptions(getNativeObject(), options.enabled);
}
/**
* Gets the stereoscopic options.
*
* @return options Stereoscopic options currently set.
* @see #setStereoscopicOptions
*/
@NonNull
public StereoscopicOptions getStereoscopicOptions() {
if (mStereoscopicOptions == null) {
mStereoscopicOptions = new StereoscopicOptions();
}
return mStereoscopicOptions;
}
/**
* A class containing the result of a picking query
*/
@@ -1263,10 +1216,9 @@ public class View {
private static native int nGetAmbientOcclusion(long nativeView);
private static native void nSetAmbientOcclusionOptions(long nativeView, float radius, float bias, float power, float resolution, float intensity, float bilateralThreshold, int quality, int lowPassFilter, int upsampling, boolean enabled, boolean bentNormals, float minHorizonAngleRad);
private static native void nSetSSCTOptions(long nativeView, float ssctLightConeRad, float ssctStartTraceDistance, float ssctContactDistanceMax, float ssctIntensity, float v, float v1, float v2, float ssctDepthBias, float ssctDepthSlopeBias, int ssctSampleCount, int ssctRayCount, boolean ssctEnabled);
private static native void nSetBloomOptions(long nativeView, long dirtNativeObject, float dirtStrength, float strength, int resolution, int levels, int blendMode, boolean threshold, boolean enabled, float highlight,
private static native void nSetBloomOptions(long nativeView, long dirtNativeObject, float dirtStrength, float strength, int resolution, float anamorphism, int levels, int blendMode, boolean threshold, boolean enabled, float highlight,
boolean lensFlare, boolean starburst, float chromaticAberration, int ghostCount, float ghostSpacing, float ghostThreshold, float haloThickness, float haloRadius, float haloThreshold);
private static native void nSetFogOptions(long nativeView, float distance, float maximumOpacity, float height, float heightFalloff, float cutOffDistance, float v, float v1, float v2, float density, float inScatteringStart, float inScatteringSize, boolean fogColorFromIbl, long skyColorNativeObject, boolean enabled);
private static native void nSetStereoscopicOptions(long nativeView, boolean enabled);
private static native void nSetBlendMode(long nativeView, int blendMode);
private static native void nSetDepthOfFieldOptions(long nativeView, float cocScale, float maxApertureDiameter, boolean enabled, int filter,
boolean nativeResolution, int foregroundRingCount, int backgroundRingCount, int fastGatherRingCount, int maxForegroundCOC, int maxBackgroundCOC);
@@ -1401,6 +1353,8 @@ public class View {
* blendMode: Whether the bloom effect is purely additive (false) or mixed with the original
* image (true).
*
* anamorphism: Bloom's aspect ratio (x/y), for artistic purposes.
*
* threshold: When enabled, a threshold at 1.0 is applied on the source image, this is
* useful for artistic reasons and is usually needed when a dirt texture is used.
*
@@ -1439,6 +1393,10 @@ public class View {
* resolution of vertical axis (2^levels to 2048)
*/
public int resolution = 384;
/**
* bloom x/y aspect-ratio (1/32 to 32)
*/
public float anamorphism = 1.0f;
/**
* number of blur levels (1 to 11)
*/
@@ -1460,17 +1418,6 @@ public class View {
* limit highlights to this value before bloom [10, +inf]
*/
public float highlight = 1000.0f;
/**
* Bloom quality level.
* LOW (default): use a more optimized down-sampling filter, however there can be artifacts
* with dynamic resolution, this can be alleviated by using the homogenous mode.
* MEDIUM: Good balance between quality and performance.
* HIGH: In this mode the bloom resolution is automatically increased to avoid artifacts.
* This mode can be significantly slower on mobile, especially at high resolution.
* This mode greatly improves the anamorphic bloom.
*/
@NonNull
public QualityLevel quality = QualityLevel.LOW;
/**
* enable screen-space lens flare
*/
@@ -1852,7 +1799,7 @@ public class View {
}
/**
* Options for Multi-Sample Anti-aliasing (MSAA)
* Options for Temporal Multi-Sample Anti-aliasing (MSAA)
* @see setMultiSampleAntiAliasingOptions()
*/
public static class MultiSampleAntiAliasingOptions {
@@ -1876,98 +1823,21 @@ public class View {
/**
* Options for Temporal Anti-aliasing (TAA)
* Most TAA parameters are extremely costly to change, as they will trigger the TAA post-process
* shaders to be recompiled. These options should be changed or set during initialization.
* `filterWidth`, `feedback` and `jitterPattern`, however, can be changed at any time.
*
* `feedback` of 0.1 effectively accumulates a maximum of 19 samples in steady state.
* see "A Survey of Temporal Antialiasing Techniques" by Lei Yang and all for more information.
*
* @see setTemporalAntiAliasingOptions()
*/
public static class TemporalAntiAliasingOptions {
public enum BoxType {
/**
* use an AABB neighborhood
*/
AABB,
/**
* use the variance of the neighborhood (not recommended)
*/
VARIANCE,
/**
* use both AABB and variance
*/
AABB_VARIANCE,
}
public enum BoxClipping {
/**
* Accurate box clipping
*/
ACCURATE,
/**
* clamping
*/
CLAMP,
/**
* no rejections (use for debugging)
*/
NONE,
}
public enum JitterPattern {
RGSS_X4,
UNIFORM_HELIX_X4,
HALTON_23_X8,
HALTON_23_X16,
}
/**
* reconstruction filter width typically between 0.2 (sharper, aliased) and 1.5 (smoother)
* reconstruction filter width typically between 0 (sharper, aliased) and 1 (smoother)
*/
public float filterWidth = 1.0f;
/**
* history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA).
*/
public float feedback = 0.12f;
public float feedback = 0.04f;
/**
* enables or disables temporal anti-aliasing
*/
public boolean enabled = false;
/**
* whether to filter the history buffer
*/
public boolean filterHistory = true;
/**
* whether to apply the reconstruction filter to the input
*/
public boolean filterInput = true;
/**
* whether to use the YcoCg color-space for history rejection
*/
public boolean useYCoCg = false;
/**
* type of color gamut box
*/
@NonNull
public TemporalAntiAliasingOptions.BoxType boxType = TemporalAntiAliasingOptions.BoxType.AABB;
/**
* clipping algorithm
*/
@NonNull
public TemporalAntiAliasingOptions.BoxClipping boxClipping = TemporalAntiAliasingOptions.BoxClipping.ACCURATE;
@NonNull
public TemporalAntiAliasingOptions.JitterPattern jitterPattern = TemporalAntiAliasingOptions.JitterPattern.HALTON_23_X16;
public float varianceGamma = 1.0f;
/**
* adjust the feedback dynamically to reduce flickering
*/
public boolean preventFlickering = false;
/**
* whether to apply history reprojection (debug option)
*/
public boolean historyReprojection = true;
}
/**
@@ -2054,7 +1924,6 @@ public class View {
* PCF with soft shadows and contact hardening
*/
PCSS,
PCFd,
}
/**

View File

@@ -182,85 +182,28 @@ public class UiHelper {
void detach();
}
private class SurfaceViewHandler implements RenderSurface, SurfaceHolder.Callback {
@NonNull private final SurfaceView mSurfaceView;
private static class SurfaceViewHandler implements RenderSurface {
private final SurfaceView mSurfaceView;
SurfaceViewHandler(@NonNull SurfaceView surfaceView) {
mSurfaceView = surfaceView;
@NonNull SurfaceHolder holder = surfaceView.getHolder();
holder.addCallback(this);
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
}
// in case the SurfaceView's surface already existed
final Surface surface = holder.getSurface();
if (surface != null && surface.isValid()) {
surfaceCreated(holder);
// there is no way to retrieve the actual PixelFormat, since it is not used
// in the callback, we can use whatever we want.
surfaceChanged(holder, PixelFormat.RGBA_8888,
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
}
SurfaceViewHandler(SurfaceView surface) {
mSurfaceView = surface;
}
@Override
public void resize(int width, int height) {
@NonNull SurfaceHolder holder = mSurfaceView.getHolder();
holder.setFixedSize(width, height);
mSurfaceView.getHolder().setFixedSize(width, height);
}
@Override
public void detach() {
@NonNull SurfaceHolder holder = mSurfaceView.getHolder();
holder.removeCallback(this);
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
createSwapChain(holder.getSurface());
}
@Override
public void surfaceChanged(
@NonNull SurfaceHolder holder, int format, int width, int height) {
// Note: this is always called at least once after surfaceCreated()
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
if (mRenderCallback != null) {
mRenderCallback.onResized(width, height);
}
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
destroySwapChain();
}
}
private class SurfaceHolderHandler implements RenderSurface, SurfaceHolder.Callback {
private static class SurfaceHolderHandler implements RenderSurface {
private final SurfaceHolder mSurfaceHolder;
SurfaceHolderHandler(@NonNull SurfaceHolder holder) {
mSurfaceHolder = holder;
holder.addCallback(this);
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
}
// in case the SurfaceHolder's surface already existed
final Surface surface = holder.getSurface();
if (surface != null && surface.isValid()) {
surfaceCreated(holder);
// there is no way to retrieve the actual PixelFormat, since it is not used
// in the callback, we can use whatever we want.
surfaceChanged(holder, PixelFormat.RGBA_8888,
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
}
SurfaceHolderHandler(SurfaceHolder surface) {
mSurfaceHolder = surface;
}
@Override
@@ -270,127 +213,30 @@ public class UiHelper {
@Override
public void detach() {
mSurfaceHolder.removeCallback(this);
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
createSwapChain(holder.getSurface());
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
// Note: this is always called at least once after surfaceCreated()
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
if (mRenderCallback != null) {
mRenderCallback.onResized(width, height);
}
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
destroySwapChain();
}
}
private class TextureViewHandler implements RenderSurface, TextureView.SurfaceTextureListener {
private class TextureViewHandler implements RenderSurface {
private final TextureView mTextureView;
private Surface mSurface;
TextureViewHandler(@NonNull TextureView view) {
mTextureView = view;
mTextureView.setSurfaceTextureListener(this);
// in case the View's SurfaceTexture already existed
if (view.isAvailable()) {
SurfaceTexture surfaceTexture = view.getSurfaceTexture();
if (surfaceTexture != null) {
this.onSurfaceTextureAvailable(surfaceTexture,
mDesiredWidth, mDesiredHeight);
}
}
}
TextureViewHandler(TextureView surface) { mTextureView = surface; }
@Override
public void resize(int width, int height) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
if (surfaceTexture != null) {
surfaceTexture.setDefaultBufferSize(width, height);
}
}
if (mRenderCallback != null) {
// the call above won't cause TextureView.onSurfaceTextureSizeChanged()
mRenderCallback.onResized(width, height);
mTextureView.getSurfaceTexture().setDefaultBufferSize(width, height);
}
// the call above won't cause TextureView.onSurfaceTextureSizeChanged()
mRenderCallback.onResized(width, height);
}
@Override
public void detach() {
mTextureView.setSurfaceTextureListener(null);
}
@Override
public void onSurfaceTextureAvailable(
@NonNull SurfaceTexture surfaceTexture, int width, int height) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureAvailable()");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
}
}
final Surface surface = new Surface(surfaceTexture);
setSurface(surface);
createSwapChain(surface);
if (mRenderCallback != null) {
// Call this the first time because onSurfaceTextureSizeChanged()
// isn't called at initialization time
mRenderCallback.onResized(width, height);
}
}
@Override
public void onSurfaceTextureSizeChanged(
@NonNull SurfaceTexture surfaceTexture, int width, int height) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureSizeChanged()");
if (mRenderCallback != null) {
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
mRenderCallback.onResized(mDesiredWidth, mDesiredHeight);
} else {
mRenderCallback.onResized(width, height);
}
// We must recreate the SwapChain to guarantee that it sees the new size.
// More precisely, for an EGL client, the EGLSurface must be recreated. For
// a Vulkan client, the SwapChain must be recreated. Calling
// onNativeWindowChanged() will accomplish that.
// This requirement comes from SurfaceTexture.setDefaultBufferSize()
// documentation.
final Surface surface = getSurface();
if (surface != null) {
mRenderCallback.onNativeWindowChanged(surface);
}
}
}
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureDestroyed()");
setSurface(null);
destroySwapChain();
return true;
}
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { }
private void setSurface(@Nullable Surface surface) {
void setSurface(Surface surface) {
if (surface == null) {
if (mSurface != null) {
mSurface.release();
@@ -399,7 +245,7 @@ public class UiHelper {
mSurface = surface;
}
private Surface getSurface() {
public Surface getSurface() {
return mSurface;
}
}
@@ -445,9 +291,6 @@ public class UiHelper {
* {@link #attachTo(TextureView)}, or {@link #attachTo(SurfaceHolder)}.
*/
public void detach() {
if (mRenderSurface != null) {
mRenderSurface.detach();
}
destroySwapChain();
mNativeWindow = null;
mRenderSurface = null;
@@ -455,6 +298,7 @@ public class UiHelper {
/**
* Checks whether we are ready to render into the attached surface.
*
* Using OpenGL ES when this returns true, will result in drawing commands being lost,
* HOWEVER, GLES state will be preserved. This is useful to initialize the engine.
*
@@ -499,6 +343,7 @@ public class UiHelper {
/**
* Controls whether the render target (SurfaceView or TextureView) is opaque or not.
* The render target is considered opaque by default.
*
* Must be called before calling {@link #attachTo(SurfaceView)}, {@link #attachTo(TextureView)},
* or {@link #attachTo(SurfaceHolder)}.
*
@@ -521,8 +366,10 @@ public class UiHelper {
* positioned above other surfaces but below the activity's surface. This property
* only has an effect when used in combination with {@link #setOpaque(boolean) setOpaque(false)}
* and does not affect TextureView targets.
*
* Must be called before calling {@link #attachTo(SurfaceView)}
* or {@link #attachTo(TextureView)}.
*
* Has no effect when using {@link #attachTo(SurfaceHolder)}.
*
* @param overlay Indicates whether the render target should be rendered below the activity's
@@ -543,6 +390,7 @@ public class UiHelper {
/**
* Associate UiHelper with a SurfaceView.
*
* As soon as SurfaceView is ready (i.e. has a Surface), we'll create the
* EGL resources needed, and call user callbacks if needed.
*/
@@ -557,32 +405,171 @@ public class UiHelper {
view.setZOrderOnTop(translucent);
}
view.getHolder().setFormat(isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT);
int format = isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
view.getHolder().setFormat(format);
mRenderSurface = new SurfaceViewHandler(view);
final SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
createSwapChain(holder.getSurface());
}
@Override
public void surfaceChanged(
SurfaceHolder holder, int format, int width, int height) {
// Note: this is always called at least once after surfaceCreated()
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
mRenderCallback.onResized(width, height);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
destroySwapChain();
}
};
SurfaceHolder holder = view.getHolder();
holder.addCallback(callback);
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
}
// in case the SurfaceView's surface already existed
final Surface surface = holder.getSurface();
if (surface != null && surface.isValid()) {
callback.surfaceCreated(holder);
callback.surfaceChanged(holder, format,
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
}
}
}
/**
* Associate UiHelper with a TextureView.
*
* As soon as TextureView is ready (i.e. has a buffer), we'll create the
* EGL resources needed, and call user callbacks if needed.
*/
public void attachTo(@NonNull TextureView view) {
if (attach(view)) {
view.setOpaque(isOpaque());
mRenderSurface = new TextureViewHandler(view);
TextureView.SurfaceTextureListener listener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(
SurfaceTexture surfaceTexture, int width, int height) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureAvailable()");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
}
}
Surface surface = new Surface(surfaceTexture);
TextureViewHandler textureViewHandler = (TextureViewHandler) mRenderSurface;
textureViewHandler.setSurface(surface);
createSwapChain(surface);
// Call this the first time because onSurfaceTextureSizeChanged()
// isn't called at initialization time
mRenderCallback.onResized(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(
SurfaceTexture surfaceTexture, int width, int height) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureSizeChanged()");
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
mRenderCallback.onResized(mDesiredWidth, mDesiredHeight);
} else {
mRenderCallback.onResized(width, height);
}
// We must recreate the SwapChain to guarantee that it sees the new size.
// More precisely, for an EGL client, the EGLSurface must be recreated. For
// a Vulkan client, the SwapChain must be recreated. Calling
// onNativeWindowChanged() will accomplish that.
// This requirement comes from SurfaceTexture.setDefaultBufferSize()
// documentation.
TextureViewHandler textureViewHandler = (TextureViewHandler) mRenderSurface;
mRenderCallback.onNativeWindowChanged(textureViewHandler.getSurface());
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureDestroyed()");
destroySwapChain();
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) { }
};
view.setSurfaceTextureListener(listener);
// in case the View's SurfaceTexture already existed
if (view.isAvailable()) {
SurfaceTexture surfaceTexture = view.getSurfaceTexture();
listener.onSurfaceTextureAvailable(surfaceTexture, mDesiredWidth, mDesiredHeight);
}
}
}
/**
* Associate UiHelper with a SurfaceHolder.
*
* As soon as a Surface is created, we'll create the
* EGL resources needed, and call user callbacks if needed.
*/
public void attachTo(@NonNull SurfaceHolder holder) {
if (attach(holder)) {
holder.setFormat(isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT);
int format = isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
holder.setFormat(format);
mRenderSurface = new SurfaceHolderHandler(holder);
final SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
createSwapChain(holder.getSurface());
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// Note: this is always called at least once after surfaceCreated()
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
mRenderCallback.onResized(width, height);
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
destroySwapChain();
}
};
holder.addCallback(callback);
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
}
// in case the SurfaceHolder's surface already existed
final Surface surface = holder.getSurface();
if (surface != null && surface.isValid()) {
callback.surfaceCreated(holder);
callback.surfaceChanged(holder, format,
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
}
}
}
@@ -593,10 +580,6 @@ public class UiHelper {
// nothing to do
return false;
}
if (mRenderSurface != null) {
mRenderSurface.detach();
mRenderSurface = null;
}
destroySwapChain();
}
mNativeWindow = nativeWindow;
@@ -604,16 +587,15 @@ public class UiHelper {
}
private void createSwapChain(@NonNull Surface surface) {
if (mRenderCallback != null) {
mRenderCallback.onNativeWindowChanged(surface);
}
mRenderCallback.onNativeWindowChanged(surface);
mHasSwapChain = true;
}
private void destroySwapChain() {
if (mRenderCallback != null) {
mRenderCallback.onDetachedFromSurface();
if (mRenderSurface != null) {
mRenderSurface.detach();
}
mRenderCallback.onDetachedFromSurface();
mHasSwapChain = false;
}
}

View File

@@ -12,6 +12,9 @@ android {
}
}
defaultConfig {
missingDimensionStrategy 'functionality', 'full'
}
packagingOptions {
// No need to package up the following shared libs, which arise as a side effect of our
// externalNativeBuild dependencies. When clients pick and choose from project-level gradle

View File

@@ -17,8 +17,6 @@
// Operators +, *, / based on http://half.sourceforge.net/ by Christian Rau
// and licensed under MIT
@file:Suppress("NOTHING_TO_INLINE")
package com.google.android.filament.utils
import com.google.android.filament.utils.Half.Companion.POSITIVE_INFINITY

View File

@@ -52,7 +52,6 @@ set(GLTFIO_SRCS
${GLTFIO_DIR}/include/gltfio/FilamentInstance.h
${GLTFIO_DIR}/include/gltfio/MaterialProvider.h
${GLTFIO_DIR}/include/gltfio/NodeManager.h
${GLTFIO_DIR}/include/gltfio/TrsTransformManager.h
${GLTFIO_DIR}/include/gltfio/ResourceLoader.h
${GLTFIO_DIR}/include/gltfio/TextureProvider.h
${GLTFIO_DIR}/include/gltfio/math.h
@@ -70,12 +69,10 @@ set(GLTFIO_SRCS
${GLTFIO_DIR}/src/FilamentAsset.cpp
${GLTFIO_DIR}/src/FilamentInstance.cpp
${GLTFIO_DIR}/src/FNodeManager.h
${GLTFIO_DIR}/src/FTrsTransformManager.h
${GLTFIO_DIR}/src/GltfEnums.h
${GLTFIO_DIR}/src/Ktx2Provider.cpp
${GLTFIO_DIR}/src/MaterialProvider.cpp
${GLTFIO_DIR}/src/NodeManager.cpp
${GLTFIO_DIR}/src/TrsTransformManager.cpp
${GLTFIO_DIR}/src/ResourceLoader.cpp
${GLTFIO_DIR}/src/StbProvider.cpp
${GLTFIO_DIR}/src/TangentsJob.cpp

View File

@@ -1,6 +1,12 @@
android {
namespace 'com.google.android.filament.gltfio'
flavorDimensions "functionality"
productFlavors {
full {
dimension "functionality"
}
}
packagingOptions {
// No need to package up the following shared libs, which arise as a side effect of our
// externalNativeBuild dependencies. When clients pick and choose from project-level gradle
@@ -12,7 +18,7 @@ android {
}
publishing {
singleVariant("release") {
singleVariant("fullRelease") {
withSourcesJar()
withJavadocJar()
}
@@ -30,9 +36,9 @@ apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
afterEvaluate { project ->
publishing {
publications {
release(MavenPublication) {
fullRelease(MavenPublication) {
artifactId = POM_ARTIFACT_ID_FULL
from components.release
from components.fullRelease
}
}
}

View File

@@ -144,7 +144,7 @@ public class FilamentInstance {
*
* Ignored if variantIndex is out of bounds.
*/
public void applyMaterialVariant(@IntRange(from = 0) int variantIndex) {
void applyMaterialVariant(@IntRange(from = 0) int variantIndex) {
nApplyMaterialVariant(mNativeObject, variantIndex);
}

View File

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

View File

@@ -1,6 +1,6 @@
#Wed Nov 17 10:40:18 PST 2021
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -87,9 +87,9 @@ compile Filament's native library and Filament's AAR for this project. The easie
is to install all the required dependencies and to run the following commands at the root of the
source tree:
```shell
./build.sh -p desktop -i release
./build.sh -p android release
```
$ ./build.sh -p desktop -i release
$ ./build.sh -p android release
```
This will build all the native components and the AAR required by this sample application.
@@ -100,8 +100,8 @@ distribution/install directory for desktop (produced by make/ninja install). Thi
contain `bin/matc` and `bin/cmgen`.
Example:
```shell
./gradlew -Pfilament_tools_dir=../../dist-release assembleDebug
```
$ ./gradlew -Pfilament_tools_dir=../../dist-release assembleDebug
```
## Important: SDK location
@@ -110,24 +110,14 @@ Either ensure your `ANDROID_HOME` environment variable is set or make sure the r
contains a `local.properties` file with the `sdk.dir` property pointing to your installation of
the Android SDK.
## Compiling
### Android Studio
## Android Studio
You must use the latest stable release of Android Studio. To open the project, point Studio to the
`android` folder. After opening the project and syncing to gradle, select the sample of your choice
using the drop-down widget in the toolbar.
## Compiling
To compile and run each sample make sure you have selected the appropriate build variant
(arm7, arm8, x86 or x86_64). If you are not sure you can simply select the "universal"
variant which includes all the other ones.
### Command Line
From the `android` directory in the project root:
```shell
./gradlew :samples:sample-hello-triangle:installDebug
```
Replace `sample-hello-triangle` with your preferred project.

View File

@@ -32,6 +32,7 @@ android {
applicationId "com.google.android.filament.gltf"
minSdkVersion 19
targetSdkVersion versions.targetSdk
missingDimensionStrategy 'functionality', 'full'
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies

View File

@@ -26,7 +26,6 @@ import android.widget.TextView
import android.widget.Toast
import com.google.android.filament.Fence
import com.google.android.filament.IndirectLight
import com.google.android.filament.Material
import com.google.android.filament.Skybox
import com.google.android.filament.View
import com.google.android.filament.View.OnPickCallback
@@ -393,36 +392,6 @@ class MainActivity : Activity() {
Log.i(TAG, "The Filament backend took $total ms to load the model geometry.")
modelViewer.engine.destroyFence(it)
loadStartFence = null
val materials = mutableSetOf<Material>()
val rcm = modelViewer.engine.renderableManager
modelViewer.scene.forEach {
val entity = it
if (rcm.hasComponent(entity)) {
val ri = rcm.getInstance(entity)
val c = rcm.getPrimitiveCount(ri)
for (i in 0 until c) {
val mi = rcm.getMaterialInstanceAt(ri, i)
val ma = mi.material
materials.add(ma)
}
}
}
materials.forEach {
it.compile(
Material.CompilerPriorityQueue.HIGH,
Material.UserVariantFilterBit.DIRECTIONAL_LIGHTING or
Material.UserVariantFilterBit.DYNAMIC_LIGHTING or
Material.UserVariantFilterBit.SHADOW_RECEIVER,
null, null)
it.compile(
Material.CompilerPriorityQueue.LOW,
Material.UserVariantFilterBit.FOG or
Material.UserVariantFilterBit.SKINNING or
Material.UserVariantFilterBit.SSR or
Material.UserVariantFilterBit.VSM,
null, null)
}
}
}

View File

@@ -20,8 +20,6 @@ import android.animation.ValueAnimator
import android.app.Activity
import android.opengl.Matrix
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.Choreographer
import android.view.Surface
import android.view.SurfaceView
@@ -112,7 +110,7 @@ class MainActivity : Activity() {
}
private fun setupFilament() {
engine = Engine.Builder().featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0).build()
engine = Engine.create()
renderer = engine.createRenderer()
scene = engine.createScene()
view = engine.createView()
@@ -122,8 +120,13 @@ class MainActivity : Activity() {
private fun setupView() {
scene.skybox = Skybox.Builder().color(0.035f, 0.035f, 0.035f, 1.0f).build(engine)
// post-processing is not supported at feature level 0
view.isPostProcessingEnabled = false
if (engine.activeFeatureLevel == Engine.FeatureLevel.FEATURE_LEVEL_0) {
// post-processing is not supported at feature level 0
view.isPostProcessingEnabled = false
} else {
// NOTE: Try to disable post-processing (tone-mapping, etc.) to see the difference
// view.isPostProcessingEnabled = false
}
// Tell the view which camera we want to use
view.camera = camera
@@ -157,16 +160,12 @@ class MainActivity : Activity() {
}
private fun loadMaterial() {
readUncompressedAsset("materials/baked_color.filamat").let {
var name = "materials/baked_color.filamat"
if (engine.activeFeatureLevel == Engine.FeatureLevel.FEATURE_LEVEL_0) {
name = "materials/baked_color_es2.filamat"
}
readUncompressedAsset(name).let {
material = Material.Builder().payload(it, it.remaining()).build(engine)
material.compile(
Material.CompilerPriorityQueue.HIGH,
Material.UserVariantFilterBit.ALL,
Handler(Looper.getMainLooper())) {
android.util.Log.i("hellotriangle",
"Material " + material.name + " compiled.")
}
engine.flush()
}
}

View File

@@ -17,8 +17,7 @@ material {
],
// This material disables all lighting
shadingModel : unlit,
featureLevel : 0
shadingModel : unlit
}
fragment {

View File

@@ -0,0 +1,33 @@
// Simple unlit material that uses the colors associated with each vertex.
//
// This source material must be compiled to a binary material using the matc tool.
// The command used to compile this material is:
// matc -p mobile -a opengl -o app/src/main/assets/baked_color.filamat app/src/materials/baked_color.mat
//
// See build.gradle for an example of how to compile materials automatically
// Please refer to the documentation for more information about matc and the materials system.
material {
name : baked_color,
// Lists the required vertex attributes
// Here we only need a color (RGBA)
requires : [
color
],
// This material disables all lighting
shadingModel : unlit,
featureLevel : 0
}
fragment {
void material(inout MaterialInputs material) {
// You must always call the prepareMaterial() function
prepareMaterial(material);
// We set the material's color to the color interpolated from
// the model's vertices
material.baseColor = getColor();
}
}

View File

@@ -32,6 +32,13 @@ android {
targetSdkVersion versions.targetSdk
}
// The filamat library has two variants: full and lite. Here we default to the "full" variant.
// Replace "full" with "lite" to use the filamat-lite variant, which has a smaller binary size
// but comes with certain restrictions. See "Filamat Lite" in libs/filamat/README.md.
defaultConfig {
missingDimensionStrategy 'functionality', 'full'
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {

View File

@@ -33,6 +33,7 @@ android {
applicationId "com.google.android.filament.textured"
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
missingDimensionStrategy 'functionality', 'full'
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies

104
build.sh
View File

@@ -191,7 +191,6 @@ function build_clean {
rm -Rf android/filamat-android/build android/filamat-android/.externalNativeBuild android/filamat-android/.cxx
rm -Rf android/gltfio-android/build android/gltfio-android/.externalNativeBuild android/gltfio-android/.cxx
rm -Rf android/filament-utils-android/build android/filament-utils-android/.externalNativeBuild android/filament-utils-android/.cxx
rm -f compile_commands.json
}
function build_clean_aggressive {
@@ -211,10 +210,13 @@ function build_desktop_target {
echo "Building ${lc_target} in out/cmake-${lc_target}..."
mkdir -p "out/cmake-${lc_target}"
pushd "out/cmake-${lc_target}" > /dev/null
cd "out/cmake-${lc_target}"
# On macOS, set the deployment target to 10.15.
local lc_name=$(echo "${UNAME}" | tr '[:upper:]' '[:lower:]')
if [[ "${lc_name}" == "darwin" ]]; then
local deployment_target="-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15"
if [[ "${BUILD_UNIVERSAL_LIBRARIES}" == "true" ]]; then
local architectures="-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64"
fi
@@ -231,10 +233,9 @@ function build_desktop_target {
${MATDBG_OPTION} \
${MATOPT_OPTION} \
${ASAN_UBSAN_OPTION} \
${deployment_target} \
${architectures} \
../..
ln -sf "out/cmake-${lc_target}/compile_commands.json" \
../../compile_commands.json
fi
${BUILD_COMMAND} ${build_targets}
@@ -246,13 +247,12 @@ function build_desktop_target {
if [[ -d "../${lc_target}/filament" ]]; then
if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then
echo "Generating out/filament-${lc_target}-${LC_UNAME}.tgz..."
pushd "../${lc_target}" > /dev/null
cd "../${lc_target}"
tar -czvf "../filament-${lc_target}-${LC_UNAME}.tgz" filament
popd > /dev/null
fi
fi
popd > /dev/null
cd ../..
}
function build_desktop {
@@ -270,7 +270,7 @@ function build_webgl_with_target {
echo "Building WebGL ${lc_target}..."
mkdir -p "out/cmake-webgl-${lc_target}"
pushd "out/cmake-webgl-${lc_target}" > /dev/null
cd "out/cmake-webgl-${lc_target}"
if [[ ! "${BUILD_TARGETS}" ]]; then
BUILD_TARGETS=${BUILD_CUSTOM_TARGETS}
@@ -290,8 +290,6 @@ function build_webgl_with_target {
-DCMAKE_INSTALL_PREFIX="../webgl-${lc_target}/filament" \
-DWEBGL=1 \
../..
ln -sf "out/cmake-webgl-${lc_target}/compile_commands.json" \
../../compile_commands.json
${BUILD_COMMAND} ${BUILD_TARGETS}
)
fi
@@ -309,17 +307,17 @@ function build_webgl_with_target {
if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then
echo "Generating out/filament-${lc_target}-web.tgz..."
pushd web/filament-js > /dev/null
cd web/filament-js
tar -cvf "../../../filament-${lc_target}-web.tar" filament.js
tar -rvf "../../../filament-${lc_target}-web.tar" filament.wasm
tar -rvf "../../../filament-${lc_target}-web.tar" filament.d.ts
popd > /dev/null
cd -
gzip -c "../filament-${lc_target}-web.tar" > "../filament-${lc_target}-web.tgz"
rm "../filament-${lc_target}-web.tar"
fi
fi
popd > /dev/null
cd ../..
}
function build_webgl {
@@ -350,7 +348,7 @@ function build_android_target {
echo "Building Android ${lc_target} (${arch})..."
mkdir -p "out/cmake-android-${lc_target}-${arch}"
pushd "out/cmake-android-${lc_target}-${arch}" > /dev/null
cd "out/cmake-android-${lc_target}-${arch}"
if [[ ! -d "CMakeFiles" ]] || [[ "${ISSUE_CMAKE_ALWAYS}" == "true" ]]; then
cmake \
@@ -364,14 +362,12 @@ function build_android_target {
${MATOPT_OPTION} \
${VULKAN_ANDROID_OPTION} \
../..
ln -sf "out/cmake-android-${lc_target}-${arch}/compile_commands.json" \
../../compile_commands.json
fi
# We must always install Android libraries to build the AAR
${BUILD_COMMAND} install
popd > /dev/null
cd ../..
}
function build_android_arch {
@@ -392,9 +388,9 @@ function archive_android {
if [[ -d "out/android-${lc_target}/filament" ]]; then
if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then
echo "Generating out/filament-android-${lc_target}-${LC_UNAME}.tgz..."
pushd "out/android-${lc_target}" > /dev/null
cd "out/android-${lc_target}"
tar -czvf "../filament-android-${lc_target}-${LC_UNAME}.tgz" filament
popd > /dev/null
cd ../..
fi
fi
}
@@ -472,7 +468,7 @@ function build_android {
archive_android "Release"
fi
pushd android > /dev/null
cd android
if [[ "${ISSUE_DEBUG_BUILD}" == "true" ]]; then
./gradlew \
@@ -502,13 +498,13 @@ function build_android {
if [[ "${INSTALL_COMMAND}" ]]; then
echo "Installing out/filamat-android-debug.aar..."
cp filamat-android/build/outputs/aar/filamat-android-debug.aar ../out/filamat-android-debug.aar
cp filamat-android/build/outputs/aar/filamat-android-full-debug.aar ../out/filamat-android-debug.aar
echo "Installing out/filament-android-debug.aar..."
cp filament-android/build/outputs/aar/filament-android-debug.aar ../out/
echo "Installing out/gltfio-android-debug.aar..."
cp gltfio-android/build/outputs/aar/gltfio-android-debug.aar ../out/gltfio-android-debug.aar
cp gltfio-android/build/outputs/aar/gltfio-android-full-debug.aar ../out/gltfio-android-debug.aar
echo "Installing out/filament-utils-android-debug.aar..."
cp filament-utils-android/build/outputs/aar/filament-utils-android-debug.aar ../out/filament-utils-android-debug.aar
@@ -551,13 +547,14 @@ function build_android {
if [[ "${INSTALL_COMMAND}" ]]; then
echo "Installing out/filamat-android-release.aar..."
cp filamat-android/build/outputs/aar/filamat-android-release.aar ../out/filamat-android-release.aar
cp filamat-android/build/outputs/aar/filamat-android-lite-release.aar ../out/
cp filamat-android/build/outputs/aar/filamat-android-full-release.aar ../out/filamat-android-release.aar
echo "Installing out/filament-android-release.aar..."
cp filament-android/build/outputs/aar/filament-android-release.aar ../out/
echo "Installing out/gltfio-android-release.aar..."
cp gltfio-android/build/outputs/aar/gltfio-android-release.aar ../out/gltfio-android-release.aar
cp gltfio-android/build/outputs/aar/gltfio-android-full-release.aar ../out/gltfio-android-release.aar
echo "Installing out/filament-utils-android-release.aar..."
cp filament-utils-android/build/outputs/aar/filament-utils-android-release.aar ../out/filament-utils-android-release.aar
@@ -572,7 +569,7 @@ function build_android {
fi
fi
popd > /dev/null
cd ..
}
function build_ios_target {
@@ -583,7 +580,7 @@ function build_ios_target {
echo "Building iOS ${lc_target} (${arch}) for ${platform}..."
mkdir -p "out/cmake-ios-${lc_target}-${arch}"
pushd "out/cmake-ios-${lc_target}-${arch}" > /dev/null
cd "out/cmake-ios-${lc_target}-${arch}"
if [[ ! -d "CMakeFiles" ]] || [[ "${ISSUE_CMAKE_ALWAYS}" == "true" ]]; then
cmake \
@@ -598,8 +595,6 @@ function build_ios_target {
${MATDBG_OPTION} \
${MATOPT_OPTION} \
../..
ln -sf "out/cmake-ios-${lc_target}-${arch}/compile_commands.json" \
../../compile_commands.json
fi
${BUILD_COMMAND}
@@ -609,7 +604,7 @@ function build_ios_target {
${BUILD_COMMAND} ${INSTALL_COMMAND}
fi
popd > /dev/null
cd ../..
}
function archive_ios {
@@ -618,9 +613,9 @@ function archive_ios {
if [[ -d "out/ios-${lc_target}/filament" ]]; then
if [[ "${ISSUE_ARCHIVES}" == "true" ]]; then
echo "Generating out/filament-${lc_target}-ios.tgz..."
pushd "out/ios-${lc_target}" > /dev/null
cd "out/ios-${lc_target}"
tar -czvf "../filament-${lc_target}-ios.tgz" filament
popd > /dev/null
cd ../..
fi
fi
}
@@ -679,14 +674,14 @@ function build_web_docs {
mkdir -p out/web-docs
cp -f docs/web-docs-package.json out/web-docs/package.json
pushd out/web-docs > /dev/null
cd out/web-docs
npm install > /dev/null
# Generate documents
npx markdeep-rasterizer ../../docs/Filament.md.html ../../docs/Materials.md.html ../../docs/
popd > /dev/null
cd ../..
}
function validate_build_command {
@@ -759,19 +754,6 @@ function run_tests {
fi
}
function check_debug_release_build {
if [[ "${ISSUE_DEBUG_BUILD}" == "true" || \
"${ISSUE_RELEASE_BUILD}" == "true" || \
"${ISSUE_CLEAN}" == "true" || \
"${ISSUE_WEB_DOCS}" == "true" ]]; then
"$@";
else
echo "You must declare a debug or release target for $@ builds."
echo ""
exit 1
fi
}
# Beginning of the script
pushd "$(dirname "$0")" > /dev/null
@@ -780,7 +762,7 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
case ${opt} in
h)
print_help
exit 0
exit 1
;;
a)
ISSUE_ARCHIVES=true
@@ -816,7 +798,7 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
platforms=$(echo "${OPTARG}" | tr ',' '\n')
for platform in ${platforms}
do
case $(echo "${platform}" | tr '[:upper:]' '[:lower:]') in
case ${platform} in
desktop)
ISSUE_DESKTOP_BUILD=true
;;
@@ -835,12 +817,6 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
ISSUE_DESKTOP_BUILD=true
ISSUE_WEBGL_BUILD=false
;;
*)
echo "Unknown platform ${platform}"
echo "Platform must be one of [desktop|android|ios|webgl|all]"
echo ""
exit 1
;;
esac
done
;;
@@ -853,7 +829,7 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
abis=$(echo "${OPTARG}" | tr ',' '\n')
for abi in ${abis}
do
case $(echo "${abi}" | tr '[:upper:]' '[:lower:]') in
case ${abi} in
armeabi-v7a)
ABI_ARMEABI_V7A=true
;;
@@ -872,12 +848,6 @@ while getopts ":hacCfgijmp:q:uvslwtedk:b" opt; do
ABI_X86=true
ABI_X86_64=true
;;
*)
echo "Unknown abi ${abi}"
echo "ABI must be one of [armeabi-v7a|arm64-v8a|x86|x86_64|all]"
echo ""
exit 1
;;
esac
done
;;
@@ -941,9 +911,9 @@ fi
shift $((OPTIND - 1))
for arg; do
if [[ $(echo "${arg}" | tr '[:upper:]' '[:lower:]') == "release" ]]; then
if [[ "${arg}" == "release" ]]; then
ISSUE_RELEASE_BUILD=true
elif [[ $(echo "${arg}" | tr '[:upper:]' '[:lower:]') == "debug" ]]; then
elif [[ "${arg}" == "debug" ]]; then
ISSUE_DEBUG_BUILD=true
else
BUILD_CUSTOM_TARGETS="${BUILD_CUSTOM_TARGETS} ${arg}"
@@ -961,19 +931,19 @@ if [[ "${ISSUE_CLEAN_AGGRESSIVE}" == "true" ]]; then
fi
if [[ "${ISSUE_DESKTOP_BUILD}" == "true" ]]; then
check_debug_release_build build_desktop
build_desktop
fi
if [[ "${ISSUE_ANDROID_BUILD}" == "true" ]]; then
check_debug_release_build build_android
build_android
fi
if [[ "${ISSUE_IOS_BUILD}" == "true" ]]; then
check_debug_release_build build_ios
build_ios
fi
if [[ "${ISSUE_WEBGL_BUILD}" == "true" ]]; then
check_debug_release_build build_webgl
build_webgl
fi
if [[ "${ISSUE_WEB_DOCS}" == "true" ]]; then

View File

@@ -50,7 +50,7 @@ function replace {
FIND_STR="${1//\{\{VERSION\}\}/${VERSION_REGEX}}"
REPLACE_STR="${1//\{\{VERSION\}\}/${NEW_VERSION}}"
local FILE_NAME="$2"
if [ $IS_DARWIN == 1 ]; then
if [ IS_DARWIN ]; then
sed -i '' -E "s/${FIND_STR}/${REPLACE_STR}/" "${FILE_NAME}"
else
sed -i -E "s/${FIND_STR}/${REPLACE_STR}/" "${FILE_NAME}"

View File

@@ -5,6 +5,7 @@ libs/math/test_math
libs/image/test_image compare libs/image/tests/reference/
libs/utils/test_utils
libs/filamat/test_filamat
libs/filamat/test_filamat_lite
tools/matc/test_matc
tools/cmgen/test_cmgen compare
tools/glslminifier/test_glslminifier

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -15,7 +15,7 @@
*/
// If you are bundling this with rollup, webpack, or esbuild, the following URL should be trimmed.
import { LitElement, html, css } from "https://unpkg.com/lit@2.8.0?module";
import { LitElement, html, css } from "https://unpkg.com/lit?module";
// This little utility checks if the Filament module is ready for action.
// If so, it immediately calls the given function. If not, it asks the Filament
@@ -287,12 +287,12 @@ class FilamentViewer extends LitElement {
// Dropping a glb file is simple because there are no external resources.
if (this.srcBlob && this.srcBlob.name.endsWith(".glb")) {
this.srcBlob.arrayBuffer().then(buffer => {
this.asset = this.loader.createAsset(new Uint8Array(buffer));
this.asset = this.loader.createAssetFromBinary(new Uint8Array(buffer));
const aabb = this.asset.getBoundingBox();
this.assetRoot = this.asset.getRoot();
this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset);
this.asset.loadResources();
this.animator = this.asset.getInstance().getAnimator();
this.animator = this.asset.getAnimator();
this.animationStartTime = Date.now();
this._updateOverlay();
});
@@ -304,6 +304,8 @@ class FilamentViewer extends LitElement {
const config = {
normalizeSkinningWeights: true,
recomputeBoundingBoxes: false,
ignoreBindTransform: false,
asyncInterval: 30
};
@@ -318,20 +320,22 @@ class FilamentViewer extends LitElement {
resourceLoader.delete();
stbProvider.delete();
ktx2Provider.delete();
this.animator = this.asset.getInstance().getAnimator();
this.animator = this.asset.getAnimator();
this.animationStartTime = Date.now();
}
}, config.asyncInterval);
};
this.srcBlob.arrayBuffer().then(buffer => {
this.asset = this.loader.createAsset(new Uint8Array(buffer));
this.asset = this.loader.createAssetFromJson(new Uint8Array(buffer));
const aabb = this.asset.getBoundingBox();
this.assetRoot = this.asset.getRoot();
this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset);
const resourceLoader = new Filament.gltfio$ResourceLoader(this.engine,
config.normalizeSkinningWeights);
config.normalizeSkinningWeights,
config.recomputeBoundingBoxes,
config.ignoreBindTransform);
const stbProvider = new Filament.gltfio$StbProvider(this.engine);
const ktx2Provider = new Filament.gltfio$Ktx2Provider(this.engine);
@@ -363,7 +367,12 @@ class FilamentViewer extends LitElement {
return response.arrayBuffer();
}).then(arrayBuffer => {
const modelData = new Uint8Array(arrayBuffer);
this.asset = this.loader.createAsset(modelData);
if (this.src.endsWith(".glb")) {
this.asset = this.loader.createAssetFromBinary(modelData);
} else {
this.asset = this.loader.createAssetFromJson(modelData);
}
const aabb = this.asset.getBoundingBox();
this.assetRoot = this.asset.getRoot();
this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset);
@@ -371,7 +380,7 @@ class FilamentViewer extends LitElement {
const basePath = '' + new URL(this.src, document.location);
this.asset.loadResources(() => {
this.animator = this.asset.getInstance().getAnimator();
this.animator = this.asset.getAnimator();
this.animationStartTime = Date.now();
this._applyMaterialVariant();
}, null, basePath);
@@ -432,15 +441,14 @@ class FilamentViewer extends LitElement {
if (!this.hasAttribute("materialVariant")) {
return;
}
const instance = this.asset.getInstance();
const names = instance.getMaterialVariantNames();
const names = this.asset.getMaterialVariantNames();
const index = this.materialVariant;
if (index < 0 || index >= names.length) {
console.error(`Material variant ${index} does not exist in this asset.`);
return;
}
console.info(this.src, `Applying material variant: ${names[index]}`);
instance.applyMaterialVariant(index);
this.asset.applyMaterialVariant(index);
}
}

View File

@@ -43,7 +43,7 @@ filament-viewer::part(canvas) {
</p>
</main>
<script src="https://unpkg.com/filament@1.44.0/filament.js"></script>
<script src="https://unpkg.com/filament@1.25.3/filament.js"></script>
<script src="https://unpkg.com/gltumble"></script>
<script src="filament-viewer.js" type="module"></script>
</body>

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -587,7 +587,6 @@
<li><em>overrides</em> Dictionary with one or more of the following properties: mapSize, shadowCascades, constantBias, normalBias, shadowFar, shadowNearHint, shadowFarHint, stable, polygonOffsetConstant, polygonOffsetSlope, \</li>
</ul>
</li>
<li><strong>engine.setStereoscopicOptions()</strong></li>
<li><strong>engine.setTemporalAntiAliasingOptions()</strong></li>
<li><strong>engine.setVignetteOptions()</strong></li>
</ul>
@@ -1463,8 +1462,6 @@ This defines the following functions:</p>
<li>STENCIL_ATTACHMENT</li>
<li>UPLOADABLE</li>
<li>SAMPLEABLE</li>
<li>BLIT_SRC</li>
<li>BLIT_DST</li>
<li>SUBPASS_INPUT</li>
</ul>
</div>

Binary file not shown.

Binary file not shown.

View File

@@ -22,26 +22,27 @@ converting <a href="https://github.com/google/filament/blob/main/assets/models/m
<p>Next, let&#x27;s create mipmapped KTX files using filament&#x27;s <code>mipgen</code> tool. We&#x27;ll create compressed and
non-compressed variants for each texture, since not all platforms support the same compression
formats. First copy over the PNG files from the <a href="https://github.com/google/filament/blob/main/assets/models/monkey">monkey folder</a>, then do:</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># Create mipmaps for base color</span>
mipgen albedo.png albedo.ktx2
mipgen --compression<span style="color: #666666">=</span>uastc albedo.png albedo.ktx2
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># Create mipmaps for base color and two compressed variants.</span>
mipgen albedo.png albedo.ktx
mipgen --compression<span style="color: #666666">=</span>astc_fast_ldr_4x4 albedo.png albedo_astc.ktx
mipgen --compression<span style="color: #666666">=</span>s3tc_rgb_dxt1 albedo.png albedo_s3tc_srgb.ktx
<span style="color: #3D7B7B; font-style: italic"># Create mipmaps for the normal map and a compressed variant.</span>
mipgen --strip-alpha --kernel<span style="color: #666666">=</span>NORMALS --linear normal.png normal.ktx
mipgen --strip-alpha --kernel<span style="color: #666666">=</span>NORMALS --linear --compression<span style="color: #666666">=</span>uastc_normals <span style="color: #AA5D1F; font-weight: bold">\</span>
normal.png normal.ktx2
mipgen --strip-alpha --kernel<span style="color: #666666">=</span>NORMALS --linear --compression<span style="color: #666666">=</span>etc_rgb8_normalxyz_40 <span style="color: #AA5D1F; font-weight: bold">\</span>
normal.png normal_etc.ktx
<span style="color: #3D7B7B; font-style: italic"># Create mipmaps for the single-component roughness map and a compressed variant.</span>
mipgen --grayscale roughness.png roughness.ktx
mipgen --grayscale --compression<span style="color: #666666">=</span>uastc roughness.png roughness.ktx2
mipgen --grayscale --compression<span style="color: #666666">=</span>etc_r11_numeric_40 roughness.png roughness_etc.ktx
<span style="color: #3D7B7B; font-style: italic"># Create mipmaps for the single-component metallic map and a compressed variant.</span>
mipgen --grayscale metallic.png metallic.ktx
mipgen --grayscale --compression<span style="color: #666666">=</span>uastc metallic.png metallic.ktx2
mipgen --grayscale --compression<span style="color: #666666">=</span>etc_r11_numeric_40 metallic.png metallic_etc.ktx
<span style="color: #3D7B7B; font-style: italic"># Create mipmaps for the single-component occlusion map and a compressed variant.</span>
mipgen --grayscale ao.png ao.ktx
mipgen --grayscale --compression<span style="color: #666666">=</span>uastc ao.png ao.ktx2
mipgen --grayscale --compression<span style="color: #666666">=</span>etc_r11_numeric_40 ao.png ao_etc.ktx
</pre></div>
<p>For more information on mipgen&#x27;s arguments and supported formats, do <code>mipgen --help</code>.</p>
@@ -206,11 +207,11 @@ when the assets have finished downloading.</p>
recreate the skybox using a higher-resolution texture. As a last step we unhide the renderable that
was created in the app constructor.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>Filament.fetch([sky_large_url,<span style="color: #bbbbbb"> </span>albedo_url,<span style="color: #bbbbbb"> </span>roughness_url,<span style="color: #bbbbbb"> </span>metallic_url,<span style="color: #bbbbbb"> </span>normal_url,<span style="color: #bbbbbb"> </span>ao_url],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>albedo<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(albedo_url,<span style="color: #bbbbbb"> </span>{srgb<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">true</span>});<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>roughness<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(roughness_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>metallic<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(metallic_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>normal<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(normal_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ao<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(ao_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>albedo<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(albedo_url,<span style="color: #bbbbbb"> </span>{srgb<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">true</span>});<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>roughness<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(roughness_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>metallic<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(metallic_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>normal<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(normal_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ao<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(ao_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sampler<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.TextureSampler(<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>Filament.MinFilter.LINEAR_MIPMAP_LINEAR,<span style="color: #bbbbbb"></span>

View File

@@ -33,13 +33,13 @@ class App {
});
Filament.fetch([sky_large_url, albedo_url, roughness_url, metallic_url, normal_url, ao_url],
() => {
const albedo = this.engine.createTextureFromKtx2(albedo_url, {
const albedo = this.engine.createTextureFromKtx1(albedo_url, {
srgb: true
});
const roughness = this.engine.createTextureFromKtx2(roughness_url);
const metallic = this.engine.createTextureFromKtx2(metallic_url);
const normal = this.engine.createTextureFromKtx2(normal_url);
const ao = this.engine.createTextureFromKtx2(ao_url);
const roughness = this.engine.createTextureFromKtx1(roughness_url);
const metallic = this.engine.createTextureFromKtx1(metallic_url);
const normal = this.engine.createTextureFromKtx1(normal_url);
const ao = this.engine.createTextureFromKtx1(ao_url);
const sampler = new Filament.TextureSampler(Filament.MinFilter.LINEAR_MIPMAP_LINEAR,
Filament.MagFilter.LINEAR, Filament.WrapMode.CLAMP_TO_EDGE);
this.matinstance.setTextureParameter('albedo', albedo, sampler);

View File

@@ -209,13 +209,10 @@ set(PRIVATE_HDRS
)
set(MATERIAL_SRCS
src/materials/antiAliasing/fxaa.mat
src/materials/antiAliasing/taa.mat
src/materials/blitLow.mat
src/materials/bloom/bloomDownsample.mat
src/materials/bloom/bloomDownsample2x.mat
src/materials/bloom/bloomDownsample9.mat
src/materials/bloom/bloomUpsample.mat
src/materials/fsr/fsr_easu.mat
src/materials/fsr/fsr_easu_mobile.mat
src/materials/fsr/fsr_easu_mobileF.mat
src/materials/fsr/fsr_rcas.mat
src/materials/colorGrading/colorGrading.mat
src/materials/colorGrading/colorGradingAsSubpass.mat
src/materials/colorGrading/customResolveAsSubpass.mat
@@ -223,30 +220,30 @@ set(MATERIAL_SRCS
src/materials/defaultMaterial.mat
src/materials/dof/dof.mat
src/materials/dof/dofCoc.mat
src/materials/dof/dofCombine.mat
src/materials/dof/dofDilate.mat
src/materials/dof/dofDownsample.mat
src/materials/dof/dofMedian.mat
src/materials/dof/dofMipmap.mat
src/materials/dof/dofCombine.mat
src/materials/dof/dofTiles.mat
src/materials/dof/dofTilesSwizzle.mat
src/materials/dof/dofDilate.mat
src/materials/dof/dofMipmap.mat
src/materials/dof/dofMedian.mat
src/materials/flare/flare.mat
src/materials/fsr/fsr_easu.mat
src/materials/fsr/fsr_easu_mobile.mat
src/materials/fsr/fsr_easu_mobileF.mat
src/materials/fsr/fsr_rcas.mat
src/materials/resolveDepth.mat
src/materials/separableGaussianBlur.mat
src/materials/skybox.mat
src/materials/blitLow.mat
src/materials/bloom/bloomDownsample.mat
src/materials/bloom/bloomUpsample.mat
src/materials/ssao/bilateralBlur.mat
src/materials/ssao/bilateralBlurBentNormals.mat
src/materials/ssao/mipmapDepth.mat
src/materials/skybox.mat
src/materials/ssao/sao.mat
src/materials/ssao/saoBentNormals.mat
src/materials/separableGaussianBlur.mat
src/materials/antiAliasing/fxaa.mat
src/materials/antiAliasing/taa.mat
src/materials/vsmMipmap.mat
)
set(MATERIAL_FL0_SRCS
set(MATERIAL_ES2_SRCS
src/materials/defaultMaterial0.mat
src/materials/skybox0.mat
)
@@ -275,11 +272,6 @@ if (NOT DFG_LUT_SIZE)
endif()
message(STATUS "DFG LUT size set to ${DFG_LUT_SIZE}x${DFG_LUT_SIZE}")
# Whether to include FL0 materials.
if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
add_definitions(-DFILAMENT_ENABLE_FEATURE_LEVEL_0)
endif()
# ==================================================================================================
# Definitions
# ==================================================================================================
@@ -321,15 +313,15 @@ foreach (mat_src ${MATERIAL_SRCS})
list(APPEND MATERIAL_BINS ${output_path})
endforeach()
if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
foreach (mat_src ${MATERIAL_FL0_SRCS})
if (IS_MOBILE_TARGET AND FILAMENT_SUPPORTS_OPENGL)
foreach (mat_src ${MATERIAL_ES2_SRCS})
get_filename_component(localname "${mat_src}" NAME_WE)
get_filename_component(fullname "${mat_src}" ABSOLUTE)
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
add_custom_command(
OUTPUT ${output_path}
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
COMMAND matc -a opengl -p ${MATC_TARGET} ${MATC_OPT_FLAGS} -o ${output_path} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
COMMENT "Compiling material ${mat_src} to ${output_path}"
@@ -563,7 +555,7 @@ if (MSVC)
set(FILAMENT_WARNINGS /W3)
else()
set(FILAMENT_WARNINGS
-Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers
-Wall -Wextra -Wno-unused-parameter
-Wextra-semi -Wnewline-eof -Wdeprecated -Wundef
-Wgnu-conditional-omitted-operand
-Wweak-vtables -Wnon-virtual-dtor -Wclass-varargs -Wimplicit-fallthrough

View File

@@ -61,7 +61,7 @@ with the platform name, for example, `filament-20181009-linux.tgz`.
Create a file, `main.cpp`, in the same directory with the following contents:
```c++
```
#include <filament/FilamentAPI.h>
#include <filament/Engine.h>
@@ -91,7 +91,7 @@ Copy your platform's Makefile below into a `Makefile` inside the same directory.
### Linux
```make
```
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl
CC=clang++
@@ -109,7 +109,7 @@ clean:
### macOS
```make
```
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl
FRAMEWORKS=-framework Cocoa -framework Metal -framework CoreVideo
CC=clang++
@@ -137,7 +137,7 @@ be sure to also include `matdbg.lib` in `FILAMENT_LIBS`.
When building Filament from source, the `USE_STATIC_CRT` CMake option can be
used to change the run-time library version.
```make
```
FILAMENT_LIBS=filament.lib backend.lib bluegl.lib bluevk.lib filabridge.lib filaflat.lib \
utils.lib geometry.lib smol-v.lib ibl.lib vkshaders.lib
CC=cl.exe
@@ -171,12 +171,12 @@ and invoke `nmake` instead of `make`.
### Generating C++ documentation
To generate the documentation you must first install `doxygen` and `graphviz`, then run the
To generate the documentation you must first install `doxygen` and `graphviz`, then run the
following commands:
```shell
cd filament/filament
doxygen docs/doxygen/filament.doxygen
```
$ cd filament/filament
$ doxygen docs/doxygen/filament.doxygen
```
Finally simply open `docs/html/index.html` in your web browser.

View File

@@ -27,7 +27,6 @@ set(SRCS
src/BackendUtils.cpp
src/BlobCacheKey.cpp
src/Callable.cpp
src/CallbackManager.cpp
src/CircularBuffer.cpp
src/CommandBufferQueue.cpp
src/CommandStream.cpp
@@ -55,7 +54,6 @@ set(PRIVATE_HDRS
include/private/backend/HandleAllocator.h
include/private/backend/PlatformFactory.h
include/private/backend/SamplerGroup.h
src/CallbackManager.h
src/CommandStreamDispatcher.h
src/CompilerThreadPool.h
src/DataReshaper.h
@@ -69,6 +67,8 @@ set(PRIVATE_HDRS
if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3 AND NOT FILAMENT_USE_SWIFTSHADER)
list(APPEND SRCS
include/backend/platforms/OpenGLPlatform.h
src/opengl/CallbackManager.h
src/opengl/CallbackManager.cpp
src/opengl/gl_headers.cpp
src/opengl/gl_headers.h
src/opengl/GLUtils.cpp
@@ -133,7 +133,6 @@ if (FILAMENT_SUPPORTS_METAL)
src/metal/MetalExternalImage.mm
src/metal/MetalHandles.mm
src/metal/MetalPlatform.mm
src/metal/MetalShaderCompiler.mm
src/metal/MetalState.mm
src/metal/MetalTimerQuery.mm
src/metal/MetalUtils.mm
@@ -169,8 +168,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/platform/VulkanPlatform.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
src/vulkan/spirv/VulkanSpirvUtils.cpp
src/vulkan/spirv/VulkanSpirvUtils.h
src/vulkan/VulkanBlitter.cpp
src/vulkan/VulkanBlitter.h
src/vulkan/VulkanBuffer.cpp
@@ -315,7 +312,6 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN)
target_link_libraries(${TARGET} PUBLIC bluevk vkmemalloc vkshaders smol-v)
target_link_libraries(${TARGET} PRIVATE SPIRV-Headers)
endif()
if (FILAMENT_SUPPORTS_METAL)
@@ -359,7 +355,7 @@ if (MSVC)
set(FILAMENT_WARNINGS /W3)
else()
set(FILAMENT_WARNINGS
-Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers
-Wall -Wextra -Wno-unused-parameter
-Wextra-semi -Wnewline-eof -Wdeprecated -Wundef
-Wgnu-conditional-omitted-operand
-Wweak-vtables -Wnon-virtual-dtor -Wclass-varargs -Wimplicit-fallthrough
@@ -400,8 +396,8 @@ install(DIRECTORY ${PUBLIC_HDR_DIR}/backend DESTINATION include)
# ==================================================================================================
option(INSTALL_BACKEND_TEST "Install the backend test library so it can be consumed on iOS" OFF)
if (APPLE OR LINUX)
set(BACKEND_TEST_SRC
if (APPLE)
add_library(backend_test STATIC
test/BackendTest.cpp
test/ShaderGenerator.cpp
test/TrianglePrimitive.cpp
@@ -413,26 +409,18 @@ if (APPLE OR LINUX)
test/test_BufferUpdates.cpp
test/test_MRT.cpp
test/test_LoadImage.cpp
test/test_RenderExternalImage.cpp
test/test_StencilBuffer.cpp
test/test_Scissor.cpp
test/test_MipLevels.cpp
)
set(BACKEND_TEST_LIBS
)
target_link_libraries(backend_test PRIVATE
backend
getopt
gtest
imageio
filamat
SPIRV
spirv-cross-glsl)
endif()
if (APPLE)
# TODO: we should expand this test to Linux and other platforms.
list(APPEND BACKEND_TEST_SRC
test/test_RenderExternalImage.cpp)
add_library(backend_test STATIC ${BACKEND_TEST_SRC})
target_link_libraries(backend_test PRIVATE ${BACKEND_TEST_LIBS})
set(BACKEND_TEST_DEPS
OGLCompiler
@@ -446,11 +434,12 @@ if (APPLE)
glslang
spirv-cross-core
spirv-cross-glsl
spirv-cross-msl)
spirv-cross-msl
)
if (NOT IOS)
target_link_libraries(backend_test PRIVATE image imageio)
list(APPEND BACKEND_TEST_DEPS image)
list(APPEND BACKEND_TEST_DEPS image imageio)
endif()
set(BACKEND_TEST_COMBINED_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/libbackendtest_combined.a")
@@ -464,21 +453,15 @@ if (APPLE)
endif()
set_target_properties(backend_test PROPERTIES FOLDER Tests)
if (APPLE AND NOT IOS)
add_executable(backend_test_mac test/mac_runner.mm)
target_link_libraries(backend_test_mac PRIVATE "-framework Metal -framework AppKit -framework QuartzCore")
# Because each test case is a separate file, the -force_load flag is necessary to prevent the
# 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)
endif()
endif()
if (LINUX)
add_executable(backend_test_linux test/linux_runner.cpp ${BACKEND_TEST_SRC})
target_link_libraries(backend_test_linux PRIVATE ${BACKEND_TEST_LIBS})
set_target_properties(backend_test_linux PROPERTIES FOLDER Tests)
if (APPLE AND NOT IOS)
add_executable(backend_test_mac test/mac_runner.mm)
target_link_libraries(backend_test_mac PRIVATE "-framework Metal -framework AppKit -framework QuartzCore")
# Because each test case is a separate file, the -force_load flag is necessary to prevent the
# 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)
endif()
# ==================================================================================================

View File

@@ -113,7 +113,7 @@ public:
/**
* Helper to create a BufferDescriptor that uses a KNOWN method pointer w/ object passed
* by pointer as the callback. e.g.:
* auto bd = BufferDescriptor::make<Foo, &Foo::method>(buffer, size, foo);
* auto bd = BufferDescriptor::make(buffer, size, &Foo::method, foo);
*
* @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes
@@ -121,12 +121,12 @@ public:
* @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,
CallbackHandler* handler = nullptr) noexcept {
static BufferDescriptor make(
void const* buffer, size_t size, T* data, CallbackHandler* handler = nullptr) noexcept {
return {
buffer, size,
handler, [](void* b, size_t s, void* u) {
(static_cast<T*>(u)->*method)(b, s);
(*static_cast<T**>(u)->*method)(b, s);
}, data
};
}
@@ -145,14 +145,14 @@ public:
* @return a new BufferDescriptor
*/
template<typename T>
static BufferDescriptor make(void const* buffer, size_t size, T&& functor,
CallbackHandler* handler = nullptr) noexcept {
static BufferDescriptor make(
void const* buffer, size_t size, T&& functor, CallbackHandler* handler = nullptr) noexcept {
return {
buffer, size,
handler, [](void* b, size_t s, void* u) {
T* const that = static_cast<T*>(u);
that->operator()(b, s);
delete that;
T& that = *static_cast<T*>(u);
that(b, s);
delete &that;
},
new T(std::forward<T>(functor))
};
@@ -201,7 +201,7 @@ public:
return mUser;
}
//! CPU memory-buffer virtual address
//! CPU mempry-buffer virtual address
void* buffer = nullptr;
//! CPU memory-buffer size in bytes

View File

@@ -77,11 +77,6 @@ static constexpr uint64_t SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = 0x8;
*/
static constexpr uint64_t SWAP_CHAIN_CONFIG_SRGB_COLORSPACE = 0x10;
/**
* Indicates that the SwapChain should also contain a stencil component.
*/
static constexpr uint64_t SWAP_CHAIN_HAS_STENCIL_BUFFER = 0x20;
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_SAMPLER_COUNT = 62; // Maximum needed at feature level 3.
@@ -143,30 +138,6 @@ static constexpr const char* backendToString(Backend backend) {
}
}
/**
* Defines the shader language. Similar to the above backend enum, but the OpenGL backend can select
* between two shader languages: ESSL 1.0 and ESSL 3.0.
*/
enum class ShaderLanguage {
ESSL1 = 0,
ESSL3 = 1,
SPIRV = 2,
MSL = 3,
};
static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) {
switch (shaderLanguage) {
case ShaderLanguage::ESSL1:
return "ESSL 1.0";
case ShaderLanguage::ESSL3:
return "ESSL 3.0";
case ShaderLanguage::SPIRV:
return "SPIR-V";
case ShaderLanguage::MSL:
return "MSL";
}
}
/**
* Bitmask for selecting render buffers
*/
@@ -227,7 +198,6 @@ struct Viewport {
int32_t top() const noexcept { return bottom + int32_t(height); }
};
/**
* Specifies the mapping of the near and far clipping plane to window coordinates.
*/
@@ -659,15 +629,13 @@ enum class TextureFormat : uint16_t {
//! Bitmask describing the intended Texture Usage
enum class TextureUsage : uint8_t {
NONE = 0x00,
COLOR_ATTACHMENT = 0x01, //!< Texture can be used as a color attachment
DEPTH_ATTACHMENT = 0x02, //!< Texture can be used as a depth attachment
STENCIL_ATTACHMENT = 0x04, //!< Texture can be used as a stencil attachment
UPLOADABLE = 0x08, //!< Data can be uploaded into this texture (default)
NONE = 0x0,
COLOR_ATTACHMENT = 0x1, //!< Texture can be used as a color attachment
DEPTH_ATTACHMENT = 0x2, //!< Texture can be used as a depth attachment
STENCIL_ATTACHMENT = 0x4, //!< Texture can be used as a stencil attachment
UPLOADABLE = 0x8, //!< Data can be uploaded into this texture (default)
SAMPLEABLE = 0x10, //!< Texture can be sampled (default)
SUBPASS_INPUT = 0x20, //!< Texture can be used as a subpass input
BLIT_SRC = 0x40, //!< Texture can be used the source of a blit()
BLIT_DST = 0x80, //!< Texture can be used the destination of a blit()
DEFAULT = UPLOADABLE | SAMPLEABLE //!< Default texture usage
};
@@ -695,17 +663,6 @@ static constexpr bool isDepthFormat(TextureFormat format) noexcept {
}
}
static constexpr bool isStencilFormat(TextureFormat format) noexcept {
switch (format) {
case TextureFormat::STENCIL8:
case TextureFormat::DEPTH24_STENCIL8:
case TextureFormat::DEPTH32F_STENCIL8:
return true;
default:
return false;
}
}
static constexpr bool isUnsignedIntFormat(TextureFormat format) {
switch (format) {
case TextureFormat::R8UI:
@@ -856,22 +813,22 @@ struct SamplerParams { // NOLINT
struct Hasher {
size_t operator()(SamplerParams p) const noexcept {
// we don't use std::hash<> here, so we don't have to include <functional>
return *reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&p));
return *reinterpret_cast<uint64_t const*>(reinterpret_cast<char const*>(&p));
}
};
struct EqualTo {
bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept {
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
auto* pLhs = reinterpret_cast<uint64_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint64_t const*>(reinterpret_cast<char const*>(&rhs));
return *pLhs == *pRhs;
}
};
struct LessThan {
bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept {
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
auto* pLhs = reinterpret_cast<uint64_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint64_t const*>(reinterpret_cast<char const*>(&rhs));
return *pLhs == *pRhs;
}
};
@@ -881,7 +838,6 @@ private:
return SamplerParams::LessThan{}(lhs, rhs);
}
};
static_assert(sizeof(SamplerParams) == 4);
// The limitation to 64-bits max comes from how we store a SamplerParams in our JNI code
// see android/.../TextureSampler.cpp
@@ -1194,8 +1150,8 @@ using FrameScheduledCallback = void(*)(PresentCallable callable, void* user);
enum class Workaround : uint16_t {
// The EASU pass must split because shader compiler flattens early-exit branch
SPLIT_EASU,
// Backend allows feedback loop with ancillary buffers (depth/stencil) as long as they're
// read-only for the whole render pass.
// Backend allows feedback loop with ancillary buffers (depth/stencil) as long as they're read-only for
// the whole render pass.
ALLOW_READ_ONLY_ANCILLARY_FEEDBACK_LOOP,
// for some uniform arrays, it's needed to do an initialization to avoid crash on adreno gpu
ADRENO_UNIFORM_ARRAY_CRASH,

View File

@@ -141,7 +141,7 @@ public:
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type, alignment, left, top, stride,
handler, [](void* b, size_t s, void* u) {
(static_cast<T*>(u)->*method)(b, s); }, data };
(*static_cast<T**>(u)->*method)(b, s); }, data };
}
template<typename T, void(T::*method)(void const*, size_t)>
@@ -149,7 +149,7 @@ public:
PixelDataFormat format, PixelDataType type, T* data,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type, handler, [](void* b, size_t s, void* u) {
(static_cast<T*>(u)->*method)(b, s); }, data };
(*static_cast<T**>(u)->*method)(b, s); }, data };
}
template<typename T, void(T::*method)(void const*, size_t)>
@@ -157,7 +157,7 @@ public:
backend::CompressedPixelDataType format, uint32_t imageSize, T* data,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, imageSize, handler, [](void* b, size_t s, void* u) {
(static_cast<T*>(u)->*method)(b, s); }, data
(*static_cast<T**>(u)->*method)(b, s); }, data
};
}
@@ -168,9 +168,9 @@ public:
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type, alignment, left, top, stride,
handler, [](void* b, size_t s, void* u) {
T* const that = static_cast<T*>(u);
that->operator()(b, s);
delete that;
T& that = *static_cast<T*>(u);
that(b, s);
delete &that;
}, new T(std::forward<T>(functor))
};
}
@@ -181,9 +181,9 @@ public:
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type,
handler, [](void* b, size_t s, void* u) {
T* const that = static_cast<T*>(u);
that->operator()(b, s);
delete that;
T& that = *static_cast<T*>(u);
that(b, s);
delete &that;
}, new T(std::forward<T>(functor))
};
}
@@ -194,9 +194,9 @@ public:
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, imageSize,
handler, [](void* b, size_t s, void* u) {
T* const that = static_cast<T*>(u);
that->operator()(b, s);
delete that;
T& that = *static_cast<T*>(u);
that(b, s);
delete &that;
}, new T(std::forward<T>(functor))
};
}

View File

@@ -46,13 +46,6 @@ public:
* Driver clamps to valid values.
*/
size_t handleArenaSize = 0;
/*
* this number of most-recently destroyed textures will be tracked for use-after-free.
* Throws an exception when a texture is freed but still bound to a SamplerGroup and used in
* a draw call. 0 disables completely. Currently only respected by the Metal backend.
*/
size_t textureUseAfterFreePoolSize = 0;
};
Platform() noexcept;
@@ -114,7 +107,6 @@ public:
* Platform. The <insert> and <retrieve> Invocables may be called at any time and
* from any thread from the time at which setBlobFunc is called until the time that Platform
* is destroyed. Concurrent calls to these functions from different threads is also allowed.
* Either function can be null.
*
* @param insertBlob an Invocable that inserts a new value into the cache and associates
* it with the given key
@@ -124,21 +116,9 @@ public:
void setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept;
/**
* @return true if insertBlob is valid.
* @return true if setBlobFunc was called.
*/
bool hasInsertBlobFunc() const noexcept;
/**
* @return true if retrieveBlob is valid.
*/
bool hasRetrieveBlobFunc() const noexcept;
/**
* @return true if either of insertBlob or retrieveBlob are valid.
*/
bool hasBlobFunc() const noexcept {
return hasInsertBlobFunc() || hasRetrieveBlobFunc();
}
bool hasBlobFunc() const noexcept;
/**
* To insert a new binary value into the cache and associate it with a given

View File

@@ -67,7 +67,7 @@ public:
Program& operator=(const Program& rhs) = delete;
Program(Program&& rhs) noexcept;
Program& operator=(Program&& rhs) noexcept = delete;
Program& operator=(Program&& rhs) noexcept;
~Program() noexcept;
@@ -104,9 +104,8 @@ public:
Sampler const* samplers, size_t count) noexcept;
struct SpecializationConstant {
using Type = std::variant<int32_t, float, bool>;
uint32_t id; // id set in glsl
Type value; // value and type
std::variant<int32_t, float, bool> value; // value and type
};
Program& specializationConstants(

View File

@@ -42,9 +42,6 @@ public:
void createContext(bool shared) override;
void releaseContext() noexcept override;
// Return true if we're on an OpenGL platform (as opposed to OpenGL ES). false by default.
virtual bool isOpenGL() const noexcept;
protected:
// --------------------------------------------------------------------------------------------
@@ -129,7 +126,6 @@ protected:
EGLSurface mCurrentDrawSurface = EGL_NO_SURFACE;
EGLSurface mCurrentReadSurface = EGL_NO_SURFACE;
EGLSurface mEGLDummySurface = EGL_NO_SURFACE;
// mEGLConfig is valid only if ext.egl.KHR_no_config_context is false
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
Config mContextAttribs;
std::vector<EGLContext> mAdditionalContexts;
@@ -150,8 +146,8 @@ protected:
void initializeGlExtensions() noexcept;
protected:
EGLConfig findSwapChainConfig(uint64_t flags, bool window, bool pbuffer) const;
private:
EGLConfig findSwapChainConfig(uint64_t flags) const;
};
} // namespace filament::backend

View File

@@ -30,9 +30,6 @@ public:
Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept override;
protected:
bool isOpenGL() const noexcept override;
};
} // namespace filament

View File

@@ -20,7 +20,6 @@
#include <backend/Platform.h>
#include <bluevk/BlueVK.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/PrivateImplementation.h>
@@ -90,44 +89,36 @@ public:
// ----------------------------------------------------
// ---------- Platform Customization options ----------
struct Customization {
/**
* The client can specify the GPU (i.e. VkDevice) for the platform. We allow the
* following preferences:
* 1) A substring to match against `VkPhysicalDeviceProperties.deviceName`. Empty string
* by default.
* 2) Index of the device in the list as returned by
* `vkEnumeratePhysicalDevices`. -1 by default to indicate no preference.
*/
struct GPUPreference {
utils::CString deviceName;
int8_t index = -1;
} gpu;
/**
* Whether the platform supports sRGB swapchain. Default is true.
*/
bool isSRGBSwapChainSupported = true;
/**
* When the platform window is resized, we will flush and wait on the command queues
* before recreating the swapchain. Default is true.
*/
bool flushAndWaitOnWindowResize = true;
/**
* The client preference can be stored within the struct. We allow for two specification of
* preference:
* 1) A substring to match against `VkPhysicalDeviceProperties.deviceName`.
* 2) Index of the device in the list as returned by vkEnumeratePhysicalDevices.
*/
struct GPUPreference {
std::string deviceName;
int8_t index = -1;
};
/**
* Client can override to indicate customized behavior or parameter for their platform.
* @return `Customization` struct that indicates the client's platform
* customizations.
* Client can provide a preference over the GPU to use in the vulkan instance
* @return `GPUPreference` struct that indicates the client's preference
*/
virtual Customization getCustomization() const noexcept {
virtual GPUPreference getPreferredGPU() noexcept {
return {};
}
// -------- End platform customization options --------
// ----------------------------------------------------
/**
* Returns whether the platform supports sRGB swapchain. This is true by default, and the client
* needs to override this method to specify otherwise.
* @return Whether the platform supports sRGB swapchain.
*/
virtual bool isSRGBSwapChainSupported() const {
return true;
}
/**
* Get the images handles and format of the memory backing the swapchain. This should be called
* after createSwapChain() or after recreateIfResized().

View File

@@ -73,14 +73,32 @@ public:
// a cost here (writing and reading the stack at each iteration), in the end it's
// probably better to pay the cost at just one location.
intptr_t next;
driver.mCurrentExecutingCommand = this;
mExecute(driver, this, &next);
return reinterpret_cast<CommandBase*>(reinterpret_cast<intptr_t>(this) + next);
}
inline void captureCallstack() noexcept {
auto c = utils::CallStack::unwind(4);
size_t i = 0;
for (; i < c.getFrameCount() && i < 16; i++) {
mCallstack[i] = c[i];
}
for (; i < 16; i++) {
mCallstack[i] = 0;
}
}
void printCallstack() noexcept {
auto c = utils::CallStack(mCallstack);
utils::slog.d << c << utils::io::endl;
}
inline ~CommandBase() noexcept = default;
private:
Execute mExecute;
std::array<intptr_t, 16> mCallstack = {0};
};
// ------------------------------------------------------------------------------------------------
@@ -218,6 +236,7 @@ public:
using Cmd = COMMAND_TYPE(methodName); \
void* const p = allocateCommand(CommandBase::align(sizeof(Cmd))); \
new(p) Cmd(mDispatcher.methodName##_, APPLY(std::move, params)); \
((Cmd*)p)->captureCallstack(); \
DEBUG_COMMAND_END(methodName, false); \
}
@@ -237,6 +256,7 @@ public:
using Cmd = COMMAND_TYPE(methodName##R); \
void* const p = allocateCommand(CommandBase::align(sizeof(Cmd))); \
new(p) Cmd(mDispatcher.methodName##_, RetType(result), APPLY(std::move, params)); \
((Cmd*)p)->captureCallstack(); \
DEBUG_COMMAND_END(methodName, false); \
return result; \
}

View File

@@ -24,7 +24,6 @@
#include <backend/PipelineState.h>
#include <backend/TargetBufferInfo.h>
#include <utils/CString.h>
#include <utils/compiler.h>
#include <functional>
@@ -54,6 +53,7 @@ template<typename T>
class ConcreteDispatcher;
class Dispatcher;
class CommandStream;
class CommandBase;
class Driver {
public:
@@ -84,6 +84,8 @@ public:
virtual void debugCommandEnd(CommandStream* cmds,
bool synchronous, const char* methodName) noexcept = 0;
CommandBase* mCurrentExecutingCommand = nullptr;
/*
* Asynchronous calls here only to provide a type to CommandStream. They must be non-virtual
* so that calling the concrete implementation won't go through a vtable.

View File

@@ -219,7 +219,7 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, importTexture,
backend::TextureUsage, usage)
DECL_DRIVER_API_R_N(backend::SamplerGroupHandle, createSamplerGroup,
uint32_t, size, utils::FixedSizeString<32>, debugName)
uint32_t, size)
DECL_DRIVER_API_R_N(backend::RenderPrimitiveHandle, createRenderPrimitive,
backend::VertexBufferHandle, vbh,
@@ -300,10 +300,10 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAutoDepthResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isSRGBSwapChainSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isStereoSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, canGenerateMipmaps)
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, getTimerQueryValue, backend::TimerQueryHandle, query, uint64_t*, elapsedTime)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isWorkaroundNeeded, backend::Workaround, workaround)
@@ -468,7 +468,7 @@ DECL_DRIVER_API_N(readBufferSubData,
* --------------------
*/
DECL_DRIVER_API_N(blitDEPRECATED,
DECL_DRIVER_API_N(blit,
backend::TargetBufferFlags, buffers,
backend::RenderTargetHandle, dst,
backend::Viewport, dstRect,
@@ -476,21 +476,6 @@ DECL_DRIVER_API_N(blitDEPRECATED,
backend::Viewport, srcRect,
backend::SamplerMagFilter, filter)
DECL_DRIVER_API_N(resolve,
backend::TextureHandle, dst,
uint8_t, dstLevel, uint8_t, dstLayer,
backend::TextureHandle, src,
uint8_t, srcLevel, uint8_t, srcLayer)
DECL_DRIVER_API_N(blit,
backend::TextureHandle, dst,
uint8_t, dstLevel, uint8_t, dstLayer,
math::uint2, dstOrigin,
backend::TextureHandle, src,
uint8_t, srcLevel, uint8_t, srcLayer,
math::uint2, srcOrigin,
math::uint2, size)
DECL_DRIVER_API_N(draw,
backend::PipelineState, state,
backend::RenderPrimitiveHandle, rph,

View File

@@ -33,7 +33,7 @@
#define HandleAllocatorGL HandleAllocator<16, 64, 208>
#define HandleAllocatorVK HandleAllocator<16, 64, 880>
#define HandleAllocatorMTL HandleAllocator<16, 64, 584>
#define HandleAllocatorMTL HandleAllocator<16, 64, 576>
namespace filament::backend {
@@ -239,16 +239,14 @@ private:
}
};
// FIXME: We should be using a Spinlock here, at least on platforms where mutexes are not
// efficient (i.e. non-Linux). However, we've seen some hangs on that spinlock, which
// we don't understand well (b/308029108).
#ifndef NDEBUG
using HandleArena = utils::Arena<Allocator,
utils::LockingPolicy::Mutex,
utils::LockingPolicy::SpinLock,
utils::TrackingPolicy::DebugAndHighWatermark>;
#else
using HandleArena = utils::Arena<Allocator,
utils::LockingPolicy::Mutex>;
utils::LockingPolicy::SpinLock>;
#endif
// allocateHandle()/deallocateHandle() selects the pool to use at compile-time based on the
@@ -258,7 +256,6 @@ private:
HandleBase::HandleId allocateHandle() noexcept {
if constexpr (SIZE <= P0) { return allocateHandleInPool<P0>(); }
if constexpr (SIZE <= P1) { return allocateHandleInPool<P1>(); }
static_assert(SIZE <= P2);
return allocateHandleInPool<P2>();
}
@@ -269,7 +266,6 @@ private:
} else if constexpr (SIZE <= P1) {
deallocateHandleFromPool<P1>(id);
} else {
static_assert(SIZE <= P2);
deallocateHandleFromPool<P2>(id);
}
}

View File

@@ -28,16 +28,14 @@ bool Platform::pumpEvents() noexcept {
}
void Platform::setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept {
mInsertBlob = std::move(insertBlob);
mRetrieveBlob = std::move(retrieveBlob);
if (!mInsertBlob && !mRetrieveBlob) {
mInsertBlob = std::move(insertBlob);
mRetrieveBlob = std::move(retrieveBlob);
}
}
bool Platform::hasInsertBlobFunc() const noexcept {
return bool(mInsertBlob);
}
bool Platform::hasRetrieveBlobFunc() const noexcept {
return bool(mRetrieveBlob);
bool Platform::hasBlobFunc() const noexcept {
return mInsertBlob && mRetrieveBlob;
}
void Platform::insertBlob(void const* key, size_t keySize, void const* value, size_t valueSize) {

View File

@@ -26,6 +26,17 @@ Program::Program() noexcept { // NOLINT(modernize-use-equals-default)
Program::Program(Program&& rhs) noexcept = default;
Program& Program::operator=(Program&& rhs) noexcept {
mUniformBlocks.operator=(rhs.mUniformBlocks);
mSamplerGroups.operator=(std::move(rhs.mSamplerGroups));
mShadersSource.operator=(std::move(rhs.mShadersSource));
mName.operator=(std::move(rhs.mName));
mLogger.operator=(std::move(rhs.mLogger));
mSpecializationConstants.operator=(std::move(rhs.mSpecializationConstants));
mBindingUniformInfo.operator=(std::move(rhs.mBindingUniformInfo));
return *this;
}
Program::~Program() noexcept = default;
Program& Program::priorityQueue(CompilerPriorityQueue priorityQueue) noexcept {

View File

@@ -24,7 +24,8 @@
#include <tsl/robin_map.h>
#include <utils/Hash.h>
namespace filament::backend {
namespace filament {
namespace backend {
struct MetalContext;
@@ -36,7 +37,8 @@ public:
struct BlitArgs {
struct Attachment {
id<MTLTexture> texture = nil;
id<MTLTexture> color = nil;
id<MTLTexture> depth = nil;
MTLRegion region = {};
uint8_t level = 0;
uint32_t slice = 0; // must be 0 on source attachment
@@ -44,13 +46,25 @@ public:
// Valid source formats: 2D, 2DArray, 2DMultisample, 3D
// Valid destination formats: 2D, 2DArray, 3D, Cube
Attachment source;
Attachment destination;
Attachment source, destination;
SamplerMagFilter filter;
bool destinationIsFullAttachment() const {
return destination.texture.width == destination.region.size.width &&
destination.texture.height == destination.region.size.height;
bool blitColor() const {
return source.color != nil && destination.color != nil;
}
bool blitDepth() const {
return source.depth != nil && destination.depth != nil;
}
bool colorDestinationIsFullAttachment() const {
return destination.color.width == destination.region.size.width &&
destination.color.height == destination.region.size.height;
}
bool depthDestinationIsFullAttachment() const {
return destination.depth.width == destination.region.size.width &&
destination.depth.height == destination.region.size.height;
}
};
@@ -64,36 +78,48 @@ public:
private:
static void setupAttachment(MTLRenderPassAttachmentDescriptor* descriptor,
const BlitArgs& args, uint32_t depthPlane);
static void setupColorAttachment(const BlitArgs& args, MTLRenderPassDescriptor* descriptor,
uint32_t depthPlane);
static void setupDepthAttachment(const BlitArgs& args, MTLRenderPassDescriptor* descriptor,
uint32_t depthPlane);
struct BlitFunctionKey {
bool msaaColorSource{};
bool sources3D{};
char padding[2]{};
bool blitColor;
bool blitDepth;
bool msaaColorSource;
bool msaaDepthSource;
bool sources3D;
char padding[3];
bool isValid() const noexcept {
// MSAA 3D textures do not exist.
bool const hasMsaa = msaaColorSource;
bool hasMsaa = msaaColorSource || msaaDepthSource;
return !(hasMsaa && sources3D);
}
bool operator==(const BlitFunctionKey& rhs) const noexcept {
return msaaColorSource == rhs.msaaColorSource &&
return blitColor == rhs.blitColor &&
blitDepth == rhs.blitDepth &&
msaaColorSource == rhs.msaaColorSource &&
msaaDepthSource == rhs.msaaDepthSource &&
sources3D == rhs.sources3D;
}
BlitFunctionKey() {
std::memset(this, 0, sizeof(BlitFunctionKey));
}
};
static bool blitFastPath(id<MTLCommandBuffer> cmdBuffer,
void blitFastPath(id<MTLCommandBuffer> cmdBuffer, bool& blitColor, bool& blitDepth,
const BlitArgs& args, const char* label);
void blitSlowPath(id<MTLCommandBuffer> cmdBuffer,
void blitSlowPath(id<MTLCommandBuffer> cmdBuffer, bool& blitColor, bool& blitDepth,
const BlitArgs& args, const char* label);
void blitDepthPlane(id <MTLCommandBuffer> cmdBuffer, const BlitArgs& args,
uint32_t depthPlaneSource, uint32_t depthPlaneDest, const char* label);
id<MTLFunction> compileFragmentFunction(BlitFunctionKey key) const;
void blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor, bool blitDepth,
const BlitArgs& args, uint32_t depthPlaneSource, uint32_t depthPlaneDest,
const char* label);
id<MTLTexture> createIntermediateTexture(id<MTLTexture> t, MTLSize size);
id<MTLFunction> compileFragmentFunction(BlitFunctionKey key);
id<MTLFunction> getBlitVertexFunction();
id<MTLFunction> getBlitFragmentFunction(BlitFunctionKey key);
@@ -104,9 +130,10 @@ private:
tsl::robin_map<BlitFunctionKey, Function, HashFn> mBlitFunctions;
id<MTLFunction> mVertexFunction = nil;
};
} // namespace filament::backend
} // namespace backend
} // namespace filament
#endif //TNT_METALBLITTER_H

View File

@@ -21,7 +21,8 @@
#include <utils/Panic.h>
namespace filament::backend {
namespace filament {
namespace backend {
static const char* functionLibrary = R"(
#include <metal_stdlib>
@@ -36,7 +37,13 @@ struct VertexOut
struct FragmentOut
{
#ifdef BLIT_COLOR
float4 color [[color(0)]];
#endif
#ifdef BLIT_DEPTH
float depth [[depth(any)]];
#endif
};
vertex VertexOut
@@ -60,6 +67,7 @@ fragment FragmentOut
blitterFrag(VertexOut in [[stage_in]],
sampler sourceSampler [[sampler(0)]],
#ifdef BLIT_COLOR
#ifdef MSAA_COLOR_SOURCE
texture2d_ms<float, access::read> sourceColor [[texture(0)]],
#elif SOURCES_3D
@@ -67,18 +75,32 @@ blitterFrag(VertexOut in [[stage_in]],
#else
texture2d<float, access::sample> sourceColor [[texture(0)]],
#endif // MSAA_COLOR_SOURCE
#endif // BLIT_COLOR
#ifdef BLIT_DEPTH
#ifdef MSAA_DEPTH_SOURCE
texture2d_ms<float, access::read> sourceDepth [[texture(1)]],
#elif SOURCES_3D
texture3d<float, access::sample> sourceDepth [[texture(1)]],
#else
texture2d<float, access::sample> sourceDepth [[texture(1)]],
#endif // MSAA_DEPTH_SOURCE
#endif // BLIT_DEPTH
constant FragmentArgs* args [[buffer(0)]])
{
FragmentOut out = {};
#if defined(BLIT_COLOR) || defined(BLIT_DEPTH)
// These coordinates match the Vulkan vkCmdBlitImage spec:
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdBlitImage.html
float2 uvbase = in.position.xy; // unnormalized coordinates at center of texel: (1.5, 2.5, etc)
float2 uvoffset = uvbase - args->dstOffset;
float2 uvscaled = uvoffset * args->scale;
float2 uv = uvscaled + args->srcOffset;
#endif
#ifdef BLIT_COLOR
#ifdef MSAA_COLOR_SOURCE
out.color = float4(0.0);
for (uint s = 0; s < sourceColor.get_num_samples(); s++) {
@@ -94,106 +116,201 @@ blitterFrag(VertexOut in [[stage_in]],
float2 uvnorm = uv / float2(sourceColor.get_width(args->lod), sourceColor.get_height(args->lod));
out.color += sourceColor.sample(sourceSampler, uvnorm, level(args->lod));
#endif // MSAA_COLOR_SOURCE
#endif // BLIT_COLOR
#ifdef BLIT_DEPTH
#ifdef MSAA_DEPTH_SOURCE
out.depth = 0.0;
for (uint s = 0; s < sourceDepth.get_num_samples(); s++) {
out.depth += sourceDepth.read(static_cast<uint2>(uv), s).r;
}
out.depth /= sourceDepth.get_num_samples();
#elif SOURCES_3D
float2 uvnormd = uv / float2(sourceDepth.get_width(args->lod), sourceDepth.get_height(args->lod));
float3 coords = float3(uvnormd, (static_cast<float>(args->depthPlane) + 0.5) /
sourceDepth.get_depth(args->lod));
out.depth = sourceDepth.sample(sourceSampler, coords, level(args->lod)).r;
#else
float2 uvnormd = uv / float2(sourceDepth.get_width(args->lod), sourceDepth.get_height(args->lod));
out.depth = sourceDepth.sample(sourceSampler, uvnormd, level(args->lod)).r;
#endif // MSAA_DEPTH_SOURCE
#endif // BLIT_DEPTH
return out;
}
)";
template<typename T>
inline bool MTLSizeEqual(T a, T b) noexcept {
return (a.width == b.width && a.height == b.height && a.depth == b.depth);
}
MetalBlitter::MetalBlitter(MetalContext& context) noexcept : mContext(context) { }
#define MTLSizeEqual(a, b) (a.width == b.width && a.height == b.height && a.depth == b.depth)
void MetalBlitter::blit(id<MTLCommandBuffer> cmdBuffer, const BlitArgs& args, const char* label) {
bool blitColor = args.blitColor();
bool blitDepth = args.blitDepth();
ASSERT_PRECONDITION(args.source.region.size.depth == args.destination.region.size.depth,
"Blitting requires the source and destination regions to have the same depth.");
if (args.source.color && args.source.depth) {
MTLTextureType colorType = args.source.color.textureType;
MTLTextureType depthType = args.source.depth.textureType;
if (colorType == MTLTextureType2DMultisample) colorType = MTLTextureType2D;
if (depthType == MTLTextureType2DMultisample) depthType = MTLTextureType2D;
ASSERT_PRECONDITION(colorType == depthType,
"Blitting requires color and depth sources to be the same texture type.");
}
// Determine if the blit for color or depth are eligible to use a MTLBlitCommandEncoder.
// blitFastPath returns true upon success.
// blitFastPath fails if either the format or sampleCount don't match (i.e. resolve)
if (blitFastPath(cmdBuffer, args, label)) {
// blitColor and / or blitDepth are set to false upon success, to indicate that no more work is
// necessary for that attachment.
blitFastPath(cmdBuffer, blitColor, blitDepth, args, label);
if (!blitColor && !blitDepth) {
return;
}
// If we end-up here, it means that either:
// - we're resolving a color texture
// - src/dest formats didn't match, or we're scaling -- this can only happen with the legacy
// blit() path and implies that the format is not depth.
// note: in the future we will support a fast-path resolve
// If the destination is MSAA and we weren't able to use the fast path, report an error, as
// blitting to a MSAA texture isn't supported through the "slow path" yet.
const bool colorDestinationIsMultisample =
blitColor && args.destination.color.textureType == MTLTextureType2DMultisample;
const bool depthDestinationIsMultisample =
blitDepth && args.destination.depth.textureType == MTLTextureType2DMultisample;
ASSERT_PRECONDITION(!colorDestinationIsMultisample && !depthDestinationIsMultisample,
"Blitting between MSAA render targets with differing pixel formats and/or regions is not supported.");
UTILS_UNUSED_IN_RELEASE
const bool destinationIsMultisample =
args.destination.texture.textureType == MTLTextureType2DMultisample;
// If the destination texture doesn't have the MTLTextureUsageRenderTarget flag, we have to blit
// to an intermediate texture first to perform the format conversion. Then, we can perform a
// "fast blit" to the final destination texture.
assert_invariant(!destinationIsMultisample);
assert_invariant((args.destination.texture.usage & MTLTextureUsageRenderTarget));
blitSlowPath(cmdBuffer, args, label);
}
id<MTLTexture> intermediateColor = nil;
id<MTLTexture> intermediateDepth = nil;
BlitArgs slowBlit = args;
BlitArgs finalBlit = args;
bool MetalBlitter::blitFastPath(id<MTLCommandBuffer> cmdBuffer,
const BlitArgs& args, const char* label) {
MTLRegion sourceRegionNoOffset = MTLRegionMake3D(0, 0, 0,
args.source.region.size.width, args.source.region.size.height,
args.source.region.size.depth);
if (args.source.texture.sampleCount == args.destination.texture.sampleCount &&
args.source.texture.pixelFormat == args.destination.texture.pixelFormat &&
MTLSizeEqual(args.source.region.size, args.destination.region.size)) {
id<MTLBlitCommandEncoder> const blitEncoder = [cmdBuffer blitCommandEncoder];
blitEncoder.label = @(label);
[blitEncoder copyFromTexture:args.source.texture
sourceSlice:args.source.slice
sourceLevel:args.source.level
sourceOrigin:args.source.region.origin
sourceSize:args.source.region.size
toTexture:args.destination.texture
destinationSlice:args.destination.slice
destinationLevel:args.destination.level
destinationOrigin:args.destination.region.origin];
[blitEncoder endEncoding];
return true;
if (blitColor && !(args.destination.color.usage & MTLTextureUsageRenderTarget)) {
intermediateColor = createIntermediateTexture(args.destination.color, args.source.region.size);
slowBlit.destination.color = finalBlit.source.color = intermediateColor;
slowBlit.destination.level = finalBlit.source.level = 0;
slowBlit.destination.slice = finalBlit.source.slice = 0;
slowBlit.destination.region = finalBlit.source.region = sourceRegionNoOffset;
}
return false;
if (blitDepth && !(args.destination.depth.usage & MTLTextureUsageRenderTarget)) {
intermediateDepth = createIntermediateTexture(args.destination.depth, args.source.region.size);
slowBlit.destination.depth = finalBlit.source.depth = intermediateDepth;
slowBlit.destination.level = finalBlit.source.level = 0;
slowBlit.destination.slice = finalBlit.source.slice = 0;
slowBlit.destination.region = finalBlit.source.region = sourceRegionNoOffset;
}
blitSlowPath(cmdBuffer, blitColor, blitDepth, slowBlit, label);
bool finalBlitColor = intermediateColor != nil;
bool finalBlitDepth = intermediateDepth != nil;
blitFastPath(cmdBuffer, finalBlitColor, finalBlitDepth, finalBlit, label);
}
void MetalBlitter::blitSlowPath(id<MTLCommandBuffer> cmdBuffer,
void MetalBlitter::blitFastPath(id<MTLCommandBuffer> cmdBuffer, bool& blitColor, bool& blitDepth,
const BlitArgs& args, const char* label) {
// scaling in any dimension is not allowed
assert_invariant(args.source.region.size.depth == args.destination.region.size.depth);
// we're always blitting a single plane
if (blitColor) {
if (args.source.color.sampleCount == args.destination.color.sampleCount &&
args.source.color.pixelFormat == args.destination.color.pixelFormat &&
MTLSizeEqual(args.source.region.size, args.destination.region.size)) {
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
blitEncoder.label = @(label);
[blitEncoder copyFromTexture:args.source.color
sourceSlice:args.source.slice
sourceLevel:args.source.level
sourceOrigin:args.source.region.origin
sourceSize:args.source.region.size
toTexture:args.destination.color
destinationSlice:args.destination.slice
destinationLevel:args.destination.level
destinationOrigin:args.destination.region.origin];
[blitEncoder endEncoding];
blitColor = false;
}
}
if (blitDepth) {
if (args.source.depth.sampleCount == args.destination.depth.sampleCount &&
args.source.depth.pixelFormat == args.destination.depth.pixelFormat &&
MTLSizeEqual(args.source.region.size, args.destination.region.size)) {
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
blitEncoder.label = @(label);
[blitEncoder copyFromTexture:args.source.depth
sourceSlice:args.source.slice
sourceLevel:args.source.level
sourceOrigin:args.source.region.origin
sourceSize:args.source.region.size
toTexture:args.destination.depth
destinationSlice:args.destination.slice
destinationLevel:args.destination.level
destinationOrigin:args.destination.region.origin];
[blitEncoder endEncoding];
blitDepth = false;
}
}
}
void MetalBlitter::blitSlowPath(id<MTLCommandBuffer> cmdBuffer, bool& blitColor, bool& blitDepth,
const BlitArgs& args, const char* label) {
uint32_t depthPlaneSource = args.source.region.origin.z;
uint32_t depthPlaneDest = args.destination.region.origin.z;
for (NSUInteger d = 0; d < args.source.region.size.depth; d++) {
blitDepthPlane(cmdBuffer, args,
depthPlaneSource++, depthPlaneDest++, label);
assert_invariant(args.source.region.size.depth == args.destination.region.size.depth);
uint32_t depthPlaneCount = args.source.region.size.depth;
for (NSUInteger d = 0; d < depthPlaneCount; d++) {
blitDepthPlane(cmdBuffer, blitColor, blitDepth, args, depthPlaneSource++, depthPlaneDest++,
label);
}
blitColor = false;
blitDepth = false;
}
void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, const BlitArgs& args,
uint32_t depthPlaneSource, uint32_t depthPlaneDest, const char* label) {
void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor, bool blitDepth,
const BlitArgs& args, uint32_t depthPlaneSource, uint32_t depthPlaneDest,
const char* label) {
MTLRenderPassDescriptor* descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
MTLRenderPassDescriptor* const descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
if (blitColor) {
setupColorAttachment(args, descriptor, depthPlaneDest);
}
setupAttachment(descriptor.colorAttachments[0], args, depthPlaneDest);
if (blitDepth) {
setupDepthAttachment(args, descriptor, depthPlaneDest);
}
id<MTLRenderCommandEncoder> const encoder =
[cmdBuffer renderCommandEncoderWithDescriptor:descriptor];
id<MTLRenderCommandEncoder> encoder = [cmdBuffer renderCommandEncoderWithDescriptor:descriptor];
encoder.label = @(label);
BlitFunctionKey key;
key.msaaColorSource = args.source.texture.textureType == MTLTextureType2DMultisample;
key.sources3D = args.source.texture.textureType == MTLTextureType3D;
id<MTLFunction> const fragmentFunction = getBlitFragmentFunction(key);
key.blitColor = blitColor;
key.blitDepth = blitDepth;
key.msaaColorSource = blitColor && args.source.color.textureType == MTLTextureType2DMultisample;
key.msaaDepthSource = blitDepth && args.source.depth.textureType == MTLTextureType2DMultisample;
key.sources3D = blitColor && args.source.color.textureType == MTLTextureType3D;
if (key.sources3D && blitDepth) {
assert_invariant(args.source.depth.textureType == MTLTextureType3D);
}
id<MTLFunction> fragmentFunction = getBlitFragmentFunction(key);
MetalPipelineState const pipelineState {
MetalPipelineState pipelineState {
.vertexFunction = getBlitVertexFunction(),
.fragmentFunction = fragmentFunction,
.vertexDescription = {},
.colorAttachmentPixelFormat = {
args.destination.texture.pixelFormat,
blitColor ? args.destination.color.pixelFormat : MTLPixelFormatInvalid,
MTLPixelFormatInvalid,
MTLPixelFormatInvalid,
MTLPixelFormatInvalid,
@@ -202,21 +319,31 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, const BlitArgs
MTLPixelFormatInvalid,
MTLPixelFormatInvalid
},
.depthAttachmentPixelFormat = MTLPixelFormatInvalid,
.depthAttachmentPixelFormat =
blitDepth ? args.destination.depth.pixelFormat : MTLPixelFormatInvalid,
.sampleCount = 1,
.blendState = {}
};
id<MTLRenderPipelineState> const pipeline =
mContext.pipelineStateCache.getOrCreateState(pipelineState);
id<MTLRenderPipelineState> pipeline = mContext.pipelineStateCache.getOrCreateState(pipelineState);
[encoder setRenderPipelineState:pipeline];
// For texture arrays, create a view of the texture at the given slice (layer).
id<MTLTexture> srcTextureColor = args.source.texture;
id<MTLTexture> srcTextureColor = args.source.color;
if (srcTextureColor && srcTextureColor.textureType == MTLTextureType2DArray) {
srcTextureColor = createTextureViewWithSingleSlice(srcTextureColor, args.source.slice);
}
[encoder setFragmentTexture:srcTextureColor atIndex:0];
id<MTLTexture> srcTextureDepth = args.source.depth;
if (srcTextureDepth && srcTextureDepth.textureType == MTLTextureType2DArray) {
srcTextureDepth = createTextureViewWithSingleSlice(srcTextureDepth, args.source.slice);
}
if (blitColor) {
[encoder setFragmentTexture:srcTextureColor atIndex:0];
}
if (blitDepth) {
[encoder setFragmentTexture:srcTextureDepth atIndex:1];
}
SamplerMinFilter filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST;
if (args.filter == SamplerMagFilter::NEAREST) {
@@ -225,13 +352,13 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, const BlitArgs
filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST;
}
SamplerState const s {
SamplerState s {
.samplerParams = {
.filterMag = args.filter,
.filterMin = filterMin
}
};
id<MTLSamplerState> const sampler = mContext.samplerStateCache.getOrCreateState(s);
id<MTLSamplerState> sampler = mContext.samplerStateCache.getOrCreateState(s);
[encoder setFragmentSamplerState:sampler atIndex:0];
MTLViewport viewport;
@@ -243,11 +370,11 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, const BlitArgs
viewport.zfar = 1.0;
[encoder setViewport:viewport];
DepthStencilState const depthStencilState {
DepthStencilState depthStencilState {
.depthCompare = MTLCompareFunctionAlways,
.depthWriteEnabled = false
.depthWriteEnabled = blitDepth
};
id<MTLDepthStencilState> const depthStencil =
id<MTLDepthStencilState> depthStencil =
mContext.depthStencilStateCache.getOrCreateState(depthStencilState);
[encoder setDepthStencilState:depthStencil];
@@ -283,42 +410,80 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, const BlitArgs
[encoder endEncoding];
}
id<MTLTexture> MetalBlitter::createIntermediateTexture(id<MTLTexture> t, MTLSize size) {
MTLTextureDescriptor* descriptor = [MTLTextureDescriptor new];
descriptor.textureType = size.depth == 1 ? MTLTextureType2D : MTLTextureType3D;
descriptor.pixelFormat = t.pixelFormat;
descriptor.width = size.width;
descriptor.height = size.height;
descriptor.depth = size.depth;
descriptor.usage = t.usage & MTLTextureUsageRenderTarget;
return [mContext.device newTextureWithDescriptor:descriptor];
}
void MetalBlitter::shutdown() noexcept {
mBlitFunctions.clear();
mVertexFunction = nil;
}
void MetalBlitter::setupAttachment(MTLRenderPassAttachmentDescriptor* descriptor,
const BlitArgs& args, uint32_t depthPlane) {
descriptor.texture = args.destination.texture;
descriptor.level = args.destination.level;
descriptor.slice = args.destination.slice;
descriptor.depthPlane = depthPlane;
descriptor.loadAction = MTLLoadActionLoad;
void MetalBlitter::setupColorAttachment(const BlitArgs& args,
MTLRenderPassDescriptor* descriptor, uint32_t depthPlane) {
descriptor.colorAttachments[0].texture = args.destination.color;
descriptor.colorAttachments[0].level = args.destination.level;
descriptor.colorAttachments[0].slice = args.destination.slice;
descriptor.colorAttachments[0].depthPlane = depthPlane;
descriptor.colorAttachments[0].loadAction = MTLLoadActionLoad;
// We don't need to load the contents of the attachment if we're blitting over all of it.
if (args.destinationIsFullAttachment()) {
descriptor.loadAction = MTLLoadActionDontCare;
if (args.colorDestinationIsFullAttachment()) {
descriptor.colorAttachments[0].loadAction = MTLLoadActionDontCare;
}
descriptor.storeAction = MTLStoreActionStore;
descriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
}
id<MTLFunction> MetalBlitter::compileFragmentFunction(BlitFunctionKey key) const {
MTLCompileOptions* const options = [MTLCompileOptions new];
NSMutableDictionary* const macros = [NSMutableDictionary dictionary];
void MetalBlitter::setupDepthAttachment(const BlitArgs& args, MTLRenderPassDescriptor* descriptor,
uint32_t depthPlane) {
descriptor.depthAttachment.texture = args.destination.depth;
descriptor.depthAttachment.level = args.destination.level;
descriptor.depthAttachment.slice = args.destination.slice;
descriptor.depthAttachment.depthPlane = depthPlane;
descriptor.depthAttachment.loadAction = MTLLoadActionLoad;
// We don't need to load the contents of the attachment if we're blitting over all of it.
if (args.depthDestinationIsFullAttachment()) {
descriptor.depthAttachment.loadAction = MTLLoadActionDontCare;
}
descriptor.depthAttachment.storeAction = MTLStoreActionStore;
}
id<MTLFunction> MetalBlitter::compileFragmentFunction(BlitFunctionKey key) {
MTLCompileOptions* options = [MTLCompileOptions new];
NSMutableDictionary* macros = [NSMutableDictionary dictionary];
if (key.blitColor) {
macros[@"BLIT_COLOR"] = @"1";
}
if (key.blitDepth) {
macros[@"BLIT_DEPTH"] = @"1";
}
if (key.msaaColorSource) {
macros[@"MSAA_COLOR_SOURCE"] = @"1";
}
if (key.msaaDepthSource) {
macros[@"MSAA_DEPTH_SOURCE"] = @"1";
}
if (key.sources3D) {
macros[@"SOURCES_3D"] = @"1";
}
options.preprocessorMacros = macros;
NSString* const objcSource = [NSString stringWithCString:functionLibrary
encoding:NSUTF8StringEncoding];
NSString* objcSource = [NSString stringWithCString:functionLibrary
encoding:NSUTF8StringEncoding];
NSError* error = nil;
id <MTLLibrary> const library = [mContext.device newLibraryWithSource:objcSource
options:options
error:&error];
id <MTLFunction> const function = [library newFunctionWithName:@"blitterFrag"];
id<MTLLibrary> library = [mContext.device newLibraryWithSource:objcSource
options:options
error:&error];
id<MTLFunction> function = [library newFunctionWithName:@"blitterFrag"];
if (!library || !function) {
if (error) {
@@ -336,14 +501,13 @@ id<MTLFunction> MetalBlitter::getBlitVertexFunction() {
return mVertexFunction;
}
NSString* const objcSource = [NSString stringWithCString:functionLibrary
encoding:NSUTF8StringEncoding];
NSString* objcSource = [NSString stringWithCString:functionLibrary
encoding:NSUTF8StringEncoding];
NSError* error = nil;
id <MTLLibrary> const library = [mContext.device newLibraryWithSource:objcSource
options:nil
error:&error];
id<MTLFunction> const function = [library newFunctionWithName:@"blitterVertex"];
id<MTLLibrary> library = [mContext.device newLibraryWithSource:objcSource
options:nil
error:&error];
id<MTLFunction> function = [library newFunctionWithName:@"blitterVertex"];
if (!library || !function) {
if (error) {
@@ -371,5 +535,5 @@ id<MTLFunction> MetalBlitter::getBlitFragmentFunction(BlitFunctionKey key) {
return function;
}
} // namespace filament::backend
} // namespace backend
} // namespace filament

View File

@@ -73,10 +73,7 @@ private:
std::unordered_set<MetalBufferPoolEntry const*> mUsedStages;
// Store the current "time" (really just a frame count) and LRU eviction parameters.
// An atomic is necessary as mCurrentFrame is incremented in gc() (called on
// the driver thread) and read from acquireBuffer() and releaseBuffer(),
// which may be called on non-driver threads.
std::atomic<uint64_t> mCurrentFrame = 0;
uint64_t mCurrentFrame = 0;
static constexpr uint32_t TIME_BEFORE_EVICTION = 10;
};

View File

@@ -18,15 +18,12 @@
#define TNT_METALCONTEXT_H
#include "MetalResourceTracker.h"
#include "MetalShaderCompiler.h"
#include "MetalState.h"
#include <CoreVideo/CVMetalTextureCache.h>
#include <Metal/Metal.h>
#include <QuartzCore/QuartzCore.h>
#include <utils/FixedCircularBuffer.h>
#include <array>
#include <atomic>
#include <stack>
@@ -56,9 +53,6 @@ struct MetalVertexBuffer;
constexpr static uint8_t MAX_SAMPLE_COUNT = 8; // Metal devices support at most 8 MSAA samples
struct MetalContext {
explicit MetalContext(size_t metalFreedTextureListSize)
: texturesToDestroy(metalFreedTextureListSize) {}
MetalDriver* driver;
id<MTLDevice> device = nullptr;
id<MTLCommandQueue> commandQueue = nullptr;
@@ -117,14 +111,6 @@ struct MetalContext {
tsl::robin_set<MetalSamplerGroup*> samplerGroups;
tsl::robin_set<MetalTexture*> textures;
// This circular buffer implements delayed destruction for Metal texture handles. It keeps a
// handle to a fixed number of the most recently destroyed texture handles. When we're asked to
// destroy a texture handle, we free its texture memory, but keep the MetalTexture object alive,
// marking it as "terminated". If we later are asked to use that texture, we can check its
// terminated status and throw an Objective-C error instead of crashing, which is helpful for
// debugging use-after-free issues in release builds.
utils::FixedCircularBuffer<Handle<HwTexture>> texturesToDestroy;
MetalBufferPool* bufferPool;
MetalSwapChain* currentDrawSwapChain = nil;
@@ -142,10 +128,7 @@ struct MetalContext {
// Fences, only supported on macOS 10.14 and iOS 12 and above.
API_AVAILABLE(macos(10.14), ios(12.0))
MTLSharedEventListener* eventListener = nil;
// signalId is incremented in the MetalFence constructor, which is called on
// both the driver (MetalTimerQueryFence::beginTimeElapsedQuery) and main
// threads (in createFenceS), so an atomic is necessary.
std::atomic<uint64_t> signalId = 1;
uint64_t signalId = 1;
MetalTimerQueryInterface* timerQueryImpl;
@@ -153,8 +136,6 @@ struct MetalContext {
MTLViewport currentViewport;
MetalShaderCompiler* shaderCompiler = nullptr;
#if defined(FILAMENT_METAL_PROFILING)
// Logging and profiling.
os_log_t log;

View File

@@ -28,21 +28,17 @@
#include <utils/Log.h>
#include <utils/debug.h>
#include <functional>
#include <mutex>
#include <vector>
namespace filament {
namespace backend {
class MetalPlatform;
class MetalBuffer;
class MetalProgram;
class MetalSamplerGroup;
class MetalTexture;
struct MetalUniformBuffer;
struct MetalContext;
struct MetalProgram;
struct BufferState;
#ifndef FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB
@@ -56,13 +52,13 @@ class MetalDriver final : public DriverBase {
public:
static Driver* create(MetalPlatform* platform, const Platform::DriverConfig& driverConfig);
void runAtNextTick(const std::function<void()>& fn) noexcept;
private:
friend class MetalSwapChain;
MetalPlatform& mPlatform;
MetalContext* mContext;
ShaderModel getShaderModel() const noexcept final;
@@ -70,13 +66,6 @@ private:
// Overrides the default implementation by wrapping the call to fn in an @autoreleasepool block.
void execute(std::function<void(void)> const& fn) noexcept final;
/*
* Tasks run regularly on the driver thread.
*/
void executeTickOps() noexcept;
std::vector<std::function<void()>> mTickOps;
std::mutex mTickOpsLock;
/*
* Driver interface
*/

View File

@@ -50,8 +50,7 @@ UTILS_NOINLINE
Driver* MetalDriver::create(MetalPlatform* const platform, const Platform::DriverConfig& driverConfig) {
assert_invariant(platform);
size_t defaultSize = FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig {driverConfig};
validConfig.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize);
Platform::DriverConfig validConfig { .handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize) };
return new MetalDriver(platform, validConfig);
}
@@ -61,7 +60,7 @@ Dispatcher MetalDriver::getDispatcher() const noexcept {
MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept
: mPlatform(*platform),
mContext(new MetalContext(driverConfig.textureUseAfterFreePoolSize)),
mContext(new MetalContext),
mHandleAllocator("Handles", driverConfig.handleArenaSize) {
mContext->driver = this;
@@ -144,9 +143,6 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
mContext->eventListener = [[MTLSharedEventListener alloc] initWithDispatchQueue:queue];
}
mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this);
mContext->shaderCompiler->init();
#if defined(FILAMENT_METAL_PROFILING)
mContext->log = os_log_create("com.google.filament", "Metal");
mContext->signpostId = os_signpost_id_generate(mContext->log);
@@ -161,12 +157,10 @@ MetalDriver::~MetalDriver() noexcept {
delete mContext->bufferPool;
delete mContext->blitter;
delete mContext->timerQueryImpl;
delete mContext->shaderCompiler;
delete mContext;
}
void MetalDriver::tick(int) {
executeTickOps();
}
void MetalDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
@@ -309,9 +303,8 @@ void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
target, levels, format, samples, width, height, depth, usage, metalTexture));
}
void MetalDriver::createSamplerGroupR(
Handle<HwSamplerGroup> sbh, uint32_t size, utils::FixedSizeString<32> debugName) {
mContext->samplerGroups.insert(construct_handle<MetalSamplerGroup>(sbh, size, debugName));
void MetalDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t size) {
mContext->samplerGroups.insert(construct_handle<MetalSamplerGroup>(sbh, size));
}
void MetalDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
@@ -324,7 +317,7 @@ void MetalDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
}
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program) {
construct_handle<MetalProgram>(rph, *mContext, std::move(program));
construct_handle<MetalProgram>(rph, mContext->device, program);
}
void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int dummy) {
@@ -352,7 +345,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
auto colorTexture = handle_cast<MetalTexture>(buffer.handle);
ASSERT_PRECONDITION(colorTexture->getMtlTextureForWrite(),
"Color texture passed to render target has no texture allocation");
colorTexture->extendLodRangeTo(buffer.level);
colorTexture->updateLodRange(buffer.level);
colorAttachments[i] = { colorTexture, color[i].level, color[i].layer };
}
@@ -363,7 +356,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
auto depthTexture = handle_cast<MetalTexture>(depth.handle);
ASSERT_PRECONDITION(depthTexture->getMtlTextureForWrite(),
"Depth texture passed to render target has no texture allocation.");
depthTexture->extendLodRangeTo(depth.level);
depthTexture->updateLodRange(depth.level);
depthAttachment = { depthTexture, depth.level, depth.layer };
}
@@ -374,7 +367,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
auto stencilTexture = handle_cast<MetalTexture>(stencil.handle);
ASSERT_PRECONDITION(stencilTexture->getMtlTextureForWrite(),
"Stencil texture passed to render target has no texture allocation.");
stencilTexture->extendLodRangeTo(stencil.level);
stencilTexture->updateLodRange(stencil.level);
stencilAttachment = { stencilTexture, stencil.level, stencil.layer };
}
@@ -537,18 +530,8 @@ void MetalDriver::destroyTexture(Handle<HwTexture> th) {
return;
}
auto* metalTexture = handle_cast<MetalTexture>(th);
mContext->textures.erase(metalTexture);
// Free memory from the texture and mark it as freed.
metalTexture->terminate();
// Add this texture handle to our texturesToDestroy queue to be destroyed later.
if (auto handleToFree = mContext->texturesToDestroy.push(th)) {
// If texturesToDestroy is full, then .push evicts the oldest texture handle in the
// queue (or simply th, if use-after-free detection is disabled).
destruct_handle<MetalTexture>(handleToFree.value());
}
mContext->textures.erase(handle_cast<MetalTexture>(th));
destruct_handle<MetalTexture>(th);
}
void MetalDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
@@ -574,24 +557,15 @@ void MetalDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
}
void MetalDriver::terminate() {
// Terminate any outstanding MetalTextures.
while (!mContext->texturesToDestroy.empty()) {
Handle<HwTexture> toDestroy = mContext->texturesToDestroy.pop();
destruct_handle<MetalTexture>(toDestroy);
}
// finish() will flush the pending command buffer and will ensure all GPU work has finished.
// This must be done before calling bufferPool->reset() to ensure no buffers are in flight.
finish();
executeTickOps();
mContext->bufferPool->reset();
mContext->commandQueue = nil;
MetalExternalImage::shutdown(*mContext);
mContext->blitter->shutdown();
mContext->shaderCompiler->terminate();
}
ShaderModel MetalDriver::getShaderModel() const noexcept {
@@ -727,10 +701,6 @@ bool MetalDriver::isStereoSupported() {
}
bool MetalDriver::isParallelShaderCompileSupported() {
return true;
}
bool MetalDriver::isDepthStencilResolveSupported() {
return false;
}
@@ -819,8 +789,6 @@ void MetalDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t ind
}
void MetalDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint32_t maxLevel) {
auto tex = handle_cast<MetalTexture>(th);
tex->setLodRange(minLevel, maxLevel);
}
void MetalDriver::update3DImage(Handle<HwTexture> th, uint32_t level,
@@ -872,6 +840,10 @@ void MetalDriver::generateMipmaps(Handle<HwTexture> th) {
tex->generateMipmaps();
}
bool MetalDriver::canGenerateMipmaps() {
return true;
}
void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescriptor&& data) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"updateSamplerGroup must be called outside of a render pass.");
@@ -880,17 +852,13 @@ void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescripto
assert_invariant(sb->size == data.size / sizeof(SamplerDescriptor));
auto const* const samplers = (SamplerDescriptor const*) data.buffer;
// Verify that all the textures in the sampler group are still alive.
#ifndef NDEBUG
// In debug builds, verify that all the textures in the sampler group are still alive.
// These bugs lead to memory corruption and can be difficult to track down.
for (size_t s = 0; s < data.size / sizeof(SamplerDescriptor); s++) {
if (!samplers[s].t) {
continue;
}
// The difference between this check and the one below is that in release, we do this for
// only a set number of recently freed textures, while the debug check is exhaustive.
auto* metalTexture = handle_cast<MetalTexture>(samplers[s].t);
metalTexture->checkUseAfterFree(sb->debugName.c_str(), s);
#ifndef NDEBUG
auto iter = mContext->textures.find(handle_cast<MetalTexture>(samplers[s].t));
if (iter == mContext->textures.end()) {
utils::slog.e << "updateSamplerGroup: texture #"
@@ -898,8 +866,8 @@ void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescripto
<< samplers[s].t << utils::io::endl;
}
assert_invariant(iter != mContext->textures.end());
#endif
}
#endif
// Create a MTLArgumentEncoder for these textures.
// Ideally, we would create this encoder at createSamplerGroup time, but we need to know the
@@ -932,24 +900,29 @@ void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescripto
// 2. LOD-clamped textures
//
// Both of these cases prevent us from knowing the final id<MTLTexture> that will be bound into
// the argument buffer representing the sampler group. So, we wait until draw call time to bind
// textures (done in finalizeSamplerGroup).
// the argument buffer representing the sampler group. So, we bind what we can now and wait
// until draw call time to bind any special cases (done in finalizeSamplerGroup).
// The good news is that once a render pass has started, the texture bindings won't change.
// A SamplerGroup is "finalized" when all of its textures have been set and is ready for use in
// a draw call.
// finalizeSamplerGroup has one additional responsibility: to call useResources for all the
// textures, which is required by Metal.
// Even if we do know all the final textures at this point, we still wait until draw call time
// to call finalizeSamplerGroup, which has one additional responsibility: to call useResources
// for all the textures, which is required by Metal.
for (size_t s = 0; s < data.size / sizeof(SamplerDescriptor); s++) {
if (!samplers[s].t) {
// Assign a default sampler to empty slots.
// Assign a default texture / sampler to empty slots.
// Metal requires all samplers referenced in shaders to be bound.
// An empty texture will be assigned inside finalizeSamplerGroup.
id<MTLTexture> empty = getOrCreateEmptyTexture(mContext);
sb->setFinalizedTexture(s, empty);
id<MTLSamplerState> sampler = mContext->samplerStateCache.getOrCreateState({});
sb->setFinalizedSampler(s, sampler);
continue;
}
// Bind the sampler state. We always know the full sampler state at updateSamplerGroup time.
// First, bind the sampler state. We always know the full sampler state at
// updateSamplerGroup time.
SamplerState samplerState {
.samplerParams = samplers[s].s,
};
@@ -957,6 +930,27 @@ void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescripto
sb->setFinalizedSampler(s, sampler);
sb->setTextureHandle(s, samplers[s].t);
auto* t = handle_cast<MetalTexture>(samplers[s].t);
assert_invariant(t);
// If this texture is an external texture, we defer binding the texture until draw call time
// (in finalizeSamplerGroup).
if (t->target == SamplerType::SAMPLER_EXTERNAL) {
continue;
}
if (!t->allLodsValid()) {
// The texture doesn't have all of its LODs loaded, and this could change by the time we
// issue a draw call with this sampler group. So, we defer binding the texture until
// draw call time (in finalizeSamplerGroup).
continue;
}
// If we get here, we know we have a valid MTLTexture that's guaranteed not to change.
id<MTLTexture> mtlTexture = t->getMtlTextureForRead();
assert_invariant(mtlTexture);
sb->setFinalizedTexture(s, mtlTexture);
}
scheduleDestroy(std::move(data));
@@ -965,7 +959,7 @@ void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescripto
void MetalDriver::compilePrograms(CompilerPriorityQueue priority,
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
if (callback) {
mContext->shaderCompiler->notifyWhenAllProgramsAreReady(handler, callback, user);
scheduleCallback(handler, user, callback);
}
}
@@ -1259,14 +1253,14 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
textureDescriptor.usage = MTLTextureUsageShaderWrite | MTLTextureUsageRenderTarget;
id<MTLTexture> readPixelsTexture = [mContext->device newTextureWithDescriptor:textureDescriptor];
MetalBlitter::BlitArgs args{};
MetalBlitter::BlitArgs args;
args.filter = SamplerMagFilter::NEAREST;
args.source.level = miplevel;
args.source.region = MTLRegionMake2D(0, 0, srcTexture.width >> miplevel, srcTexture.height >> miplevel);
args.source.texture = srcTexture;
args.destination.level = 0;
args.destination.region = MTLRegionMake2D(0, 0, readPixelsTexture.width, readPixelsTexture.height);
args.destination.texture = readPixelsTexture;
args.source.color = srcTexture;
args.destination.color = readPixelsTexture;
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "readPixels blit");
@@ -1302,123 +1296,24 @@ void MetalDriver::readBufferSubData(backend::BufferObjectHandle boh,
scheduleDestroy(std::move(p));
}
void MetalDriver::resolve(
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer,
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
auto* const srcTexture = handle_cast<MetalTexture>(src);
auto* const dstTexture = handle_cast<MetalTexture>(dst);
assert_invariant(srcTexture);
assert_invariant(dstTexture);
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
"resolve() cannot be invoked inside a render pass.");
ASSERT_PRECONDITION(
dstTexture->width == srcTexture->width && dstTexture->height == srcTexture->height,
"invalid resolve: src and dst sizes don't match");
ASSERT_PRECONDITION(srcTexture->samples > 1 && dstTexture->samples == 1,
"invalid resolve: src.samples=%u, dst.samples=%u",
+srcTexture->samples, +dstTexture->samples);
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
"src and dst texture format don't match");
ASSERT_PRECONDITION(!isDepthFormat(srcTexture->format),
"can't resolve depth formats");
ASSERT_PRECONDITION(!isStencilFormat(srcTexture->format),
"can't resolve stencil formats");
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
"texture doesn't have BLIT_DST");
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
"texture doesn't have BLIT_SRC");
// FIXME: on metal the blit() call below always take the slow path (using a shader)
blit( dst, dstLevel, dstLayer, {},
src, srcLevel, srcLayer, {},
{ dstTexture->width, dstTexture->height });
}
void MetalDriver::blit(
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer, math::uint2 dstOrigin,
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer, math::uint2 srcOrigin,
math::uint2 size) {
auto isBlitableTextureType = [](MTLTextureType t) -> bool {
return t == MTLTextureType2D || t == MTLTextureType2DMultisample ||
t == MTLTextureType2DArray;
};
auto* const srcTexture = handle_cast<MetalTexture>(src);
auto* const dstTexture = handle_cast<MetalTexture>(dst);
assert_invariant(srcTexture);
assert_invariant(dstTexture);
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
"blit() cannot be invoked inside a render pass.");
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
"texture doesn't have BLIT_DST");
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
"texture doesn't have BLIT_SRC");
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
"src and dst texture format don't match");
ASSERT_PRECONDITION(isBlitableTextureType(srcTexture->getMtlTextureForRead().textureType) &&
isBlitableTextureType(dstTexture->getMtlTextureForWrite().textureType),
"Metal does not support blitting to/from non-2D textures.");
MetalBlitter::BlitArgs args{};
args.filter = SamplerMagFilter::NEAREST;
args.source.region = MTLRegionMake2D(
(NSUInteger)srcOrigin.x,
std::max(srcTexture->height - (int64_t)srcOrigin.y - size.y, (int64_t)0),
size.x, size.y);
args.destination.region = MTLRegionMake2D(
(NSUInteger)dstOrigin.x,
std::max(dstTexture->height - (int64_t)dstOrigin.y - size.y, (int64_t)0),
size.x, size.y);
// FIXME: we shouldn't need to know the type here. This is an artifact of using the old API.
args.source.texture = srcTexture->getMtlTextureForRead();
args.destination.texture = dstTexture->getMtlTextureForWrite();
args.source.level = srcLevel;
args.source.slice = srcLayer;
args.destination.level = dstLevel;
args.destination.slice = dstLayer;
// TODO: The blit() call below always take the fast path.
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "blit/resolve");
dstTexture->extendLodRangeTo(dstLevel);
}
void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers,
void MetalDriver::blit(TargetBufferFlags buffers,
Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect,
SamplerMagFilter filter) {
// Note: blitDEPRECATED is only used by Renderer::copyFrame
// It is called between beginFrame and endFrame, but should never be called in the middle of
// a render pass.
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
"blitDEPRECATED() cannot be invoked inside a render pass.");
// If we're the in middle of a render pass, finish it.
// This condition should only occur during copyFrame. It's okay to end the render pass because
// we don't issue any other rendering commands.
if (mContext->currentRenderPassEncoder) {
[mContext->currentRenderPassEncoder endEncoding];
mContext->currentRenderPassEncoder = nil;
}
auto srcTarget = handle_cast<MetalRenderTarget>(src);
auto dstTarget = handle_cast<MetalRenderTarget>(dst);
ASSERT_PRECONDITION(buffers == TargetBufferFlags::COLOR0,
"blitDEPRECATED only supports COLOR0");
ASSERT_PRECONDITION(
!(buffers & (TargetBufferFlags::COLOR_ALL & ~TargetBufferFlags::COLOR0)),
"Blitting only supports COLOR0");
ASSERT_PRECONDITION(srcRect.left >= 0 && srcRect.bottom >= 0 &&
dstRect.left >= 0 && dstRect.bottom >= 0,
@@ -1429,91 +1324,103 @@ void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers,
t == MTLTextureType2DArray;
};
// We always blit from/to the COLOR0 attachment.
MetalRenderTarget::Attachment const srcColorAttachment = srcTarget->getReadColorAttachment(0);
MetalRenderTarget::Attachment const dstColorAttachment = dstTarget->getDrawColorAttachment(0);
// MetalBlitter supports blitting color and depth simultaneously, but for simplicitly we'll blit
// them separately. In practice, Filament only ever blits a single buffer at a time anyway.
if (srcColorAttachment && dstColorAttachment) {
ASSERT_PRECONDITION(isBlitableTextureType(srcColorAttachment.getTexture().textureType) &&
isBlitableTextureType(dstColorAttachment.getTexture().textureType),
"Metal does not support blitting to/from non-2D textures.");
if (any(buffers & TargetBufferFlags::COLOR_ALL)) {
// We always blit from/to the COLOR0 attachment.
MetalRenderTarget::Attachment srcColorAttachment = srcTarget->getReadColorAttachment(0);
MetalRenderTarget::Attachment dstColorAttachment = dstTarget->getDrawColorAttachment(0);
MetalBlitter::BlitArgs args{};
args.filter = filter;
args.source.region = srcTarget->getRegionFromClientRect(srcRect);
args.source.texture = srcColorAttachment.getTexture();
args.source.level = srcColorAttachment.level;
args.source.slice = srcColorAttachment.layer;
if (srcColorAttachment && dstColorAttachment) {
ASSERT_PRECONDITION(isBlitableTextureType(srcColorAttachment.getTexture().textureType) &&
isBlitableTextureType(dstColorAttachment.getTexture().textureType),
"Metal does not support blitting to/from non-2D textures.");
args.destination.region = dstTarget->getRegionFromClientRect(dstRect);
args.destination.texture = dstColorAttachment.getTexture();
args.destination.level = dstColorAttachment.level;
args.destination.slice = dstColorAttachment.layer;
MetalBlitter::BlitArgs args;
args.filter = filter;
args.source.region = srcTarget->getRegionFromClientRect(srcRect);
args.destination.region = dstTarget->getRegionFromClientRect(dstRect);
args.source.color = srcColorAttachment.getTexture();
args.destination.color = dstColorAttachment.getTexture();
args.source.level = srcColorAttachment.level;
args.destination.level = dstColorAttachment.level;
args.source.slice = srcColorAttachment.layer;
args.destination.slice = dstColorAttachment.layer;
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "Color blit");
}
}
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "blitDEPRECATED");
if (any(buffers & TargetBufferFlags::DEPTH)) {
MetalRenderTarget::Attachment srcDepthAttachment = srcTarget->getDepthAttachment();
MetalRenderTarget::Attachment dstDepthAttachment = dstTarget->getDepthAttachment();
if (srcDepthAttachment && dstDepthAttachment) {
ASSERT_PRECONDITION(isBlitableTextureType(srcDepthAttachment.getTexture().textureType) &&
isBlitableTextureType(dstDepthAttachment.getTexture().textureType),
"Metal does not support blitting to/from non-2D textures.");
MetalBlitter::BlitArgs args;
args.filter = filter;
args.source.region = srcTarget->getRegionFromClientRect(srcRect);
args.destination.region = dstTarget->getRegionFromClientRect(dstRect);
args.source.depth = srcDepthAttachment.getTexture();
args.destination.depth = dstDepthAttachment.getTexture();
args.source.level = srcDepthAttachment.level;
args.destination.level = dstDepthAttachment.level;
args.source.slice = srcDepthAttachment.layer;
args.destination.slice = dstDepthAttachment.layer;
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "Depth blit");
}
}
}
void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
// All the id<MTLSamplerState> objects have already been bound to the argument buffer.
// Here we bind all the textures.
// All of the id<MTLSamplerState> objects have already been bound to the argument buffer.
// Here we bind any textures that were unable to be bound in updateSamplerGroup.
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(mContext);
// Verify that all the textures in the sampler group are still alive.
#ifndef NDEBUG
// In debug builds, verify that all the textures in the sampler group are still alive.
// These bugs lead to memory corruption and can be difficult to track down.
const auto& handles = samplerGroup->getTextureHandles();
for (size_t s = 0; s < handles.size(); s++) {
if (!handles[s]) {
continue;
}
// The difference between this check and the one below is that in release, we do this for
// only a set number of recently freed textures, while the debug check is exhaustive.
auto* metalTexture = handle_cast<MetalTexture>(handles[s]);
metalTexture->checkUseAfterFree(samplerGroup->debugName.c_str(), s);
#ifndef NDEBUG
auto iter = mContext->textures.find(metalTexture);
auto iter = mContext->textures.find(handle_cast<MetalTexture>(handles[s]));
if (iter == mContext->textures.end()) {
utils::slog.e << "finalizeSamplerGroup: texture #"
<< (int) s << " is dead, texture handle = "
<< handles[s] << utils::io::endl;
}
assert_invariant(iter != mContext->textures.end());
#endif
}
#endif
utils::FixedCapacityVector<id<MTLTexture>> newTextures(samplerGroup->size, nil);
for (size_t binding = 0; binding < samplerGroup->size; binding++) {
auto [th, _] = samplerGroup->getFinalizedTexture(binding);
auto [th, t] = samplerGroup->getFinalizedTexture(binding);
if (!th) {
// Bind an empty texture.
newTextures[binding] = getOrCreateEmptyTexture(mContext);
// This may be an external texture, in which case we can't cache the id<MTLTexture>, we
// need to refetch it in case the external image has changed.
bool isExternalImage = false;
if (th) {
auto* texture = handle_cast<MetalTexture>(th);
isExternalImage = texture->target == SamplerType::SAMPLER_EXTERNAL;
}
// If t is non-nil, then we've already finalized this texture.
if (t && !isExternalImage) {
continue;
}
// It's possible that some texture handles are null, but we should have already handled
// these inside updateSamplerGroup by binding an "empty" texture.
assert_invariant(th);
auto* texture = handle_cast<MetalTexture>(th);
// External images
if (texture->target == SamplerType::SAMPLER_EXTERNAL) {
if (texture->externalImage.isValid()) {
id<MTLTexture> mtlTexture = texture->externalImage.getMetalTextureForDraw();
assert_invariant(mtlTexture);
newTextures[binding] = mtlTexture;
} else {
// Bind an empty texture.
newTextures[binding] = getOrCreateEmptyTexture(mContext);
}
continue;
}
newTextures[binding] = texture->getMtlTextureForRead();
}
if (!std::equal(newTextures.begin(), newTextures.end(), samplerGroup->textures.begin())) {
// One or more of the id<MTLTexture>s has changed.
// First, determine if this SamplerGroup needs mutation.
// Determine if this SamplerGroup needs mutation.
// We can't just simply mutate the SamplerGroup, since it could currently be in use by the
// GPU from a prior render pass.
// If the SamplerGroup does need mutation, then there's two cases:
@@ -1521,18 +1428,30 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
// draw call). We're free to mutate it.
// 2. The SamplerGroup is finalized. We must call mutate(), which will create a new argument
// buffer that we can then mutate freely.
// TODO: don't just always call mutate, check to see if the texture is actually different.
if (samplerGroup->isFinalized()) {
samplerGroup->mutate(cmdBuffer);
}
for (size_t binding = 0; binding < samplerGroup->size; binding++) {
samplerGroup->setFinalizedTexture(binding, newTextures[binding]);
// External images
if (texture->target == SamplerType::SAMPLER_EXTERNAL) {
if (texture->externalImage.isValid()) {
id<MTLTexture> mtlTexture = texture->externalImage.getMetalTextureForDraw();
assert_invariant(mtlTexture);
samplerGroup->setFinalizedTexture(binding, mtlTexture);
} else {
// Bind an empty texture.
samplerGroup->setFinalizedTexture(binding, getOrCreateEmptyTexture(mContext));
}
continue;
}
samplerGroup->finalize();
samplerGroup->setFinalizedTexture(binding, texture->getMtlTextureForRead());
}
samplerGroup->finalize();
// At this point, all the id<MTLTextures> should be set to valid textures. Some of them will be
// the "empty" texture. Per Apple documentation, the useResource method must be called once per
// render pass.
@@ -1554,19 +1473,14 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
auto program = handle_cast<MetalProgram>(ps.program);
const auto& rs = ps.rasterState;
// This might block until the shader compilation has finished.
auto functions = program->getFunctions();
// If the material debugger is enabled, avoid fatal (or cascading) errors and that can occur
// during the draw call when the program is invalid. The shader compile error has already been
// dumped to the console at this point, so it's fine to simply return early.
if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!functions)) {
if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!program->isValid)) {
return;
}
ASSERT_PRECONDITION(bool(functions), "Attempting to draw with an invalid Metal program.");
auto [fragment, vertex] = functions.getRasterFunctions();
ASSERT_PRECONDITION(program->isValid, "Attempting to draw with an invalid Metal program.");
// Pipeline state
MTLPixelFormat colorPixelFormat[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = { MTLPixelFormatInvalid };
@@ -1588,9 +1502,9 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
stencilPixelFormat = stencilAttachment.getPixelFormat();
assert_invariant(isMetalFormatStencil(stencilPixelFormat));
}
MetalPipelineState const pipelineState {
.vertexFunction = vertex,
.fragmentFunction = fragment,
MetalPipelineState pipelineState {
.vertexFunction = program->vertexFunction,
.fragmentFunction = program->fragmentFunction,
.vertexDescription = primitive->vertexDescription,
.colorAttachmentPixelFormat = {
colorPixelFormat[0],
@@ -1606,13 +1520,13 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
.stencilAttachmentPixelFormat = stencilPixelFormat,
.sampleCount = mContext->currentRenderTarget->getSamples(),
.blendState = BlendState {
.alphaBlendOperation = getMetalBlendOperation(rs.blendEquationAlpha),
.rgbBlendOperation = getMetalBlendOperation(rs.blendEquationRGB),
.destinationAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionDstAlpha),
.destinationRGBBlendFactor = getMetalBlendFactor(rs.blendFunctionDstRGB),
.sourceAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcAlpha),
.sourceRGBBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcRGB),
.blendingEnabled = rs.hasBlending(),
.rgbBlendOperation = getMetalBlendOperation(rs.blendEquationRGB),
.alphaBlendOperation = getMetalBlendOperation(rs.blendEquationAlpha),
.sourceRGBBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcRGB),
.sourceAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcAlpha),
.destinationRGBBlendFactor = getMetalBlendFactor(rs.blendFunctionDstRGB),
.destinationAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionDstAlpha)
},
.colorWrite = rs.colorWrite
};
@@ -1735,7 +1649,7 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
if (!samplerGroup) {
continue;
}
const auto& stageFlags = program->getSamplerGroupInfo()[s].stageFlags;
const auto& stageFlags = program->samplerGroupInfo[s].stageFlags;
if (stageFlags == ShaderStageFlags::NONE) {
continue;
}
@@ -1762,23 +1676,26 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
// Bind the user vertex buffers.
MetalBuffer* vertexBuffers[MAX_VERTEX_BUFFER_COUNT] = {};
MetalBuffer* buffers[MAX_VERTEX_BUFFER_COUNT] = {};
size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT] = {};
size_t maxBufferIndex = 0;
size_t bufferIndex = 0;
auto vb = primitive->vertexBuffer;
for (auto m : primitive->bufferMapping) {
assert_invariant(
m.bufferArgumentIndex >= USER_VERTEX_BUFFER_BINDING_START &&
m.bufferArgumentIndex < USER_VERTEX_BUFFER_BINDING_START + MAX_VERTEX_BUFFER_COUNT);
size_t vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START;
vertexBuffers[vertexBufferIndex] = vb->buffers[m.sourceBufferIndex];
maxBufferIndex = std::max(maxBufferIndex, vertexBufferIndex);
for (uint32_t attributeIndex = 0; attributeIndex < vb->attributes.size(); attributeIndex++) {
const auto& attribute = vb->attributes[attributeIndex];
if (attribute.buffer == Attribute::BUFFER_UNUSED) {
continue;
}
assert_invariant(vb->buffers[attribute.buffer]);
buffers[bufferIndex] = vb->buffers[attribute.buffer];
vertexBufferOffsets[bufferIndex] = attribute.offset;
bufferIndex++;
}
const auto bufferCount = maxBufferIndex + 1;
const auto bufferCount = bufferIndex;
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
USER_VERTEX_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX, vertexBuffers,
USER_VERTEX_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX, buffers,
vertexBufferOffsets, bufferCount);
// Bind the zero buffer, used for missing vertex attributes.
@@ -1805,26 +1722,21 @@ void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGro
auto mtlProgram = handle_cast<MetalProgram>(program);
// This might block until the shader compilation has finished.
auto functions = mtlProgram->getFunctions();
// If the material debugger is enabled, avoid fatal (or cascading) errors and that can occur
// during the draw call when the program is invalid. The shader compile error has already been
// dumped to the console at this point, so it's fine to simply return early.
if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!functions)) {
if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!mtlProgram->isValid)) {
return;
}
auto compute = functions.getComputeFunction();
assert_invariant(bool(functions) && compute);
assert_invariant(mtlProgram->isValid && mtlProgram->computeFunction);
id<MTLComputeCommandEncoder> computeEncoder =
[getPendingCommandBuffer(mContext) computeCommandEncoder];
NSError* error = nil;
id<MTLComputePipelineState> computePipelineState =
[mContext->device newComputePipelineStateWithFunction:compute
[mContext->device newComputePipelineStateWithFunction:mtlProgram->computeFunction
error:&error];
if (error) {
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
@@ -1911,21 +1823,6 @@ void MetalDriver::enumerateBoundBuffers(BufferObjectBinding bindingType,
void MetalDriver::resetState(int) {
}
void MetalDriver::runAtNextTick(const std::function<void()>& fn) noexcept {
std::lock_guard<std::mutex> const lock(mTickOpsLock);
mTickOps.push_back(fn);
}
void MetalDriver::executeTickOps() noexcept {
std::vector<std::function<void()>> ops;
mTickOpsLock.lock();
std::swap(ops, mTickOps);
mTickOpsLock.unlock();
for (const auto& f : ops) {
f();
}
}
// explicit instantiation of the Dispatcher
template class ConcreteDispatcher<MetalDriver>;

View File

@@ -32,7 +32,6 @@
#include "private/backend/SamplerGroup.h"
#include <utils/bitset.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
@@ -67,7 +66,6 @@ public:
id<MTLTexture> acquireDrawable();
id<MTLTexture> acquireDepthTexture();
id<MTLTexture> acquireStencilTexture();
void releaseDrawable();
@@ -96,16 +94,12 @@ private:
void scheduleFrameScheduledCallback();
void scheduleFrameCompletedCallback();
static MTLPixelFormat decideDepthStencilFormat(uint64_t flags);
void ensureDepthStencilTexture();
MetalContext& context;
id<CAMetalDrawable> drawable = nil;
id<MTLTexture> depthStencilTexture = nil;
id<MTLTexture> depthTexture = nil;
id<MTLTexture> headlessDrawable = nil;
MTLPixelFormat depthStencilFormat = MTLPixelFormatInvalid;
NSUInteger headlessWidth = 0;
NSUInteger headlessHeight = 0;
NSUInteger headlessWidth;
NSUInteger headlessHeight;
CAMetalLayer* layer = nullptr;
MetalExternalImage externalImage;
SwapChainType type;
@@ -160,7 +154,6 @@ struct MetalIndexBuffer : public HwIndexBuffer {
};
struct MetalRenderPrimitive : public HwRenderPrimitive {
MetalRenderPrimitive();
void setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
// The pointers to MetalVertexBuffer and MetalIndexBuffer are "weak".
// The MetalVertexBuffer and MetalIndexBuffer must outlive the MetalRenderPrimitive.
@@ -170,35 +163,18 @@ struct MetalRenderPrimitive : public HwRenderPrimitive {
// This struct is used to create the pipeline description to describe vertex assembly.
VertexDescription vertexDescription = {};
struct Entry {
uint8_t sourceBufferIndex = 0;
uint8_t stride = 0;
// maps to ->
uint8_t bufferArgumentIndex = 0;
Entry(uint8_t sourceBufferIndex, uint8_t stride, uint8_t bufferArgumentIndex)
: sourceBufferIndex(sourceBufferIndex),
stride(stride),
bufferArgumentIndex(bufferArgumentIndex) {}
};
utils::FixedCapacityVector<Entry> bufferMapping;
};
class MetalProgram : public HwProgram {
public:
MetalProgram(MetalContext& context, Program&& program) noexcept;
struct MetalProgram : public HwProgram {
MetalProgram(id<MTLDevice> device, const Program& program) noexcept;
const MetalShaderCompiler::MetalFunctionBundle& getFunctions();
const Program::SamplerGroupInfo& getSamplerGroupInfo() { return samplerGroupInfo; }
private:
void initialize();
id<MTLFunction> vertexFunction;
id<MTLFunction> fragmentFunction;
id<MTLFunction> computeFunction;
Program::SamplerGroupInfo samplerGroupInfo;
MetalContext& mContext;
MetalShaderCompiler::MetalFunctionBundle mFunctionBundle;
MetalShaderCompiler::program_token_t mToken;
bool isValid = false;
};
struct PixelBufferShape {
@@ -241,14 +217,21 @@ public:
void generateMipmaps() noexcept;
// A texture starts out with none of its mip levels (also referred to as LODs) available for
// reading. 4 actions update the range of LODs available:
// reading. 3 actions update the range of LODs available:
// - calling loadImage
// - calling generateMipmaps
// - using the texture as a render target attachment
// - calling setMinMaxLevels
// The range of available mips can only increase, never decrease.
// A texture's available mips are consistent throughout a render pass.
void setLodRange(uint16_t minLevel, uint16_t maxLevel);
void extendLodRangeTo(uint16_t level);
void updateLodRange(uint32_t level);
void updateLodRange(uint32_t minLevel, uint32_t maxLevel);
// Returns true if the texture has all of its mip levels accessible for reading.
// For any MetalTexture, once this is true, will always return true.
// The value returned will remain consistent for an entire render pass.
bool allLodsValid() const {
return minLod == 0 && maxLod == levels - 1;
}
static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format);
@@ -262,26 +245,6 @@ public:
MTLPixelFormat devicePixelFormat;
// Frees memory associated with this texture and marks it as "terminated".
// Used to track "use after free" scenario.
void terminate() noexcept;
bool isTerminated() const noexcept { return terminated; }
inline void checkUseAfterFree(const char* samplerGroupDebugName, size_t textureIndex) const {
if (UTILS_LIKELY(!isTerminated())) {
return;
}
NSString* reason =
[NSString stringWithFormat:
@"Filament Metal texture use after free, sampler group = "
@"%s, texture index = %zu",
samplerGroupDebugName, textureIndex];
NSException* useAfterFreeException =
[NSException exceptionWithName:@"MetalTextureUseAfterFree"
reason:reason
userInfo:nil];
[useAfterFreeException raise];
}
private:
void loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
PixelBufferDescriptor const& data) noexcept;
@@ -298,17 +261,14 @@ private:
id<MTLTexture> swizzledTextureView = nil;
id<MTLTexture> lodTextureView = nil;
uint16_t minLod = std::numeric_limits<uint16_t>::max();
uint16_t maxLod = 0;
bool terminated = false;
uint32_t minLod = UINT_MAX;
uint32_t maxLod = 0;
};
class MetalSamplerGroup : public HwSamplerGroup {
public:
explicit MetalSamplerGroup(size_t size, utils::FixedSizeString<32> name) noexcept
explicit MetalSamplerGroup(size_t size) noexcept
: size(size),
debugName(name),
textureHandles(size, Handle<HwTexture>()),
textures(size, nil),
samplers(size, nil) {}
@@ -318,10 +278,12 @@ public:
textureHandles[index] = th;
}
#ifndef NDEBUG
// This method is only used for debugging, to ensure all texture handles are alive.
const auto& getTextureHandles() const {
return textureHandles;
}
#endif
// Encode a MTLTexture into this SamplerGroup at the given index.
inline void setFinalizedTexture(size_t index, id<MTLTexture> t) {
@@ -367,7 +329,6 @@ public:
void useResources(id<MTLRenderCommandEncoder> renderPassEncoder);
size_t size;
utils::FixedSizeString<32> debugName;
public:

View File

@@ -39,15 +39,7 @@ namespace filament {
namespace backend {
static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) {
NSUInteger u = MTLTextureUsageUnknown;
if (any(usage & TextureUsage::SAMPLEABLE)) {
u |= MTLTextureUsageShaderRead;
}
if (any(usage & TextureUsage::UPLOADABLE)) {
// This is only needed because of the slowpath is MetalBlitter
u |= MTLTextureUsageRenderTarget;
}
NSUInteger u = 0;
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
u |= MTLTextureUsageRenderTarget;
}
@@ -57,23 +49,16 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) {
if (any(usage & TextureUsage::STENCIL_ATTACHMENT)) {
u |= MTLTextureUsageRenderTarget;
}
if (any(usage & TextureUsage::BLIT_DST)) {
// This is only needed because of the slowpath is MetalBlitter
u |= MTLTextureUsageRenderTarget;
}
if (any(usage & TextureUsage::BLIT_SRC)) {
u |= MTLTextureUsageShaderRead;
}
// All textures can be blitted from, so they must have the UsageShaderRead flag.
u |= MTLTextureUsageShaderRead;
return MTLTextureUsage(u);
}
MetalSwapChain::MetalSwapChain(MetalContext& context, CAMetalLayer* nativeWindow, uint64_t flags)
: context(context),
depthStencilFormat(decideDepthStencilFormat(flags)),
layer(nativeWindow),
externalImage(context),
type(SwapChainType::CAMETALLAYER) {
: context(context), layer(nativeWindow), externalImage(context),
type(SwapChainType::CAMETALLAYER) {
if (!(flags & SwapChain::CONFIG_TRANSPARENT) && !nativeWindow.opaque) {
utils::slog.w << "Warning: Filament SwapChain has no CONFIG_TRANSPARENT flag, "
@@ -94,30 +79,17 @@ MetalSwapChain::MetalSwapChain(MetalContext& context, CAMetalLayer* nativeWindow
}
MetalSwapChain::MetalSwapChain(MetalContext& context, int32_t width, int32_t height, uint64_t flags)
: context(context),
depthStencilFormat(decideDepthStencilFormat(flags)),
headlessWidth(width),
headlessHeight(height),
externalImage(context),
type(SwapChainType::HEADLESS) {}
: context(context), headlessWidth(width), headlessHeight(height), externalImage(context),
type(SwapChainType::HEADLESS) { }
MetalSwapChain::MetalSwapChain(MetalContext& context, CVPixelBufferRef pixelBuffer, uint64_t flags)
: context(context),
depthStencilFormat(decideDepthStencilFormat(flags)),
externalImage(context),
type(SwapChainType::CVPIXELBUFFERREF) {
: context(context), externalImage(context), type(SwapChainType::CVPIXELBUFFERREF) {
assert_invariant(flags & SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER);
MetalExternalImage::assertWritableImage(pixelBuffer);
externalImage.set(pixelBuffer);
assert_invariant(externalImage.isValid());
}
MTLPixelFormat MetalSwapChain::decideDepthStencilFormat(uint64_t flags) {
// These formats are supported on all devices, both iOS and macOS.
return flags & SwapChain::CONFIG_HAS_STENCIL_BUFFER ? MTLPixelFormatDepth32Float_Stencil8
: MTLPixelFormatDepth32Float;
}
MetalSwapChain::~MetalSwapChain() {
externalImage.set(nullptr);
}
@@ -184,40 +156,37 @@ void MetalSwapChain::releaseDrawable() {
}
id<MTLTexture> MetalSwapChain::acquireDepthTexture() {
ensureDepthStencilTexture();
assert_invariant(depthStencilTexture);
return depthStencilTexture;
}
id<MTLTexture> MetalSwapChain::acquireStencilTexture() {
if (!isMetalFormatStencil(depthStencilFormat)) {
return nil;
}
ensureDepthStencilTexture();
assert_invariant(depthStencilTexture);
return depthStencilTexture;
}
void MetalSwapChain::ensureDepthStencilTexture() {
NSUInteger width = getSurfaceWidth();
NSUInteger height = getSurfaceHeight();
if (UTILS_LIKELY(depthStencilTexture)) {
// If the surface size has changed, we'll need to allocate a new depth/stencil texture.
if (UTILS_UNLIKELY(
depthStencilTexture.width != width || depthStencilTexture.height != height)) {
depthStencilTexture = nil;
if (depthTexture) {
// If the surface size has changed, we'll need to allocate a new depth texture.
if (depthTexture.width != getSurfaceWidth() ||
depthTexture.height != getSurfaceHeight()) {
depthTexture = nil;
} else {
return;
return depthTexture;
}
}
const MTLPixelFormat depthFormat =
#if defined(IOS)
MTLPixelFormatDepth32Float;
#else
context.device.depth24Stencil8PixelFormatSupported ?
MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
#endif
const NSUInteger width = getSurfaceWidth();
const NSUInteger height = getSurfaceHeight();
MTLTextureDescriptor* descriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:depthStencilFormat
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:depthFormat
width:width
height:height
mipmapped:NO];
descriptor.usage = MTLTextureUsageRenderTarget;
descriptor.resourceOptions = MTLResourceStorageModePrivate;
depthStencilTexture = [context.device newTextureWithDescriptor:descriptor];
depthTexture = [context.device newTextureWithDescriptor:descriptor];
return depthTexture;
}
void MetalSwapChain::setFrameScheduledCallback(FrameScheduledCallback callback, void* user) {
@@ -245,30 +214,13 @@ void MetalSwapChain::present() {
}
}
struct PresentDrawableData {
void* drawable = nullptr;
MetalDriver* driver = nullptr;
};
void presentDrawable(bool presentFrame, void* user) {
auto* presentDrawableData = static_cast<PresentDrawableData*>(user);
// CFBridgingRelease here is used to balance the CFBridgingRetain inside acquireDrawable.
id<CAMetalDrawable> drawable =
(id<CAMetalDrawable>)CFBridgingRelease(presentDrawableData->drawable);
// CFBridgingRelease here is used to balance the CFBridgingRetain inside of acquireDrawable.
id<CAMetalDrawable> drawable = (id<CAMetalDrawable>) CFBridgingRelease(user);
if (presentFrame) {
[drawable present];
}
// Schedule the drawable destruction on the driver thread.
void* voidDrawable = (void*) CFBridgingRetain(drawable);
MetalDriver* driver = presentDrawableData->driver;
driver->runAtNextTick([voidDrawable]() {
// The drawable is released here.
CFBridgingRelease(voidDrawable);
});
delete presentDrawableData;
// The drawable will be released here when the "drawable" variable goes out of scope.
}
void MetalSwapChain::scheduleFrameScheduledCallback() {
@@ -283,15 +235,13 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
// capture the _this_ pointer (MetalSwapChain*) instead of the drawable.
id<CAMetalDrawable> d = drawable;
void* userData = frameScheduledUserData;
MetalDriver* driver = context.driver;
[getPendingCommandBuffer(&context) addScheduledHandler:^(id<MTLCommandBuffer> cb) {
// CFBridgingRetain is used here to give the drawable a +1 retain count before
// casting it to a void*.
auto* presentDrawableData = new PresentDrawableData;
presentDrawableData->drawable = (void*) CFBridgingRetain(d);
presentDrawableData->driver = driver;
PresentCallable callable(presentDrawable, (void*) presentDrawableData);
callback(callable, userData);
PresentCallable callable(presentDrawable, (void*) CFBridgingRetain(d));
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
callback(callable, userData);
});
}];
}
@@ -330,9 +280,6 @@ MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uin
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
MetalRenderPrimitive::MetalRenderPrimitive()
: bufferMapping(utils::FixedCapacityVector<Entry>::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {}
void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer*
indexBuffer) {
this->vertexBuffer = vertexBuffer;
@@ -340,94 +287,118 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde
const size_t attributeCount = vertexBuffer->attributes.size();
auto& mapping = bufferMapping;
mapping.clear();
vertexDescription = {};
// Set the layout for the zero buffer, which unused attributes are mapped to.
vertexDescription.layouts[ZERO_VERTEX_BUFFER_LOGICAL_INDEX] = {
.step = MTLVertexStepFunctionConstant, .stride = 16
};
// Here we map each source buffer to a Metal buffer argument.
// Each attribute has a source buffer, offset, and stride.
// Two source buffers with the same index and stride can share the same Metal buffer argument
// index.
//
// The source buffer is the buffer index that the Filament client sets.
// * source buffer
// .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT2, 0, 12)
// .attribute(VertexAttribute::UV, 0, VertexBuffer::AttributeType::HALF2, 8, 12)
// .attribute(VertexAttribute::COLOR, 1, VertexBuffer::AttributeType::UBYTE4, 0, 4)
auto allocateOrGetBufferArgumentIndex =
[&mapping, currentBufferArgumentIndex = USER_VERTEX_BUFFER_BINDING_START, this](
auto sourceBuffer, auto sourceBufferStride) mutable -> uint8_t {
auto match = [&](const auto& e) {
return e.sourceBufferIndex == sourceBuffer && e.stride == sourceBufferStride;
};
if (auto it = std::find_if(mapping.begin(), mapping.end(), match); it != mapping.end()) {
return it->bufferArgumentIndex;
} else {
auto bufferArgumentIndex = currentBufferArgumentIndex++;
mapping.emplace_back(sourceBuffer, sourceBufferStride, bufferArgumentIndex);
vertexDescription.layouts[bufferArgumentIndex] = {
.step = MTLVertexStepFunctionPerVertex, .stride = sourceBufferStride
};
return bufferArgumentIndex;
}
};
// Each attribute gets its own vertex buffer, starting at logical buffer 1.
uint32_t bufferIndex = 1;
for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) {
const auto& attribute = vertexBuffer->attributes[attributeIndex];
// If the attribute is unused, bind it to the zero buffer. It's a Metal error for a shader
// to read from missing vertex attributes.
if (attribute.buffer == Attribute::BUFFER_UNUSED) {
const MTLVertexFormat format = (attribute.flags & Attribute::FLAG_INTEGER_TARGET)
? MTLVertexFormatUInt4
: MTLVertexFormatFloat4;
const uint8_t flags = attribute.flags;
const MTLVertexFormat format = (flags & Attribute::FLAG_INTEGER_TARGET) ?
MTLVertexFormatUInt4 : MTLVertexFormatFloat4;
// If the attribute is not enabled, bind it to the zero buffer. It's a Metal error for a
// shader to read from missing vertex attributes.
vertexDescription.attributes[attributeIndex] = {
.format = format, .buffer = ZERO_VERTEX_BUFFER_LOGICAL_INDEX, .offset = 0
.format = format,
.buffer = ZERO_VERTEX_BUFFER_LOGICAL_INDEX,
.offset = 0
};
vertexDescription.layouts[ZERO_VERTEX_BUFFER_LOGICAL_INDEX] = {
.step = MTLVertexStepFunctionConstant,
.stride = 16
};
continue;
}
// Map the source buffer and stride of this attribute to a Metal buffer argument.
auto bufferArgumentIndex =
allocateOrGetBufferArgumentIndex(attribute.buffer, attribute.stride);
vertexDescription.attributes[attributeIndex] = {
.format = getMetalFormat(
attribute.type, attribute.flags & Attribute::FLAG_NORMALIZED),
.buffer = uint32_t(bufferArgumentIndex),
.offset = attribute.offset
.format = getMetalFormat(attribute.type,
attribute.flags & Attribute::FLAG_NORMALIZED),
.buffer = bufferIndex,
.offset = 0
};
}
vertexDescription.layouts[bufferIndex] = {
.step = MTLVertexStepFunctionPerVertex,
.stride = attribute.stride
};
bufferIndex++;
};
}
MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept
: HwProgram(program.getName()), mContext(context) {
MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcept
: HwProgram(program.getName()), vertexFunction(nil), fragmentFunction(nil),
computeFunction(nil), isValid(false) {
using MetalFunctionPtr = __strong id<MTLFunction>*;
static_assert(Program::SHADER_TYPE_COUNT == 3, "Only vertex, fragment, and/or compute shaders expected.");
MetalFunctionPtr shaderFunctions[3] = { &vertexFunction, &fragmentFunction, &computeFunction };
const auto& sources = program.getShadersSource();
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
const auto& source = sources[i];
// It's okay for some shaders to be empty, they shouldn't be used in any draw calls.
if (source.empty()) {
continue;
}
assert_invariant( source[source.size() - 1] == '\0' );
// the shader string is null terminated and the length includes the null character
NSString* objcSource = [[NSString alloc] initWithBytes:source.data()
length:source.size() - 1
encoding:NSUTF8StringEncoding];
NSError* error = nil;
// When options is nil, Metal uses the most recent language version available.
id<MTLLibrary> library = [device newLibraryWithSource:objcSource
options:nil
error:&error];
if (library == nil) {
if (error) {
auto description =
[error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
utils::slog.w << description << utils::io::endl;
}
PANIC_LOG("Failed to compile Metal program.");
return;
}
MTLFunctionConstantValues* constants = [MTLFunctionConstantValues new];
auto const& specializationConstants = program.getSpecializationConstants();
for (auto const& sc : specializationConstants) {
const std::array<MTLDataType, 3> types{
MTLDataTypeInt, MTLDataTypeFloat, MTLDataTypeBool };
std::visit([&sc, constants, type = types[sc.value.index()]](auto&& arg) {
[constants setConstantValue:&arg
type:type
atIndex:sc.id];
}, sc.value);
}
id<MTLFunction> function = [library newFunctionWithName:@"main0"
constantValues:constants
error:&error];
if (!program.getName().empty()) {
function.label = @(program.getName().c_str());
}
assert_invariant(function);
*shaderFunctions[i] = function;
}
UTILS_UNUSED_IN_RELEASE const bool isRasterizationProgram =
vertexFunction != nil && fragmentFunction != nil;
UTILS_UNUSED_IN_RELEASE const bool isComputeProgram = computeFunction != nil;
// The program must be either a rasterization program XOR a compute program.
assert_invariant(isRasterizationProgram != isComputeProgram);
// All stages of the program have compiled successfully, this is a valid program.
isValid = true;
// Save this program's SamplerGroupInfo, it's used during draw calls to bind sampler groups to
// the appropriate stage(s).
samplerGroupInfo = program.getSamplerGroupInfo();
mToken = context.shaderCompiler->createProgram(program.getName(), std::move(program));
assert_invariant(mToken);
}
const MetalShaderCompiler::MetalFunctionBundle& MetalProgram::getFunctions() {
initialize();
return mFunctionBundle;
}
void MetalProgram::initialize() {
if (!mToken) {
return;
}
mFunctionBundle = mContext.shaderCompiler->getProgram(mToken);
assert_invariant(!mToken);
}
MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t levels,
@@ -542,16 +513,7 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
: HwTexture(target, levels, samples, width, height, depth, format, usage), context(context),
externalImage(context) {
texture = metalTexture;
setLodRange(0, levels - 1);
}
void MetalTexture::terminate() noexcept {
texture = nil;
swizzledTextureView = nil;
lodTextureView = nil;
msaaSidecar = nil;
externalImage.set(nullptr);
terminated = true;
updateLodRange(0, levels - 1);
}
MetalTexture::~MetalTexture() {
@@ -696,14 +658,14 @@ void MetalTexture::loadImage(uint32_t level, MTLRegion region, PixelBufferDescri
}
}
extendLodRangeTo(level);
updateLodRange(level);
}
void MetalTexture::generateMipmaps() noexcept {
id <MTLBlitCommandEncoder> blitEncoder = [getPendingCommandBuffer(&context) blitCommandEncoder];
[blitEncoder generateMipmapsForTexture:texture];
[blitEncoder endEncoding];
setLodRange(0, texture.mipmapLevelCount - 1);
updateLodRange(0, texture.mipmapLevelCount - 1);
}
void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
@@ -787,7 +749,6 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
#endif
id<MTLTexture> stagingTexture = [context.device newTextureWithDescriptor:descriptor];
// FIXME? Why is this not just `MTLRegion sourceRegion = region;` ?
MTLRegion sourceRegion = MTLRegionMake3D(0, 0, 0,
region.size.width, region.size.height, region.size.depth);
[stagingTexture replaceRegion:sourceRegion
@@ -814,31 +775,31 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
slices:NSMakeRange(0, slices)];
}
MetalBlitter::BlitArgs args{};
MetalBlitter::BlitArgs args;
args.filter = SamplerMagFilter::NEAREST;
args.source.level = 0;
args.source.slice = 0;
args.source.region = sourceRegion;
args.source.texture = stagingTexture;
args.destination.level = level;
args.destination.slice = slice;
args.destination.region = region;
args.destination.texture = destinationTexture;
args.source.color = stagingTexture;
args.destination.color = destinationTexture;
context.blitter->blit(getPendingCommandBuffer(&context), args, "Texture upload blit");
}
void MetalTexture::extendLodRangeTo(uint16_t level) {
void MetalTexture::updateLodRange(uint32_t level) {
assert_invariant(!isInRenderPass(&context));
minLod = std::min(minLod, level);
maxLod = std::max(maxLod, level);
lodTextureView = nil;
}
void MetalTexture::setLodRange(uint16_t min, uint16_t max) {
void MetalTexture::updateLodRange(uint32_t min, uint32_t max) {
assert_invariant(!isInRenderPass(&context));
assert_invariant(min <= max);
minLod = min;
maxLod = max;
minLod = std::min(minLod, min);
maxLod = std::max(maxLod, max);
lodTextureView = nil;
}
@@ -1162,7 +1123,7 @@ MetalRenderTarget::Attachment MetalRenderTarget::getDepthAttachment() {
MetalRenderTarget::Attachment MetalRenderTarget::getStencilAttachment() {
Attachment result = stencil;
if (defaultRenderTarget) {
result.texture = context->currentDrawSwapChain->acquireStencilTexture();
// TODO: do we want the default SwapChain to have a default stencil buffer?
}
return result;
}

View File

@@ -1,110 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_METAL_METALSHADERCOMPILER_H
#define TNT_FILAMENT_BACKEND_METAL_METALSHADERCOMPILER_H
#include "CompilerThreadPool.h"
#include "CallbackManager.h"
#include <backend/CallbackHandler.h>
#include <backend/Program.h>
#include <utils/CString.h>
#include <Metal/Metal.h>
#include <array>
#include <memory>
namespace filament::backend {
class MetalDriver;
class MetalShaderCompiler {
struct MetalProgramToken;
public:
class MetalFunctionBundle {
public:
MetalFunctionBundle() = default;
MetalFunctionBundle(id<MTLFunction> fragment, id<MTLFunction> vertex)
: functions{fragment, vertex} {
assert_invariant(fragment && vertex);
assert_invariant(fragment.functionType == MTLFunctionTypeFragment);
assert_invariant(vertex.functionType == MTLFunctionTypeVertex);
}
explicit MetalFunctionBundle(id<MTLFunction> compute) : functions{compute, nil} {
assert_invariant(compute);
assert_invariant(compute.functionType == MTLFunctionTypeKernel);
}
std::pair<id<MTLFunction>, id<MTLFunction>> getRasterFunctions() const noexcept {
assert_invariant(functions[0].functionType == MTLFunctionTypeFragment);
assert_invariant(functions[1].functionType == MTLFunctionTypeVertex);
return {functions[0], functions[1]};
}
id<MTLFunction> getComputeFunction() const noexcept {
assert_invariant(functions[0].functionType == MTLFunctionTypeKernel);
return functions[0];
}
explicit operator bool() const { return functions[0] != nil; }
private:
// Can hold two functions, either:
// - fragment and vertex (for rasterization pipelines)
// - compute (for compute pipelines)
id<MTLFunction> functions[2] = {nil, nil};
};
using program_token_t = std::shared_ptr<MetalProgramToken>;
explicit MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver);
MetalShaderCompiler(MetalShaderCompiler const& rhs) = delete;
MetalShaderCompiler(MetalShaderCompiler&& rhs) = delete;
MetalShaderCompiler& operator=(MetalShaderCompiler const& rhs) = delete;
MetalShaderCompiler& operator=(MetalShaderCompiler&& rhs) = delete;
void init() noexcept;
void terminate() noexcept;
// Creates a program asynchronously
program_token_t createProgram(utils::CString const& name, Program&& program);
// Returns the functions, blocking if necessary. The Token is destroyed and becomes invalid.
MetalFunctionBundle getProgram(program_token_t& token);
// Destroys a valid token and all associated resources. Used to "cancel" a program compilation.
static void terminate(program_token_t& token);
void notifyWhenAllProgramsAreReady(
CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
private:
static MetalFunctionBundle compileProgram(const Program& program, id<MTLDevice> device);
CompilerThreadPool mCompilerThreadPool;
id<MTLDevice> mDevice;
CallbackManager mCallbackManager;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_METAL_METALSHADERCOMPILER_H

View File

@@ -1,219 +0,0 @@
/*
* Copyright (C) 2023 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 "MetalShaderCompiler.h"
#include "MetalDriver.h"
#include <backend/Program.h>
#include <utils/JobSystem.h>
#include <utils/Mutex.h>
#include <chrono>
namespace filament::backend {
using namespace utils;
struct MetalShaderCompiler::MetalProgramToken : ProgramToken {
MetalProgramToken(MetalShaderCompiler& compiler) noexcept
: compiler(compiler) {
}
~MetalProgramToken() override;
void set(MetalFunctionBundle p) noexcept {
std::unique_lock const l(lock);
std::swap(program, p);
signaled = true;
cond.notify_one();
}
MetalFunctionBundle get() const noexcept {
std::unique_lock l(lock);
cond.wait(l, [this](){ return signaled; });
return program;
}
void wait() const noexcept {
std::unique_lock l(lock);
cond.wait(l, [this]() { return signaled; });
}
bool isReady() const noexcept {
std::unique_lock l(lock);
using namespace std::chrono_literals;
return cond.wait_for(l, 0s, [this]() { return signaled; });
}
MetalShaderCompiler& compiler;
CallbackManager::Handle handle{};
MetalFunctionBundle program{};
mutable utils::Mutex lock;
mutable utils::Condition cond;
bool signaled = false;
};
MetalShaderCompiler::MetalProgramToken::~MetalProgramToken() = default;
MetalShaderCompiler::MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver)
: mDevice(device),
mCallbackManager(driver) {
}
void MetalShaderCompiler::init() noexcept {
const uint32_t poolSize = 2;
mCompilerThreadPool.init(poolSize, []() {}, []() {});
}
void MetalShaderCompiler::terminate() noexcept {
mCompilerThreadPool.terminate();
mCallbackManager.terminate();
}
/* static */ MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::compileProgram(
const Program& program, id<MTLDevice> device) {
std::array<id<MTLFunction>, Program::SHADER_TYPE_COUNT> functions = { nil };
const auto& sources = program.getShadersSource();
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
const auto& source = sources[i];
// It's okay for some shaders to be empty, they shouldn't be used in any draw calls.
if (source.empty()) {
continue;
}
assert_invariant(source[source.size() - 1] == '\0');
// the shader string is null terminated and the length includes the null character
NSString* objcSource = [[NSString alloc] initWithBytes:source.data()
length:source.size() - 1
encoding:NSUTF8StringEncoding];
NSError* error = nil;
// When options is nil, Metal uses the most recent language version available.
id<MTLLibrary> library = [device newLibraryWithSource:objcSource
options:nil
error:&error];
if (library == nil) {
if (error) {
auto description =
[error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
utils::slog.w << description << utils::io::endl;
}
PANIC_LOG("Failed to compile Metal program.");
return {};
}
MTLFunctionConstantValues* constants = [MTLFunctionConstantValues new];
auto const& specializationConstants = program.getSpecializationConstants();
for (auto const& sc : specializationConstants) {
const std::array<MTLDataType, 3> types{
MTLDataTypeInt, MTLDataTypeFloat, MTLDataTypeBool };
std::visit([&sc, constants, type = types[sc.value.index()]](auto&& arg) {
[constants setConstantValue:&arg
type:type
atIndex:sc.id];
}, sc.value);
}
id<MTLFunction> function = [library newFunctionWithName:@"main0"
constantValues:constants
error:&error];
if (!program.getName().empty()) {
function.label = @(program.getName().c_str());
}
assert_invariant(function);
functions[i] = function;
}
static_assert(Program::SHADER_TYPE_COUNT == 3,
"Only vertex, fragment, and/or compute shaders expected.");
id<MTLFunction> vertexFunction = functions[0];
id<MTLFunction> fragmentFunction = functions[1];
id<MTLFunction> computeFunction = functions[2];
const bool isRasterizationProgram = vertexFunction != nil && fragmentFunction != nil;
const bool isComputeProgram = computeFunction != nil;
// The program must be either a rasterization program XOR a compute program.
assert_invariant(isRasterizationProgram != isComputeProgram);
if (isRasterizationProgram) {
return {fragmentFunction, vertexFunction};
}
if (isComputeProgram) {
return MetalFunctionBundle{computeFunction};
}
return {};
}
MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
CString const& name, Program&& program) {
auto token = std::make_shared<MetalProgramToken>(*this);
token->handle = mCallbackManager.get();
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
mCompilerThreadPool.queue(priorityQueue, token,
[this, name, device = mDevice, program = std::move(program), token]() {
MetalFunctionBundle compiledProgram = compileProgram(program, device);
token->set(compiledProgram);
mCallbackManager.put(token->handle);
});
return token;
}
MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::getProgram(program_token_t& token) {
assert_invariant(token);
if (!token->isReady()) {
auto job = mCompilerThreadPool.dequeue(token);
if (job) {
job();
}
}
MetalShaderCompiler::MetalFunctionBundle program = token->get();
token = nullptr;
return program;
}
/* static */ void MetalShaderCompiler::terminate(program_token_t& token) {
assert_invariant(token);
auto job = token->compiler.mCompilerThreadPool.dequeue(token);
if (!job) {
// The job is being executed right now (or has already executed).
token->wait();
} else {
// The job has not executed yet.
token->compiler.mCallbackManager.put(token->handle);
}
token.reset();
}
void MetalShaderCompiler::notifyWhenAllProgramsAreReady(
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
mCallbackManager.setCallback(handler, callback, user);
}
} // namespace filament::backend

View File

@@ -182,10 +182,6 @@ bool NoopDriver::isParallelShaderCompileSupported() {
return false;
}
bool NoopDriver::isDepthStencilResolveSupported() {
return true;
}
bool NoopDriver::isWorkaroundNeeded(Workaround) {
return false;
}
@@ -256,6 +252,10 @@ void NoopDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {
void NoopDriver::generateMipmaps(Handle<HwTexture> th) { }
bool NoopDriver::canGenerateMipmaps() {
return true;
}
void NoopDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
BufferDescriptor&& data) {
scheduleDestroy(std::move(data));
@@ -322,23 +322,12 @@ void NoopDriver::readBufferSubData(backend::BufferObjectHandle boh,
scheduleDestroy(std::move(p));
}
void NoopDriver::blitDEPRECATED(TargetBufferFlags buffers,
void NoopDriver::blit(TargetBufferFlags buffers,
Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect,
SamplerMagFilter filter) {
}
void NoopDriver::resolve(
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer,
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
}
void NoopDriver::blit(
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer, math::uint2 dstOrigin,
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer, math::uint2 srcOrigin,
math::uint2 size) {
}
void NoopDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
uint32_t instanceCount) {
}

View File

@@ -12,11 +12,10 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef TNT_FILAMENT_BACKEND_CALLBACKMANAGER_H
#define TNT_FILAMENT_BACKEND_CALLBACKMANAGER_H
#ifndef TNT_FILAMENT_BACKEND_OPENGL_CALLBACKMANAGER_H
#define TNT_FILAMENT_BACKEND_OPENGL_CALLBACKMANAGER_H
#include <backend/CallbackHandler.h>
@@ -96,4 +95,4 @@ private:
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_CALLBACKMANAGER_H
#endif // TNT_FILAMENT_BACKEND_OPENGL_CALLBACKMANAGER_H

View File

@@ -373,19 +373,16 @@ constexpr inline GLenum getCullingMode(CullingMode mode) noexcept {
constexpr inline std::pair<GLenum, GLenum> textureFormatToFormatAndType(
TextureFormat format) noexcept {
switch (format) {
case TextureFormat::R8: return { 0x1909 /*GL_LUMINANCE*/, GL_UNSIGNED_BYTE };
case TextureFormat::RGB8: return { GL_RGB, GL_UNSIGNED_BYTE };
case TextureFormat::SRGB8: return { GL_RGB, GL_UNSIGNED_BYTE };
case TextureFormat::RGBA8: return { GL_RGBA, GL_UNSIGNED_BYTE };
case TextureFormat::SRGB8_A8: return { GL_RGBA, GL_UNSIGNED_BYTE };
case TextureFormat::RGB565: return { GL_RGB, GL_UNSIGNED_SHORT_5_6_5 };
case TextureFormat::RGB5_A1: return { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 };
case TextureFormat::RGBA4: return { GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 };
case TextureFormat::DEPTH16: return { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT };
case TextureFormat::DEPTH24: return { GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
case TextureFormat::RGB8: return { GL_RGB, GL_UNSIGNED_BYTE };
case TextureFormat::RGBA8: return { GL_RGBA, GL_UNSIGNED_BYTE };
case TextureFormat::RGB565: return { GL_RGB, GL_UNSIGNED_SHORT_5_6_5 };
case TextureFormat::RGB5_A1: return { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 };
case TextureFormat::RGBA4: return { GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 };
case TextureFormat::DEPTH16: return { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT };
case TextureFormat::DEPTH24: return { GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
case TextureFormat::DEPTH24_STENCIL8:
return { GL_DEPTH24_STENCIL8, GL_UNSIGNED_INT_24_8 };
default: return { GL_NONE, GL_NONE };
return { GL_DEPTH24_STENCIL8, GL_UNSIGNED_INT_24_8 };
default: return { GL_NONE, GL_NONE };
}
}

View File

@@ -16,8 +16,6 @@
#include "OpenGLBlobCache.h"
#include "OpenGLContext.h"
#include <backend/Platform.h>
#include <backend/Program.h>
@@ -30,18 +28,17 @@ struct OpenGLBlobCache::Blob {
char data[];
};
OpenGLBlobCache::OpenGLBlobCache(OpenGLContext& gl) noexcept
: mCachingSupported(gl.gets.num_program_binary_formats >= 1) {
}
GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform,
Program const& program) const noexcept {
Program const& program) noexcept {
SYSTRACE_CALL();
if (!mCachingSupported || !platform.hasRetrieveBlobFunc()) {
if (!platform.hasBlobFunc()) {
// the key is never updated in that case
return 0;
}
SYSTRACE_CONTEXT();
GLuint programId = 0;
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
@@ -67,10 +64,8 @@ GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform,
programId = glCreateProgram();
{ // scope for systrace
SYSTRACE_NAME("glProgramBinary");
glProgramBinary(programId, blob->format, blob->data, programBinarySize);
}
SYSTRACE_NAME("glProgramBinary");
glProgramBinary(programId, blob->format, blob->data, programBinarySize);
if (UTILS_UNLIKELY(glGetError() != GL_NO_ERROR)) {
// glProgramBinary can fail if for instance the driver has been updated
@@ -90,28 +85,19 @@ GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform,
void OpenGLBlobCache::insert(Platform& platform,
BlobCacheKey const& key, GLuint program) noexcept {
SYSTRACE_CALL();
if (!mCachingSupported || !platform.hasInsertBlobFunc()) {
// the key is never updated in that case
return;
}
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
GLenum format;
GLint programBinarySize = 0;
{ // scope for systrace
SYSTRACE_CALL();
if (platform.hasBlobFunc()) {
SYSTRACE_CONTEXT();
GLenum format;
GLint programBinarySize;
SYSTRACE_NAME("glGetProgramiv");
glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &programBinarySize);
}
if (programBinarySize) {
size_t const size = sizeof(Blob) + programBinarySize;
std::unique_ptr<Blob, decltype(&::free)> blob{ (Blob*)malloc(size), &::free };
if (UTILS_LIKELY(blob)) {
{ // scope for systrace
SYSTRACE_NAME("glGetProgramBinary");
glGetProgramBinary(program, programBinarySize,
&programBinarySize, &format, blob->data);
}
if (programBinarySize) {
size_t const size = sizeof(Blob) + programBinarySize;
std::unique_ptr<Blob, decltype(&::free)> blob{ (Blob*)malloc(size), &::free };
SYSTRACE_NAME("glGetProgramBinary");
glGetProgramBinary(program, programBinarySize, &programBinarySize, &format, blob->data);
GLenum const error = glGetError();
if (error == GL_NO_ERROR) {
blob->format = format;
@@ -122,4 +108,18 @@ void OpenGLBlobCache::insert(Platform& platform,
#endif
}
void OpenGLBlobCache::insert(Platform& platform, BlobCacheKey const& key,
GLenum format, void* data, GLsizei programBinarySize) noexcept {
SYSTRACE_CALL();
if (platform.hasBlobFunc()) {
if (programBinarySize) {
size_t const size = sizeof(Blob) + programBinarySize;
std::unique_ptr<Blob, decltype(&::free)> blob{ (Blob*)malloc(size), &::free };
blob->format = format;
memcpy(blob->data, data, programBinarySize);
platform.insertBlob(key.data(), key.size(), blob.get(), size);
}
}
}
} // namespace filament::backend

View File

@@ -25,21 +25,20 @@ namespace filament::backend {
class Platform;
class Program;
class OpenGLContext;
class OpenGLBlobCache {
public:
explicit OpenGLBlobCache(OpenGLContext& gl) noexcept;
static GLuint retrieve(BlobCacheKey* key, Platform& platform,
Program const& program) noexcept;
GLuint retrieve(BlobCacheKey* key, Platform& platform,
Program const& program) const noexcept;
void insert(Platform& platform,
static void insert(Platform& platform,
BlobCacheKey const& key, GLuint program) noexcept;
static void insert(Platform& platform, BlobCacheKey const& key,
GLenum format, void* data, GLsizei programBinarySize) noexcept;
private:
struct Blob;
bool mCachingSupported = false;
};
} // namespace filament::backend

View File

@@ -99,41 +99,38 @@ OpenGLContext::OpenGLContext() noexcept {
if (mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1) {
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE,
&gets.max_uniform_block_size);
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS,
&gets.max_uniform_buffer_bindings);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT,
&gets.uniform_buffer_offset_alignment);
glGetIntegerv(GL_MAX_SAMPLES,
&gets.max_samples);
glGetIntegerv(GL_MAX_DRAW_BUFFERS,
&gets.max_draw_buffers);
glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
&gets.max_transform_feedback_separate_attribs);
#ifdef GL_EXT_texture_filter_anisotropic
if (ext.EXT_texture_filter_anisotropic) {
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gets.max_anisotropy);
}
#endif
glGetIntegerv(GL_MAX_DRAW_BUFFERS,
&gets.max_draw_buffers);
glGetIntegerv(GL_MAX_SAMPLES,
&gets.max_samples);
glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
&gets.max_transform_feedback_separate_attribs);
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE,
&gets.max_uniform_block_size);
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS,
&gets.max_uniform_buffer_bindings);
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS,
&gets.num_program_binary_formats);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT,
&gets.uniform_buffer_offset_alignment);
#endif
}
#ifdef BACKEND_OPENGL_VERSION_GLES
else {
gets.max_anisotropy = 1;
gets.max_draw_buffers = 1;
gets.max_samples = 1;
gets.max_transform_feedback_separate_attribs = 0;
gets.max_uniform_block_size = 0;
gets.max_uniform_buffer_bindings = 0;
gets.num_program_binary_formats = 0;
gets.uniform_buffer_offset_alignment = 0;
gets.max_samples = 1;
gets.max_draw_buffers = 1;
gets.max_transform_feedback_separate_attribs = 0;
gets.max_anisotropy = 1;
}
#endif
slog.v << "Feature level: " << +mFeatureLevel << '\n';
slog.v << "Active workarounds: " << '\n';
UTILS_NOUNROLL
@@ -146,29 +143,13 @@ OpenGLContext::OpenGLContext() noexcept {
#ifndef NDEBUG
// this is useful for development
slog.v
<< "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = "
<< gets.max_anisotropy << '\n'
<< "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = "
<< gets.max_combined_texture_image_units << '\n'
<< "GL_MAX_DRAW_BUFFERS = "
<< gets.max_draw_buffers << '\n'
<< "GL_MAX_RENDERBUFFER_SIZE = "
<< gets.max_renderbuffer_size << '\n'
<< "GL_MAX_SAMPLES = "
<< gets.max_samples << '\n'
<< "GL_MAX_TEXTURE_IMAGE_UNITS = "
<< gets.max_texture_image_units << '\n'
<< "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = "
<< gets.max_transform_feedback_separate_attribs << '\n'
<< "GL_MAX_UNIFORM_BLOCK_SIZE = "
<< gets.max_uniform_block_size << '\n'
<< "GL_MAX_UNIFORM_BUFFER_BINDINGS = "
<< gets.max_uniform_buffer_bindings << '\n'
<< "GL_NUM_PROGRAM_BINARY_FORMATS = "
<< gets.num_program_binary_formats << '\n'
<< "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT = "
<< gets.uniform_buffer_offset_alignment << '\n'
slog.v << "GL_MAX_DRAW_BUFFERS = " << gets.max_draw_buffers << '\n'
<< "GL_MAX_RENDERBUFFER_SIZE = " << gets.max_renderbuffer_size << '\n'
<< "GL_MAX_SAMPLES = " << gets.max_samples << '\n'
<< "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = " << gets.max_anisotropy << '\n'
<< "GL_MAX_UNIFORM_BLOCK_SIZE = " << gets.max_uniform_block_size << '\n'
<< "GL_MAX_TEXTURE_IMAGE_UNITS = " << gets.max_texture_image_units << '\n'
<< "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT = " << gets.uniform_buffer_offset_alignment << '\n'
;
flush(slog.v);
#endif
@@ -274,7 +255,6 @@ void OpenGLContext::setDefaultState() noexcept {
glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST);
#endif
#if !defined(__EMSCRIPTEN__)
if (ext.EXT_clip_control) {
#if defined(BACKEND_OPENGL_VERSION_GL)
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
@@ -282,11 +262,9 @@ void OpenGLContext::setDefaultState() noexcept {
glClipControlEXT(GL_LOWER_LEFT_EXT, GL_ZERO_TO_ONE_EXT);
#endif
}
#endif
if (ext.EXT_clip_cull_distance) {
glEnable(GL_CLIP_DISTANCE0);
glEnable(GL_CLIP_DISTANCE1);
}
}
@@ -311,9 +289,7 @@ void OpenGLContext::initProcs(Procs* procs,
# ifdef BACKEND_OPENGL_VERSION_GL
procs->getQueryObjectui64v = glGetQueryObjectui64v; // only core in GL
# elif defined(GL_EXT_disjoint_timer_query)
# ifndef __EMSCRIPTEN__
procs->getQueryObjectui64v = glGetQueryObjectui64vEXT;
# endif
procs->getQueryObjectui64v = glGetQueryObjectui64vEXT;
# endif // BACKEND_OPENGL_VERSION_GL
// core in ES 3.0 and GL 4.3
@@ -325,7 +301,6 @@ void OpenGLContext::initProcs(Procs* procs,
#ifdef BACKEND_OPENGL_VERSION_GLES
# ifndef IOS // IOS is guaranteed to have ES3.x
# ifndef __EMSCRIPTEN__
if (UTILS_UNLIKELY(major == 2)) {
// Runtime OpenGL version is ES 2.x
if (UTILS_LIKELY(ext.OES_vertex_array_object)) {
@@ -353,7 +328,6 @@ void OpenGLContext::initProcs(Procs* procs,
procs->maxShaderCompilerThreadsKHR = glMaxShaderCompilerThreadsKHR;
}
# endif // __EMSCRIPTEN__
# endif // IOS
#else
procs->maxShaderCompilerThreadsKHR = glMaxShaderCompilerThreadsARB;
@@ -482,6 +456,7 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
// (that should be regardless of ANGLE, but we should double-check)
bugs->split_easu = true;
}
// TODO: see if we could use `bugs.allow_read_only_ancillary_feedback_loop = true`
}
#ifdef BACKEND_OPENGL_VERSION_GLES
@@ -586,23 +561,17 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
// figure out and initialize the extensions we need
using namespace std::literals;
ext->APPLE_color_buffer_packed_float = exts.has("GL_APPLE_color_buffer_packed_float"sv);
#ifndef __EMSCRIPTEN__
ext->EXT_clip_control = exts.has("GL_EXT_clip_control"sv);
#endif
ext->EXT_clip_cull_distance = exts.has("GL_EXT_clip_cull_distance"sv);
ext->EXT_color_buffer_float = exts.has("GL_EXT_color_buffer_float"sv);
ext->EXT_color_buffer_half_float = exts.has("GL_EXT_color_buffer_half_float"sv);
#ifndef __EMSCRIPTEN__
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
#endif
ext->EXT_discard_framebuffer = exts.has("GL_EXT_discard_framebuffer"sv);
#ifndef __EMSCRIPTEN__
ext->EXT_disjoint_timer_query = exts.has("GL_EXT_disjoint_timer_query"sv);
ext->EXT_multisampled_render_to_texture = exts.has("GL_EXT_multisampled_render_to_texture"sv);
ext->EXT_multisampled_render_to_texture2 = exts.has("GL_EXT_multisampled_render_to_texture2"sv);
#endif
ext->EXT_shader_framebuffer_fetch = exts.has("GL_EXT_shader_framebuffer_fetch"sv);
#ifndef __EMSCRIPTEN__
#if !defined(__EMSCRIPTEN__)
ext->EXT_texture_compression_etc2 = true;
#endif
ext->EXT_texture_compression_s3tc = exts.has("GL_EXT_texture_compression_s3tc"sv);

View File

@@ -153,15 +153,14 @@ public:
// glGet*() values
struct Gets {
GLfloat max_anisotropy;
GLint max_combined_texture_image_units;
GLint max_draw_buffers;
GLint max_renderbuffer_size;
GLint max_samples;
GLint max_texture_image_units;
GLint max_transform_feedback_separate_attribs;
GLint max_uniform_block_size;
GLint max_uniform_buffer_bindings;
GLint num_program_binary_formats;
GLint max_texture_image_units;
GLint max_combined_texture_image_units;
GLint max_transform_feedback_separate_attribs;
GLint max_uniform_buffer_bindings;
GLint uniform_buffer_offset_alignment;
} gets = {};

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