Compare commits

..

1 Commits

Author SHA1 Message Date
Benjamin Doherty
031cd302dd Capture command callstacks for debugging 2023-10-30 14:42:24 -07:00
684 changed files with 26338 additions and 30397 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,8 +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_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."
)
@@ -67,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
# ==================================================================================================
@@ -342,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)
@@ -379,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
@@ -391,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
@@ -531,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)
@@ -743,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

@@ -8,3 +8,8 @@ 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.0'
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.0'
```
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,84 +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.0
- matc: Fix ESSL 1.0 codegen when using external samplers [⚠️ **Recompile materials**]
- matc: Generate skinning variants for FL0 materials [⚠️ **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

@@ -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

@@ -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 getStereoscoopicOptions() {
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
*/
@@ -1977,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);
setSurface(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()");
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;
}
}
@@ -452,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.
*
@@ -496,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)}.
*
@@ -518,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
@@ -540,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.
*/
@@ -554,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());
}
}
}
@@ -597,9 +587,7 @@ public class UiHelper {
}
private void createSwapChain(@NonNull Surface surface) {
if (mRenderCallback != null) {
mRenderCallback.onNativeWindowChanged(surface);
}
mRenderCallback.onNativeWindowChanged(surface);
mHasSwapChain = true;
}
@@ -607,9 +595,7 @@ public class UiHelper {
if (mRenderSurface != null) {
mRenderSurface.detach();
}
if (mRenderCallback != null) {
mRenderCallback.onDetachedFromSurface();
}
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

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

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.

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

@@ -228,9 +228,8 @@ set(MATERIAL_SRCS
src/materials/dof/dofMipmap.mat
src/materials/dof/dofMedian.mat
src/materials/flare/flare.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/ssao/bilateralBlur.mat
src/materials/ssao/bilateralBlurBentNormals.mat
@@ -242,10 +241,9 @@ set(MATERIAL_SRCS
src/materials/antiAliasing/fxaa.mat
src/materials/antiAliasing/taa.mat
src/materials/vsmMipmap.mat
src/materials/blitLow.mat
)
set(MATERIAL_FL0_SRCS
set(MATERIAL_ES2_SRCS
src/materials/defaultMaterial0.mat
src/materials/skybox0.mat
)
@@ -274,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
# ==================================================================================================
@@ -320,8 +313,8 @@ foreach (mat_src ${MATERIAL_SRCS})
list(APPEND MATERIAL_BINS ${output_path})
endforeach()
if (FILAMENT_ENABLE_FEATURE_LEVEL_0 AND FILAMENT_SUPPORTS_OPENGL)
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")
@@ -562,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
*/
@@ -842,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;
}
};
@@ -867,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
@@ -1180,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,

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

@@ -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;
@@ -150,8 +136,6 @@ struct MetalContext {
MTLViewport currentViewport;
MetalShaderCompiler* shaderCompiler = nullptr;
#if defined(FILAMENT_METAL_PROFILING)
// Logging and profiling.
os_log_t log;

View File

@@ -34,11 +34,11 @@ 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

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,7 +157,6 @@ MetalDriver::~MetalDriver() noexcept {
delete mContext->bufferPool;
delete mContext->blitter;
delete mContext->timerQueryImpl;
delete mContext->shaderCompiler;
delete mContext;
}
@@ -308,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,
@@ -323,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) {
@@ -351,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 };
}
@@ -362,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 };
}
@@ -373,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 };
}
@@ -536,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) {
@@ -573,12 +557,6 @@ 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();
@@ -588,7 +566,6 @@ void MetalDriver::terminate() {
MetalExternalImage::shutdown(*mContext);
mContext->blitter->shutdown();
mContext->shaderCompiler->terminate();
}
ShaderModel MetalDriver::getShaderModel() const noexcept {
@@ -724,7 +701,7 @@ bool MetalDriver::isStereoSupported() {
}
bool MetalDriver::isParallelShaderCompileSupported() {
return true;
return false;
}
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
@@ -812,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,
@@ -877,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 #"
@@ -895,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
@@ -929,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,
};
@@ -954,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));
@@ -962,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);
}
}
@@ -1379,65 +1376,51 @@ void MetalDriver::blit(TargetBufferFlags buffers,
}
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:
@@ -1445,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.
@@ -1478,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 };
@@ -1513,8 +1503,8 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
assert_invariant(isMetalFormatStencil(stencilPixelFormat));
}
MetalPipelineState pipelineState {
.vertexFunction = vertex,
.fragmentFunction = fragment,
.vertexFunction = program->vertexFunction,
.fragmentFunction = program->fragmentFunction,
.vertexDescription = primitive->vertexDescription,
.colorAttachmentPixelFormat = {
colorPixelFormat[0],
@@ -1659,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;
}
@@ -1686,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.
@@ -1729,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];

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

@@ -57,11 +57,8 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) {
}
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, "
@@ -82,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);
}
@@ -172,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) {
@@ -299,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;
@@ -309,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,
@@ -511,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() {
@@ -665,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,
@@ -795,18 +788,18 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
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;
}
@@ -1130,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

@@ -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
@@ -284,7 +265,6 @@ void OpenGLContext::setDefaultState() noexcept {
if (ext.EXT_clip_cull_distance) {
glEnable(GL_CLIP_DISTANCE0);
glEnable(GL_CLIP_DISTANCE1);
}
}

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 = {};

View File

@@ -147,8 +147,8 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
#endif
size_t const defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig {driverConfig};
validConfig.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize);
Platform::DriverConfig const validConfig {
.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize) };
OpenGLDriver* const driver = new OpenGLDriver(ec, validConfig);
return driver;
}
@@ -172,7 +172,7 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
mShaderCompilerService(*this),
mHandleAllocator("Handles", driverConfig.handleArenaSize),
mSamplerMap(32) {
std::fill(mSamplerBindings.begin(), mSamplerBindings.end(), nullptr);
// set a reasonable default value for our stream array
@@ -262,27 +262,21 @@ void OpenGLDriver::bindTexture(GLuint unit, GLTexture const* t) noexcept {
mContext.bindTexture(unit, t->gl.target, t->gl.id, t->gl.targetIndex);
}
bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
if (UTILS_UNLIKELY(!p->isValid())) {
// If the program is not valid, we can't call use().
return false;
}
void OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
// set-up textures and samplers in the proper TMUs (as specified in setSamplers)
p->use(this, mContext);
if (UTILS_UNLIKELY(mContext.isES2())) {
for (uint32_t i = 0; i < Program::UNIFORM_BINDING_COUNT; i++) {
auto [id, buffer, age] = mUniformBindings[i];
auto [buffer, age] = mUniformBindings[i];
if (buffer) {
p->updateUniforms(i, id, buffer, age);
p->updateUniforms(i, buffer, age);
}
}
// Set the output colorspace for this program (linear or rec709). This in only relevant
// when mPlatform.isSRGBSwapChainSupported() is false (no need to check though).
p->setRec709ColorSpace(mRec709OutputColorspace);
}
return true;
}
@@ -485,7 +479,6 @@ void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh,
GLBufferObject* bo = construct<GLBufferObject>(boh, byteCount, bindingType, usage);
if (UTILS_UNLIKELY(bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) {
bo->gl.id = ++mLastAssignedEmulatedUboId;
bo->gl.buffer = malloc(byteCount);
memset(bo->gl.buffer, 0, byteCount);
} else {
@@ -562,8 +555,7 @@ void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t size,
utils::FixedSizeString<32> debugName) {
void OpenGLDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t size) {
DEBUG_MARKER()
construct<GLSamplerGroup>(sbh, size);
@@ -602,9 +594,8 @@ void OpenGLDriver::textureStorage(OpenGLDriver::GLTexture* t,
}
} else {
glTexImage2D(t->gl.target, level, GLint(t->gl.internalFormat),
std::max(GLsizei(1), GLsizei(width >> level)),
std::max(GLsizei(1), GLsizei(height >> level)),
0, format, type, nullptr);
GLsizei(width), GLsizei(height), 0,
format, type, nullptr);
}
}
}
@@ -1298,9 +1289,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
checkDimensions(rt->gl.color[i], color[i].level);
}
}
if (UTILS_LIKELY(!getContext().isES2())) {
glDrawBuffers((GLsizei)maxDrawBuffers, bufs);
}
glDrawBuffers((GLsizei)maxDrawBuffers, bufs);
CHECK_GL_ERROR(utils::slog.e)
}
#endif
@@ -1461,11 +1450,6 @@ void OpenGLDriver::destroySamplerGroup(Handle<HwSamplerGroup> sbh) {
DEBUG_MARKER()
if (sbh) {
GLSamplerGroup* sb = handle_cast<GLSamplerGroup*>(sbh);
for (auto& binding : mSamplerBindings) {
if (binding == sb) {
binding = nullptr;
}
}
destruct(sbh, sb);
}
}
@@ -1474,9 +1458,9 @@ void OpenGLDriver::destroyTexture(Handle<HwTexture> th) {
DEBUG_MARKER()
if (th) {
auto& gl = mContext;
GLTexture* t = handle_cast<GLTexture*>(th);
if (UTILS_LIKELY(!t->gl.imported)) {
auto& gl = mContext;
if (UTILS_LIKELY(t->usage & TextureUsage::SAMPLEABLE)) {
gl.unbindTexture(t->gl.target, t->gl.id);
if (UTILS_UNLIKELY(t->hwStream)) {
@@ -1494,8 +1478,6 @@ void OpenGLDriver::destroyTexture(Handle<HwTexture> th) {
if (t->gl.sidecarRenderBufferMS) {
glDeleteRenderbuffers(1, &t->gl.sidecarRenderBufferMS);
}
} else {
gl.unbindTexture(t->gl.target, t->gl.id);
}
destruct(th, t);
}
@@ -1792,10 +1774,6 @@ bool OpenGLDriver::isRenderTargetFormatSupported(TextureFormat format) {
// support more formats, but it requires querying GL_INTERNALFORMAT_SUPPORTED which is not
// available in OpenGL ES.
auto& gl = mContext;
if (UTILS_UNLIKELY(gl.isES2())) {
auto [es2format, type] = textureFormatToFormatAndType(format);
return es2format != GL_NONE && type != GL_NONE;
}
switch (format) {
// Core formats.
case TextureFormat::R8:
@@ -1975,13 +1953,6 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
GLSwapChain* scRead = handle_cast<GLSwapChain*>(schRead);
mPlatform.makeCurrent(scDraw->swapChain, scRead->swapChain);
mCurrentDrawSwapChain = scDraw;
// From the GL spec for glViewport and glScissor:
// When a GL context is first attached to a window, width and height are set to the
// dimensions of that window.
// So basically, our viewport/scissor can be reset to "something" here.
mContext.state.window.viewport = {};
mContext.state.window.scissor = {};
}
// ------------------------------------------------------------------------------------------------
@@ -2297,16 +2268,8 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
return;
}
GLenum glFormat;
GLenum glType;
if (mContext.isES2()) {
auto formatAndType = textureFormatToFormatAndType(t->format);
glFormat = formatAndType.first;
glType = formatAndType.second;
} else {
glFormat = getFormat(p.format);
glType = getType(p.type);
}
GLenum const glFormat = getFormat(p.format);
GLenum const glType = getType(p.type);
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (!gl.isES2()) {
@@ -2916,11 +2879,7 @@ void OpenGLDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t ind
assert_invariant(offset + size <= ub->byteCount);
if (UTILS_UNLIKELY(ub->bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) {
mUniformBindings[index] = {
ub->gl.id,
static_cast<uint8_t const*>(ub->gl.buffer) + offset,
ub->age,
};
mUniformBindings[index] = { static_cast<uint8_t const*>(ub->gl.buffer) + offset, ub->age };
} else {
GLenum const target = GLUtils::getBufferBindingType(bindingType);
@@ -3537,17 +3496,18 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
DEBUG_MARKER()
auto& gl = mContext;
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(state.program);
OpenGLProgram* p = handle_cast<OpenGLProgram*>(state.program);
bool const success = useProgram(p);
if (UTILS_UNLIKELY(!success)) {
// Avoid fatal (or cascading) errors 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 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(!p->isValid())) {
return;
}
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
useProgram(p);
GLRenderPrimitive* rp = handle_cast<GLRenderPrimitive *>(rph);
// Gracefully do nothing if the render primitive has not been set up.
VertexBufferHandle vb = rp->gl.vertexBufferWithObjects;
@@ -3558,7 +3518,7 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
gl.bindVertexArray(&rp->gl);
// If necessary, mutate the bindings in the VAO.
GLVertexBuffer const* const glvb = handle_cast<GLVertexBuffer*>(vb);
const GLVertexBuffer* glvb = handle_cast<GLVertexBuffer*>(vb);
if (UTILS_UNLIKELY(rp->gl.vertexBufferVersion != glvb->bufferObjectsVersion)) {
updateVertexArrayObject(rp, glvb);
}
@@ -3592,16 +3552,17 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
getShaderCompilerService().tick();
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(program);
OpenGLProgram* p = handle_cast<OpenGLProgram*>(program);
bool const success = useProgram(p);
if (UTILS_UNLIKELY(!success)) {
// Avoid fatal (or cascading) errors 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 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(!p->isValid())) {
return;
}
useProgram(p);
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
#if defined(__ANDROID__)

View File

@@ -83,9 +83,11 @@ public:
}
struct {
GLuint id;
union {
GLenum binding;
struct {
GLuint id;
GLenum binding;
};
void* buffer;
};
} gl;
@@ -318,7 +320,7 @@ private:
void bindTexture(GLuint unit, GLTexture const* t) noexcept;
void bindSampler(GLuint unit, GLuint sampler) noexcept;
inline bool useProgram(OpenGLProgram* p) noexcept;
inline void useProgram(OpenGLProgram* p) noexcept;
enum class ResolveAction { LOAD, STORE };
void resolvePass(ResolveAction action, GLRenderTarget const* rt,
@@ -361,8 +363,7 @@ private:
void setScissor(Viewport const& scissor) noexcept;
// ES2 only. Uniform buffer emulation binding points
GLuint mLastAssignedEmulatedUboId = 0;
std::array<std::tuple<GLuint, void const*, uint16_t>, Program::UNIFORM_BINDING_COUNT> mUniformBindings = {};
std::array<std::pair<void const*, uint16_t>, Program::UNIFORM_BINDING_COUNT> mUniformBindings = {};
// sampler buffer binding points (nullptr if not used)
std::array<GLSamplerGroup*, Program::SAMPLER_BINDING_COUNT> mSamplerBindings = {}; // 4 pointers

View File

@@ -129,7 +129,7 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
#endif
{
// ES2 initialization of (fake) UBOs
UniformsRecord* const uniformsRecords = new(std::nothrow) UniformsRecord[Program::UNIFORM_BINDING_COUNT];
UniformsRecord* const uniformsRecords = new UniformsRecord[Program::UNIFORM_BINDING_COUNT];
UTILS_NOUNROLL
for (GLuint binding = 0, n = Program::UNIFORM_BINDING_COUNT; binding < n; binding++) {
Program::UniformInfo& uniforms = lazyInitializationData.bindingUniformInfo[binding];
@@ -208,11 +208,9 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept {
auto const& UTILS_RESTRICT usedBindingPoints = mUsedSamplerBindingPoints;
for (uint8_t i = 0, tmu = 0, n = mUsedBindingsCount; i < n; i++) {
size_t const binding = usedBindingPoints[i];
assert_invariant(binding < Program::SAMPLER_BINDING_COUNT);
auto const binding = usedBindingPoints[i];
auto const * const sb = samplerBindings[binding];
assert_invariant(sb);
if (!sb) continue; // should never happen, this would be a user error.
for (uint8_t j = 0, m = sb->textureUnitEntries.size(); j < m; ++j, ++tmu) { // "<=" on purpose here
const GLTexture* const t = sb->textureUnitEntries[j].texture;
if (t) { // program may not use all samplers of sampler group
@@ -229,16 +227,15 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept {
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLProgram::updateUniforms(uint32_t index, GLuint id, void const* buffer, uint16_t age) noexcept {
void OpenGLProgram::updateUniforms(uint32_t index, void const* buffer, uint16_t age) noexcept {
assert_invariant(mUniformsRecords);
assert_invariant(buffer);
// only update the uniforms if the UBO has changed since last time we updated
UniformsRecord const& records = mUniformsRecords[index];
if (records.id == id && records.age == age) {
if (records.age == age) {
return;
}
records.id = id;
records.age = age;
assert_invariant(records.uniforms.size() == records.locations.size());

View File

@@ -59,7 +59,7 @@ public:
// - the content of any bound sampler buffer has changed
// ... since last time we used this program
// Turns out the former might be relatively cheap to check, the latter requires
// turns out the former might be relatively cheap to check, the later requires
// a bit less. Compared to what updateSamplers() actually does, which is
// pretty little, I'm not sure if we'll get ahead.
@@ -72,7 +72,7 @@ public:
} gl; // 12 bytes
// For ES2 only
void updateUniforms(uint32_t index, GLuint id, void const* buffer, uint16_t age) noexcept;
void updateUniforms(uint32_t index, void const* buffer, uint16_t age) noexcept;
void setRec709ColorSpace(bool rec709) const noexcept;
private:
@@ -98,7 +98,6 @@ private:
struct UniformsRecord {
Program::UniformInfo uniforms;
LocationInfo locations;
mutable GLuint id = 0;
mutable uint16_t age = std::numeric_limits<uint16_t>::max();
};
UniformsRecord const* mUniformsRecords = nullptr;

View File

@@ -22,6 +22,7 @@
#include <private/backend/BackendUtils.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/Program.h>
#include <utils/compiler.h>
@@ -102,11 +103,6 @@ struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
return programData;
}
void wait() const noexcept {
std::unique_lock l(lock);
cond.wait(l, [this](){ return signaled; });
}
// Checks if the programBinary is ready.
// This is similar to std::future::wait_for(0s)
bool isReady() const noexcept {
@@ -139,7 +135,6 @@ void* ShaderCompilerService::getUserData(const program_token_t& token) noexcept
ShaderCompilerService::ShaderCompilerService(OpenGLDriver& driver)
: mDriver(driver),
mBlobCache(driver.getContext()),
mCallbackManager(driver),
KHR_parallel_shader_compile(driver.getContext().ext.KHR_parallel_shader_compile) {
}
@@ -219,7 +214,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
token->attributes = std::move(program.getAttributes());
}
token->gl.program = mBlobCache.retrieve(&token->key, mDriver.mPlatform, program);
token->gl.program = OpenGLBlobCache::retrieve(&token->key, mDriver.mPlatform, program);
if (token->gl.program) {
return token;
}
@@ -249,10 +244,12 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
// We need to query the link status here to guarantee that the
// program is compiled and linked now (we don't want this to be
// deferred to later). We don't care about the result at this point.
GLint status = GL_FALSE;
GLint status;
glGetProgramiv(glProgram, GL_LINK_STATUS, &status);
programData.program = glProgram;
token->gl.program = programData.program;
// we don't need to check for success here, it'll be done on the
// main thread side.
token->set(programData);
@@ -260,9 +257,9 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
mCallbackManager.put(token->handle);
// caching must be the last thing we do
if (token->key && status == GL_TRUE) {
if (token->key) {
// Attempt to cache. This calls glGetProgramBinary.
mBlobCache.insert(mDriver.mPlatform, token->key, glProgram);
OpenGLBlobCache::insert(mDriver.mPlatform, token->key, glProgram);
}
});
@@ -315,7 +312,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
// do this later, maybe depending on CPU usage?
// attempt to cache if we don't have a thread pool (otherwise it's done
// by the pool).
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
OpenGLBlobCache::insert(mDriver.mPlatform, token->key, token->gl.program);
}
return true;
@@ -334,28 +331,12 @@ GLuint ShaderCompilerService::getProgram(ShaderCompilerService::program_token_t&
return program;
}
void ShaderCompilerService::terminate(program_token_t& token) {
/* static*/ void ShaderCompilerService::terminate(program_token_t& token) {
assert_invariant(token);
token->canceled = true;
bool const canceled = token->compiler.cancelTickOp(token);
if (token->compiler.mShaderCompilerThreadCount) {
auto job = token->compiler.mCompilerThreadPool.dequeue(token);
if (!job) {
// The job is being executed right now. We need to wait for it to finish to avoid a
// race.
token->wait();
} else {
// The job has not been executed, but we still need to inform the callback manager in
// order for future callbacks to be successfully called.
token->compiler.mCallbackManager.put(token->handle);
}
} else if (canceled) {
// Since the tick op was canceled, we need to .put the token here.
token->compiler.mCallbackManager.put(token->handle);
}
token->compiler.cancelTickOp(token);
for (GLuint& shader: token->gl.shaders) {
if (shader) {
@@ -429,7 +410,7 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
mCallbackManager.put(token->handle);
if (token->key) {
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
OpenGLBlobCache::insert(mDriver.mPlatform, token->key, token->gl.program);
}
} else {
// if we don't have a program yet, block until we get it.
@@ -681,7 +662,7 @@ void ShaderCompilerService::runAtNextTick(CompilerPriorityQueue priority,
SYSTRACE_VALUE32("ShaderCompilerService Jobs", mRunAtNextTickOps.size());
}
bool ShaderCompilerService::cancelTickOp(program_token_t token) noexcept {
void ShaderCompilerService::cancelTickOp(program_token_t token) noexcept {
// We do a linear search here, but this is rare, and we know the list is pretty small.
auto& ops = mRunAtNextTickOps;
auto pos = std::find_if(ops.begin(), ops.end(), [&](const auto& item) {
@@ -689,11 +670,9 @@ bool ShaderCompilerService::cancelTickOp(program_token_t token) noexcept {
});
if (pos != ops.end()) {
ops.erase(pos);
return true;
}
SYSTRACE_CONTEXT();
SYSTRACE_VALUE32("ShaderCompilerService Jobs", ops.size());
return false;
}
void ShaderCompilerService::executeTickOps() noexcept {

View File

@@ -21,7 +21,6 @@
#include "CallbackManager.h"
#include "CompilerThreadPool.h"
#include "OpenGLBlobCache.h"
#include <backend/CallbackHandler.h>
#include <backend/Program.h>
@@ -96,7 +95,6 @@ public:
private:
OpenGLDriver& mDriver;
OpenGLBlobCache mBlobCache;
CallbackManager mCallbackManager;
CompilerThreadPool mCompilerThreadPool;
@@ -143,7 +141,7 @@ private:
void runAtNextTick(CompilerPriorityQueue priority,
const program_token_t& token, Job job) noexcept;
void executeTickOps() noexcept;
bool cancelTickOp(program_token_t token) noexcept;
void cancelTickOp(program_token_t token) noexcept;
// order of insertion is important
using ContainerType = std::tuple<CompilerPriorityQueue, program_token_t, Job>;

View File

@@ -190,10 +190,8 @@ using namespace glext;
#if defined(GL_EXT_clip_cull_distance)
# define GL_CLIP_DISTANCE0 GL_CLIP_DISTANCE0_EXT
# define GL_CLIP_DISTANCE1 GL_CLIP_DISTANCE1_EXT
#else
# define GL_CLIP_DISTANCE0 0x3000
# define GL_CLIP_DISTANCE1 0x3001
#endif
#if defined(GL_KHR_debug)

View File

@@ -97,32 +97,13 @@ int PlatformEGL::getOSVersion() const noexcept {
return 0;
}
bool PlatformEGL::isOpenGL() const noexcept {
return false;
}
Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept {
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
assert_invariant(mEGLDisplay != EGL_NO_DISPLAY);
EGLint major, minor;
EGLBoolean initialized = eglInitialize(mEGLDisplay, &major, &minor);
if (!initialized) {
EGLDeviceEXT eglDevice;
EGLint numDevices;
PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT =
(PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT");
if (eglQueryDevicesEXT != nullptr) {
eglQueryDevicesEXT(1, &eglDevice, &numDevices);
if(auto* getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
eglGetProcAddress("eglGetPlatformDisplay"))) {
mEGLDisplay = getPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, eglDevice, 0);
initialized = eglInitialize(mEGLDisplay, &major, &minor);
}
}
}
EGLBoolean const initialized = eglInitialize(mEGLDisplay, &major, &minor);
if (UTILS_UNLIKELY(!initialized)) {
slog.e << "eglInitialize failed" << io::endl;
return nullptr;
@@ -167,16 +148,10 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
constexpr bool requestES2Context = false;
#endif
Config contextAttribs;
if (isOpenGL()) {
// Request a OpenGL 4.1 context
contextAttribs[EGL_CONTEXT_MAJOR_VERSION] = 4;
contextAttribs[EGL_CONTEXT_MINOR_VERSION] = 1;
} else {
// Request a ES2 context, devices that support ES3 will return an ES3 context
contextAttribs[EGL_CONTEXT_CLIENT_VERSION] = 2;
}
// Request a ES2 context, devices that support ES3 will return an ES3 context
Config contextAttribs = {
{ EGL_CONTEXT_CLIENT_VERSION, 2 },
};
// FOR TESTING ONLY, enforce the ES version we're asking for.
// FIXME: we should check EGL_ANGLE_create_context_backwards_compatible, however, at least
@@ -197,17 +172,14 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
// config use for creating the context
EGLConfig eglConfig = EGL_NO_CONFIG_KHR;
// find a config we can use if we don't have "EGL_KHR_no_config_context" and that we can use
// for the dummy pbuffer surface.
mEGLConfig = findSwapChainConfig(0);
if (UTILS_UNLIKELY(mEGLConfig == EGL_NO_CONFIG_KHR)) {
goto error; // error already logged
}
if (UTILS_UNLIKELY(!ext.egl.KHR_no_config_context)) {
// find a config we can use if we don't have "EGL_KHR_no_config_context" and that we can use
// for the dummy pbuffer surface.
mEGLConfig = findSwapChainConfig(
SWAP_CHAIN_CONFIG_TRANSPARENT |
SWAP_CHAIN_HAS_STENCIL_BUFFER,
true, true);
if (UTILS_UNLIKELY(mEGLConfig == EGL_NO_CONFIG_KHR)) {
goto error; // error already logged
}
// if we don't have the EGL_KHR_no_config_context the context must be created with
// the same config as the swapchain, so we have no choice but to create a
// transparent config.
@@ -361,36 +333,22 @@ void PlatformEGL::terminate() noexcept {
eglReleaseThread();
}
EGLConfig PlatformEGL::findSwapChainConfig(uint64_t flags, bool window, bool pbuffer) const {
EGLConfig PlatformEGL::findSwapChainConfig(uint64_t flags) const {
// Find config that support ES3.
EGLConfig config = EGL_NO_CONFIG_KHR;
EGLint configsCount;
Config configAttribs = {
{ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT },
{ EGL_RED_SIZE, 8 },
{ EGL_GREEN_SIZE, 8 },
{ EGL_BLUE_SIZE, 8 },
{ EGL_ALPHA_SIZE, (flags & SWAP_CHAIN_CONFIG_TRANSPARENT) ? 8 : 0 },
{ EGL_DEPTH_SIZE, 24 },
{ EGL_STENCIL_SIZE, (flags & SWAP_CHAIN_HAS_STENCIL_BUFFER) ? 8 : 0 }
};
if (!ext.egl.KHR_no_config_context) {
if (isOpenGL()) {
configAttribs[EGL_RENDERABLE_TYPE] = EGL_OPENGL_BIT;
} else {
configAttribs[EGL_RENDERABLE_TYPE] = EGL_OPENGL_ES2_BIT;
if (ext.egl.KHR_create_context) {
configAttribs[EGL_RENDERABLE_TYPE] |= EGL_OPENGL_ES3_BIT_KHR;
}
}
}
if (window) {
configAttribs[EGL_SURFACE_TYPE] |= EGL_WINDOW_BIT;
}
if (pbuffer) {
configAttribs[EGL_SURFACE_TYPE] |= EGL_PBUFFER_BIT;
if (ext.egl.KHR_create_context) {
configAttribs[EGL_RECORDABLE_ANDROID] |= EGL_OPENGL_ES3_BIT_KHR;
}
if (ext.egl.ANDROID_recordable) {
@@ -433,7 +391,7 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
EGLConfig config = EGL_NO_CONFIG_KHR;
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
config = findSwapChainConfig(flags, true, false);
config = findSwapChainConfig(flags);
} else {
config = mEGLConfig;
}
@@ -469,7 +427,7 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
EGLConfig config = EGL_NO_CONFIG_KHR;
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
config = findSwapChainConfig(flags, false, true);
config = findSwapChainConfig(flags);
} else {
config = mEGLConfig;
}
@@ -611,7 +569,7 @@ EGLint& PlatformEGL::Config::operator[](EGLint name) {
auto pos = std::find_if(mConfig.begin(), mConfig.end(),
[name](auto&& v) { return v.first == name; });
if (pos == mConfig.end()) {
mConfig.insert(pos - 1, { name, 0 });
mConfig.insert(pos - 1, { name, EGL_NONE });
pos = mConfig.end() - 2;
}
return pos->second;

View File

@@ -185,7 +185,7 @@ AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) n
AcquiredImage acquiredImage;
EGLDisplay display;
};
Closure* closure = new(std::nothrow) Closure(source, mEGLDisplay);
Closure* closure = new Closure(source, mEGLDisplay);
auto patchedCallback = [](void* image, void* userdata) {
Closure* closure = (Closure*)userdata;
if (eglDestroyImageKHR(closure->display, (EGLImageKHR) image) == EGL_FALSE) {

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