Compare commits

..
Author SHA1 Message Date
Powei Feng 962abb7e8d material-parser: refactor computing, caching of crc32
- Make the two ways to get crc32 explicitly documented.
 - Ensure the slow path of computing crc32 from scratch is only
   happens on init, either
     - Creating MaterialParser for validation
     - Or, using crc32 val as the key for the first time in
       MaterialCache
 - In doing the above, move crc32 validation to
   MaterialDefinition::createParser
2026-03-06 11:26:06 -08:00
177 changed files with 1463 additions and 2792 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ runs:
uses: actions/cache@v4
with:
path: mesa
key: ${{ runner.os }}-${{ runner.arch }}-mesa-deps-${{ env.GITHUB_MESA_VERSION }}
key: ${{ runner.os }}-mesa-deps-${{ env.GITHUB_MESA_VERSION }}
- name: Get Mesa
run: |
bash build/common/get-mesa.sh
+1 -2
View File
@@ -9,7 +9,7 @@ runs:
id: cache-vulkan-sdk
with:
path: ~/VulkanSDK
key: vulkansdk-${{ env.GITHUB_VULKANSDK_VERSION }}-2-${{ runner.os }}-${{ runner.arch }}
key: vulkansdk-${{ env.GITHUB_VULKANSDK_VERSION }}-2-${{ runner.os }}
- name: Download Vulkan SDK
if: steps.cache-vulkan-sdk.outputs.cache-hit != 'true'
run: |
@@ -23,7 +23,6 @@ runs:
unpack_vulkan_installer
shell: bash
- name: Run Vulkan SDK setup
if: runner.os != 'Linux'
run: |
pushd .
cd ~/VulkanSDK/${GITHUB_VULKANSDK_VERSION}
+12 -1
View File
@@ -11,13 +11,24 @@ runs:
echo "set man-db/auto-update false" | sudo debconf-communicate
sudo dpkg-reconfigure man-db
# Install ninja
source ./build/common/get-ninja.sh
# Install CMake
mkdir -p cmake
cd cmake
sudo wget https://github.com/Kitware/CMake/releases/download/v$GITHUB_CMAKE_VERSION/cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh
sudo chmod +x ./cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh
sudo ./cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh --skip-license > /dev/null
sudo update-alternatives --install /usr/bin/cmake cmake $(pwd)/bin/cmake 1000 --force
cd ..
sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install clang-$GITHUB_CLANG_VERSION libc++-$GITHUB_CLANG_VERSION-dev libc++abi-$GITHUB_CLANG_VERSION-dev
sudo apt-get install mesa-common-dev libxi-dev libxxf86vm-dev
sudo apt-get install cmake ninja-build
# For dawn
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libx11-xcb-dev
@@ -8,15 +8,9 @@ runs:
- uses: ./.github/actions/get-mesa
- uses: ./.github/actions/get-vulkan-sdk
- name: Prerequisites
if: runner.os == 'macOS'
run: |
# Must have at least clang-16 for a webgpu/dawn build.
sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
# this enforces the gltf_viewer build to use apple clang as oppose the brew clang used for get-mesa
echo "CC=/usr/bin/clang" >> $GITHUB_ENV
echo "CXX=/usr/bin/clang++" >> $GITHUB_ENV
echo "/usr/local/bin" >> $GITHUB_PATH
echo "/usr/bin" >> $GITHUB_PATH
shell: bash
- name: Generate images
run: |
@@ -1,4 +1,4 @@
FROM python:3.12.13
FROM python:3.10.4
RUN pip3 install pygithub==1.55
+2 -2
View File
@@ -1,4 +1,4 @@
name: 'Web Prerequisites'
name: 'Web Preqrequisites'
runs:
using: "composite"
steps:
@@ -8,7 +8,7 @@ runs:
uses: actions/cache@v4 # Use a specific version
with:
path: emsdk
key: ${{ runner.os }}-${{ runner.arch }}-emsdk-${{ env.GITHUB_EMSDK_VERSION }}
key: ${{ runner.os }}-emsdk-${{ env.GITHUB_EMSDK_VERSION }}
- name: Install Web Prerequisites
shell: bash
run: |
+2 -2
View File
@@ -10,7 +10,7 @@ jobs:
# a branch on filament-assets.
update-renderdiff-goldens:
name: update-renderdiff-goldens
runs-on: 'macos-14'
runs-on: 'macos-14-xlarge'
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -90,7 +90,7 @@ jobs:
# filament-assets
update-sizeguard:
name: update-sizeguard
runs-on: 'ubuntu-24.04-4core'
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
with:
+16 -8
View File
@@ -12,7 +12,8 @@ jobs:
name: build-android
# We intentially use a larger runner here to enable larger disk space
# (standard linux runner will fail on disk space and faster build time).
runs-on: 'ubuntu-24.04-4core'
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -25,7 +26,8 @@ jobs:
build-ios:
name: build-ios
runs-on: 'macos-14'
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -44,7 +46,8 @@ jobs:
build-linux:
name: build-linux
runs-on: 'arm-ubuntu-24.04-16core'
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -55,12 +58,13 @@ jobs:
cd build/linux && printf "y" | ./build.sh continuous
- uses: actions/upload-artifact@v4
with:
name: filament-arm-linux
path: out/filament-release-arm-linux.tgz
name: filament-linux
path: out/filament-release-linux.tgz
build-mac:
name: build-mac
runs-on: 'macos-14'
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -73,10 +77,14 @@ jobs:
with:
name: filament-mac
path: out/filament-release-darwin.tgz
- name: Check public headers
run: |
test/check-headers/test.sh out/release/filament/include
build-web:
name: build-web
runs-on: 'arm-ubuntu-24.04-16core'
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -93,7 +101,7 @@ jobs:
build-windows:
name: build-windows
runs-on: 'windows-2022'
runs-on: windows-2022-32core
steps:
- uses: actions/checkout@v4.1.6
+17 -178
View File
@@ -8,54 +8,10 @@ on:
branches:
- main
# This will cancel in-flight runs when there is an update to a PR
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# The conditional 'if' on each job is meant to skip presubmit jobs when a commit is pushed to main
# and that commit is cryptographically "verified". Typically, a verified commit (like a GitHub UI
# squash-and-merge) has already passed through presubmit during the Pull Request phase.
# The conditional explicitly checks:
# 1. always() && !cancelled(): Ensures the job runs even if 'check-verification' is skipped, but
# aborts if the workflow was manually cancelled.
# 2. github.event_name == 'pull_request': Presubmits should always run normally on PRs.
# 3. github.event_name == 'push' && ...: If it's a push to main, it only runs if the
# 'check-verification' job confirmed the commit is NOT verified.
jobs:
check-verification:
if: github.event_name == 'push'
runs-on: ubuntu-latest
outputs:
verified: ${{ steps.check.outputs.verified }}
steps:
- name: Check commit verification
id: check
uses: actions/github-script@v7
with:
script: |
const { data } = await github.rest.repos.getCommit({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.sha
});
core.setOutput('verified', data.commit.verification.verified);
build-desktop-mac:
name: build-mac
runs-on: 'macos-14-xlarge'
needs: [check-verification]
if: >
always() && !cancelled() &&
(
github.event_name == 'pull_request' ||
(
github.event_name == 'push' &&
needs.check-verification.result == 'success' &&
needs.check-verification.outputs.verified == 'false'
)
)
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -64,30 +20,13 @@ jobs:
- name: Run build script
run: |
cd build/mac && printf "y" | ./build.sh presubmit-with-test
- name: Test - material parser
- name: Test material parser
run: |
out/cmake-release/filament/test/test_material_parser
- name: Test - public headers
run: |
# out/cmake-release should have the artifacts ready for installation. Here we install it
# to test the public headers
ninja -C out/cmake-release install
test/check-headers/test.sh out/release/filament/include
build-desktop-linux:
name: build-linux
runs-on: 'arm-ubuntu-24.04-16core'
needs: [check-verification]
if: >
always() && !cancelled() &&
(
github.event_name == 'pull_request' ||
(
github.event_name == 'push' &&
needs.check-verification.result == 'success' &&
needs.check-verification.outputs.verified == 'false'
)
)
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -96,24 +35,13 @@ jobs:
- name: Run build script
run: |
cd build/linux && printf "y" | ./build.sh presubmit
- name: Test - material parser
- name: Test material parser
run: |
out/cmake-release/filament/test/test_material_parser
build-windows:
name: build-windows
runs-on: 'windows-2022'
needs: [check-verification]
if: >
always() && !cancelled() &&
(
github.event_name == 'pull_request' ||
(
github.event_name == 'push' &&
needs.check-verification.result == 'success' &&
needs.check-verification.outputs.verified == 'false'
)
)
runs-on: windows-2022-32core
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -125,18 +53,7 @@ jobs:
build-android:
name: build-android
runs-on: 'ubuntu-24.04-4core'
needs: [check-verification]
if: >
always() && !cancelled() &&
(
github.event_name == 'pull_request' ||
(
github.event_name == 'push' &&
needs.check-verification.result == 'success' &&
needs.check-verification.outputs.verified == 'false'
)
)
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -163,18 +80,7 @@ jobs:
build-ios:
name: build-iOS
runs-on: 'macos-14-xlarge'
needs: [check-verification]
if: >
always() && !cancelled() &&
(
github.event_name == 'pull_request' ||
(
github.event_name == 'push' &&
needs.check-verification.result == 'success' &&
needs.check-verification.outputs.verified == 'false'
)
)
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -189,18 +95,7 @@ jobs:
build-web:
name: build-web
runs-on: 'arm-ubuntu-24.04-16core'
needs: [check-verification]
if: >
always() && !cancelled() &&
(
github.event_name == 'pull_request' ||
(
github.event_name == 'push' &&
needs.check-verification.result == 'success' &&
needs.check-verification.outputs.verified == 'false'
)
)
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -213,18 +108,7 @@ jobs:
validate-docs:
name: validate-docs
runs-on: 'ubuntu-24.04'
needs: [check-verification]
if: >
always() && !cancelled() &&
(
github.event_name == 'pull_request' ||
(
github.event_name == 'push' &&
needs.check-verification.result == 'success' &&
needs.check-verification.outputs.verified == 'false'
)
)
runs-on: 'ubuntu-24.04-4core'
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -237,18 +121,7 @@ jobs:
test-renderdiff:
name: test-renderdiff
runs-on: 'macos-14-xlarge'
needs: [check-verification]
if: >
always() && !cancelled() &&
(
github.event_name == 'pull_request' ||
(
github.event_name == 'push' &&
needs.check-verification.result == 'success' &&
needs.check-verification.outputs.verified == 'false'
)
)
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -266,10 +139,10 @@ jobs:
echo "accept=false" >> "$GITHUB_OUTPUT"
fi
shell: bash
- name: Renderdiff generate
- name: Renderdiff Generate
if: steps.check_accept.outputs.accept != 'true'
uses: ./.github/actions/renderdiff-generate
- name: Compare rendered images
- name: Render and compare
if: steps.check_accept.outputs.accept != 'true'
id: render_compare
env:
@@ -301,13 +174,12 @@ jobs:
echo "$DELIMITER" >> "$GITHUB_OUTPUT"
fi
shell: bash
- name: Upload artifacts
uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v4
if: steps.check_accept.outputs.accept != 'true'
with:
name: presubmit-renderdiff-result
path: ./out/renderdiff
- name: Check results
- name: Compare result
if: steps.check_accept.outputs.accept != 'true'
run: |
ERROR_STR="${{ steps.render_compare.outputs.err }}"
@@ -316,20 +188,9 @@ jobs:
exit 1
fi
build-wgsl-webgpu:
validate-wgsl-webgpu:
name: validate-wgsl-webgpu
runs-on: 'arm-ubuntu-24.04-16core'
needs: [check-verification]
if: >
always() && !cancelled() &&
(
github.event_name == 'pull_request' ||
(
github.event_name == 'push' &&
needs.check-verification.result == 'success' &&
needs.check-verification.outputs.verified == 'false'
)
)
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -343,17 +204,6 @@ jobs:
test-code-correctness:
name: test-code-correctness
runs-on: 'macos-14-xlarge'
needs: [check-verification]
if: >
always() && !cancelled() &&
(
github.event_name == 'pull_request' ||
(
github.event_name == 'push' &&
needs.check-verification.result == 'success' &&
needs.check-verification.outputs.verified == 'false'
)
)
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -374,18 +224,7 @@ jobs:
test-backend:
name: test-backend
runs-on: 'macos-14-xlarge'
needs: [check-verification]
if: >
always() && !cancelled() &&
(
github.event_name == 'pull_request' ||
(
github.event_name == 'push' &&
needs.check-verification.result == 'success' &&
needs.check-verification.outputs.verified == 'false'
)
)
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
with:
+1 -34
View File
@@ -63,39 +63,6 @@ jobs:
const globber = await glob.create('out/*.tgz');
await upload({ github, context }, await globber.glob(), TAG);
build-arm-linux:
name: build-arm-linux
runs-on: 'arm-ubuntu-24.04-16core'
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
steps:
- name: Decide Git ref
id: git_ref
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- uses: ./.github/actions/linux-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
cd build/linux && printf "y" | ./build.sh release
cd ../..
mv out/filament-release-linux.tgz out/filament-${TAG}-arm-linux.tgz
- uses: actions/github-script@v6
env:
TAG: ${{ steps.git_ref.outputs.tag }}
with:
script: |
const upload = require('./build/common/upload-release-assets');
const { TAG } = process.env;
const globber = await glob.create('out/*.tgz');
await upload({ github, context }, await globber.glob(), TAG);
build-mac:
name: build-mac
runs-on: macos-14-xlarge
@@ -132,7 +99,7 @@ jobs:
build-web:
name: build-web
runs-on: 'arm-ubuntu-24.04-16core'
runs-on: 'ubuntu-24.04-16core'
if: github.event_name == 'release' || github.event.inputs.platform == 'web'
steps:
+7 -4
View File
@@ -11,11 +11,14 @@ jobs:
name: Verify Release Notes
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
# We *only* need to check out the .github/ directory.
# The verify RELEASE_NOTES script uses the GitHub API to verify that RELEASE_NOTES was
# modified.
- name: Check out action
uses: Bhacaz/checkout-files@73e17cfbe8d7e0c6b2672b20cb05a718e20d18d4
with:
fetch-depth: 0
sparse-checkout: |
.github
files: .github
token: ${{ secrets.GITHUB_TOKEN }}
- name: Verify release notes
uses: ./.github/actions/verify-release-notes
with:
+3 -10
View File
@@ -326,13 +326,11 @@ endif()
if (FILAMENT_ENABLE_LTO)
include(CheckIPOSupported)
check_ipo_supported(RESULT IPO_SUPPORT OUTPUT IPO_ERROR)
check_ipo_supported(RESULT IPO_SUPPORT)
if (IPO_SUPPORT)
message(STATUS "IPO / LTO is enabled")
message(STATUS "LTO support is enabled")
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
else()
message(WARNING "IPO / LTO is not supported by this architecture: ${IPO_ERROR}")
endif()
endif()
@@ -869,12 +867,6 @@ endfunction()
# Sub-projects
# ==================================================================================================
include(CheckSymbolExists)
check_symbol_exists(getopt_long "getopt.h" HAS_SYSTEM_GETOPT)
if (NOT HAS_SYSTEM_GETOPT)
add_subdirectory(${EXTERNAL}/getopt)
endif()
# Common to all platforms
add_subdirectory(${EXTERNAL}/libgtest/tnt)
add_subdirectory(${LIBRARIES}/camutils)
@@ -910,6 +902,7 @@ add_subdirectory(${EXTERNAL}/cgltf/tnt)
add_subdirectory(${EXTERNAL}/draco/tnt)
add_subdirectory(${EXTERNAL}/jsmn/tnt)
add_subdirectory(${EXTERNAL}/stb/tnt)
add_subdirectory(${EXTERNAL}/getopt)
add_subdirectory(${EXTERNAL}/perfetto/tnt)
add_subdirectory(${EXTERNAL}/basisu/tnt)
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.70.2'
implementation 'com.google.android.filament:filament-android:1.69.5'
}
```
@@ -50,7 +50,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.70.2'
pod 'Filament', '~> 1.69.5'
```
## Documentation
-6
View File
@@ -7,12 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.70.2
## v1.70.1
## v1.70.0
- engine: fix crash when using variance shadow maps
@@ -61,7 +61,6 @@ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size
add_library(filament-utils-jni SHARED
src/main/cpp/AutomationEngine.cpp
src/main/cpp/Bookmark.cpp
src/main/cpp/DeviceUtils.cpp
src/main/cpp/HDRLoader.cpp
src/main/cpp/IBLPrefilterContext.cpp
src/main/cpp/Utils.cpp
@@ -1,85 +0,0 @@
/*
* Copyright (C) 2026 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 <jni.h>
#include <filament/Engine.h>
#include <backend/Platform.h>
#include <utils/CString.h>
#include <algorithm>
#include <array>
using namespace filament;
namespace {
constexpr std::array<backend::Platform::DeviceInfoType, 3> VULKAN_INFO = {
backend::Platform::DeviceInfoType::VULKAN_DEVICE_NAME,
backend::Platform::DeviceInfoType::VULKAN_DRIVER_NAME,
backend::Platform::DeviceInfoType::VULKAN_DRIVER_INFO,
};
constexpr std::array<backend::Platform::DeviceInfoType, 3> GL_INFO = {
backend::Platform::DeviceInfoType::OPENGL_VENDOR,
backend::Platform::DeviceInfoType::OPENGL_RENDERER,
backend::Platform::DeviceInfoType::OPENGL_VERSION,
};
} // namespace
extern "C" JNIEXPORT jstring JNICALL
Java_com_google_android_filament_utils_DeviceUtils_nGetGpuDriverInfo(JNIEnv* env, jclass,
jlong nativeEngine) {
auto emptyStr = [env]() { return env->NewStringUTF(""); };
Engine* engine = (Engine*) nativeEngine;
if (!engine) {
return emptyStr();
}
backend::Platform* platform = engine->getPlatform();
if (!platform) {
return emptyStr();
}
std::array<backend::Platform::DeviceInfoType, 3> infoTypes;
switch (engine->getBackend()) {
case backend::Backend::VULKAN:
infoTypes = VULKAN_INFO;
break;
case backend::Backend::OPENGL:
infoTypes = GL_INFO;
break;
default:
return emptyStr();
}
backend::Driver* driver = const_cast<backend::Driver*>(engine->getDriver());
utils::CString fullInfo;
std::for_each(infoTypes.begin(), infoTypes.end(),
[&](backend::Platform::DeviceInfoType infoType) {
utils::CString const newInfo = platform->getDeviceInfo(infoType, driver);
if (!newInfo.empty()) {
if (!fullInfo.empty()) {
fullInfo += " | ";
}
fullInfo += newInfo.c_str();
}
});
return env->NewStringUTF(fullInfo.c_str());
}
@@ -20,6 +20,8 @@
#include <imagediff/ImageDiff.h>
#include <utils/Log.h>
#include <vector>
using namespace imagediff;
using namespace utils;
@@ -100,48 +102,30 @@ jobject createResult(JNIEnv* env, ImageDiffResult const& result, bool generateDi
if (generateDiff && result.diffImage.getWidth() > 0) {
jclass bitmapClass = env->FindClass("android/graphics/Bitmap");
jmethodID createBitmap = env->GetStaticMethodID(bitmapClass, "createBitmap",
jmethodID createBitmap = env->GetStaticMethodID(bitmapClass, "createBitmap",
"(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
jclass configClass = env->FindClass("android/graphics/Bitmap$Config");
jfieldID argb8888 = env->GetStaticFieldID(configClass, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");
jobject configObj = env->GetStaticObjectField(configClass, argb8888);
uint32_t width = result.diffImage.getWidth();
uint32_t height = result.diffImage.getHeight();
jobject diffBitmap = env->CallStaticObjectMethod(bitmapClass, createBitmap, (jint) width,
(jint) height, configObj);
jobject diffBitmap = env->CallStaticObjectMethod(bitmapClass, createBitmap, (jint)width, (jint)height, configObj);
if (diffBitmap) {
// We need to transport the bit differences accurately to the java side, so set
// premultiplied to false. From the java-side, if the bitmap is used to draw to a
// canvas, then client needs to set premultiplied to true again.
jmethodID setPremultiplied = env->GetMethodID(bitmapClass, "setPremultiplied", "(Z)V");
if (setPremultiplied) {
env->CallVoidMethod(diffBitmap, setPremultiplied, JNI_FALSE);
}
void* diffPixels;
if (AndroidBitmap_lockPixels(env, diffBitmap, &diffPixels) == 0) {
AndroidBitmapInfo info;
AndroidBitmap_getInfo(env, diffBitmap, &info);
float const* src = result.diffImage.getPixelRef();
uint8_t* dst = (uint8_t*) diffPixels;
uint32_t const channels = result.diffImage.getChannels(); // usually 4
for (size_t y = 0; y < height; ++y) {
uint8_t* row = dst + y * info.stride;
for (size_t x = 0; x < width; ++x) {
size_t srcIdx = (y * width + x) * channels;
for (int c = 0; c < 4; ++c) {
float v = 0.0f;
if (c < channels) v = src[srcIdx + c];
if (c == 3 && channels < 4) v = 1.0f; // Alpha 1.0 if missing
row[x * 4 + c] = uint8_t(
std::min(255.0f, std::max(0.0f, std::round(v * 255.0f))));
}
uint32_t channels = result.diffImage.getChannels(); // usually 4
for (size_t i = 0; i < width * height; ++i) {
for (int c = 0; c < 4; ++c) {
float v = 0.0f;
if (c < channels) v = src[i * channels + c];
if (c == 3 && channels < 4) v = 1.0f; // Alpha 1.0 if missing
dst[i * 4 + c] = (uint8_t) std::min(255.0f, std::max(0.0f, v * 255.0f));
}
}
AndroidBitmap_unlockPixels(env, diffBitmap);
@@ -149,7 +133,7 @@ jobject createResult(JNIEnv* env, ImageDiffResult const& result, bool generateDi
}
}
}
return resultObj;
}
@@ -163,7 +147,7 @@ Java_com_google_android_filament_utils_ImageDiff_nCompareBasic(JNIEnv* env, jcla
BitmapLock maskArg(env, maskBitmap);
if (!refArg.isValid() || !candArg.isValid()) {
ImageDiffResult emptyResult;
ImageDiffResult emptyResult;
emptyResult.status = ImageDiffResult::Status::SIZE_MISMATCH; // or ERROR
return createResult(env, emptyResult, false);
}
@@ -191,13 +175,13 @@ Java_com_google_android_filament_utils_ImageDiff_nCompareBasic(JNIEnv* env, jcla
extern "C" JNIEXPORT jobject JNICALL
Java_com_google_android_filament_utils_ImageDiff_nCompareJson(JNIEnv* env, jclass,
jobject refBitmap, jobject candBitmap, jstring jsonConfig, jobject maskBitmap) {
BitmapLock refArg(env, refBitmap);
BitmapLock candArg(env, candBitmap);
BitmapLock maskArg(env, maskBitmap);
if (!refArg.isValid() || !candArg.isValid()) {
ImageDiffResult emptyResult;
ImageDiffResult emptyResult;
emptyResult.status = ImageDiffResult::Status::SIZE_MISMATCH; // or ERROR
return createResult(env, emptyResult, false);
}
@@ -205,7 +189,7 @@ Java_com_google_android_filament_utils_ImageDiff_nCompareJson(JNIEnv* env, jclas
ImageDiffConfig config;
const char* nativeJson = env->GetStringUTFChars(jsonConfig, 0);
size_t length = env->GetStringUTFLength(jsonConfig);
bool parsed = parseConfig(nativeJson, length, &config);
env->ReleaseStringUTFChars(jsonConfig, nativeJson);
@@ -230,3 +214,4 @@ Java_com_google_android_filament_utils_ImageDiff_nCompareJson(JNIEnv* env, jclas
return createResult(env, result, generateDiff);
}
@@ -1,26 +0,0 @@
/*
* Copyright (C) 2026 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.
*/
package com.google.android.filament.utils;
import com.google.android.filament.Engine;
public class DeviceUtils {
public static String getGpuDriverInfo(Engine engine) {
return nGetGpuDriverInfo(engine.getNativeObject());
}
private static native String nGetGpuDriverInfo(long nativeEngine);
}
@@ -266,35 +266,6 @@ class ModelViewer(
}
}
/**
* Resets the model's transform, animation, and camera state to defaults.
* Call this when reusing the same model across multiple tests.
*/
fun resetToDefaultState() {
// 1. Reset Camera parameters
cameraFocalLength = 28f
cameraNear = kNearPlane
cameraFar = kFarPlane
updateCameraProjection()
// 2. Reset the manipulator's look-at vectors to initial state
cameraManipulator?.let { cm ->
cm.jumpToBookmark(cm.homeBookmark)
}
// 3. Reset Animations
animator?.let {
if (it.animationCount > 0) {
it.applyAnimation(0, 0.0f)
}
it.updateBoneMatrices()
}
// 4. Re-apply the unit cube transform to clear custom scaling/translation
clearRootTransform()
transformToUnitCube()
}
/**
* Frees all entities associated with the most recently-loaded model.
*/
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.70.2
VERSION_NAME=1.69.5
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -30,11 +30,11 @@
"view": {
"postProcessingEnabled": true,
"dithering": "NONE"
},
"tolerance": {
"maxAbsDiff": 0.1,
"maxFailingPixelsFraction": 0.0
}
},
"tolerance": {
"maxAbsDiff": 0.1,
"maxFailingPixelsFraction": 0.0
}
},
{
@@ -66,26 +66,10 @@ class MainActivity : Activity(), ValidationRunner.Callback {
private lateinit var inputManager: ValidationInputManager
private var currentInput: ValidationInputManager.ValidationInput? = null
private var currentAlphaDiffBitmap: Bitmap? = null
private var globalEnhancementFactor: Float = 1.0f
private data class TestImages(
val testName: String,
val golden: Bitmap?,
val rendered: Bitmap?,
val diff: Bitmap?,
val alphaDiff: Bitmap?
)
private val diffImageViews = mutableListOf<ImageView>()
// UI Elements
private lateinit var runButton: Button
private lateinit var loadButton: Button
private lateinit var optionsButton: Button
private lateinit var enhancementContainer: LinearLayout
private lateinit var enhancementLabel: TextView
private lateinit var enhancementSlider: android.widget.SeekBar
private var resultManager: ValidationResultManager? = null
private var validationRunner: ValidationRunner? = null
@@ -114,19 +98,6 @@ class MainActivity : Activity(), ValidationRunner.Callback {
runButton = findViewById(R.id.run_button)
loadButton = findViewById(R.id.load_button)
optionsButton = findViewById(R.id.options_button)
enhancementContainer = findViewById(R.id.enhancement_container)
enhancementLabel = findViewById(R.id.enhancement_label)
enhancementSlider = findViewById(R.id.enhancement_slider)
enhancementSlider.setOnSeekBarChangeListener(object : android.widget.SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: android.widget.SeekBar?, progress: Int, fromUser: Boolean) {
globalEnhancementFactor = 1.0f + (progress / 100f) * 49.0f
enhancementLabel.text = String.format(Locale.US, "Enhancement: %.1fx", globalEnhancementFactor)
applyGlobalEnhancement()
}
override fun onStartTrackingTouch(seekBar: android.widget.SeekBar?) {}
override fun onStopTrackingTouch(seekBar: android.widget.SeekBar?) {}
})
// Setup Run Button
runButton.setOnClickListener {
@@ -149,7 +120,6 @@ class MainActivity : Activity(), ValidationRunner.Callback {
popup.menu.add(0, 3, 0, "Export Result")
popup.menu.add(0, 4, 0, "Test ADB Info")
popup.menu.add(0, 5, 0, "Result ADB Info")
popup.menu.add(0, 6, 0, "Toggle Enhancement Slider")
popup.setOnMenuItemClickListener { item ->
when (item.itemId) {
@@ -163,9 +133,6 @@ class MainActivity : Activity(), ValidationRunner.Callback {
3 -> exportTestResultsAction()
4 -> showTestAdbInfo()
5 -> showResultAdbInfo()
6 -> {
enhancementContainer.visibility = if (enhancementContainer.visibility == View.VISIBLE) View.GONE else View.VISIBLE
}
}
true
}
@@ -175,7 +142,7 @@ class MainActivity : Activity(), ValidationRunner.Callback {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
choreographer = Choreographer.getInstance()
modelViewer = ModelViewer(surfaceView=surfaceView, manipulator=null)
modelViewer = ModelViewer(surfaceView)
inputManager = ValidationInputManager(this)
// Initialize IBL
@@ -289,7 +256,6 @@ class MainActivity : Activity(), ValidationRunner.Callback {
// Clear existing results UI and state
resultsContainer.removeAllViews()
diffImageViews.clear()
resultManager = null
val newInput = ValidationInputManager.ValidationInput(
@@ -368,29 +334,15 @@ class MainActivity : Activity(), ValidationRunner.Callback {
}
}
private fun createResultManager(outputDir: File): ValidationResultManager {
val gpuDriverInfo = com.google.android.filament.utils.DeviceUtils.getGpuDriverInfo(modelViewer.engine)
return ValidationResultManager(
outputDir = outputDir,
gpuDriverInfo = gpuDriverInfo,
deviceName = android.os.Build.MODEL,
deviceCodeName = android.os.Build.DEVICE,
androidVersion = android.os.Build.VERSION.RELEASE,
androidBuildNumber = android.os.Build.DISPLAY
)
}
private fun startValidation(input: ValidationInputManager.ValidationInput) {
try {
resultsContainer.removeAllViews()
diffImageViews.clear()
enhancementSlider.isEnabled = false
Log.i(TAG, "Starting validation with config: ${input.config.name}")
Log.i(TAG, "Output dir: ${input.outputDir.absolutePath}")
testResultsHeader.text = "${input.config.name}"
resultManager = createResultManager(input.outputDir)
resultManager = ValidationResultManager(input.outputDir)
validationRunner = ValidationRunner(this, modelViewer, input.config, resultManager!!)
validationRunner?.callback = this
@@ -468,15 +420,7 @@ class MainActivity : Activity(), ValidationRunner.Callback {
val imagesRow = LinearLayout(this)
imagesRow.orientation = LinearLayout.HORIZONTAL
val testImages = TestImages(
testName = result.testName,
golden = currentGoldenBitmap,
rendered = currentRenderedBitmap,
diff = currentDiffBitmap,
alphaDiff = currentAlphaDiffBitmap
)
fun addImage(label: String, bitmap: Bitmap?, isDiff: Boolean) {
fun addImage(label: String, bitmap: Bitmap?) {
if (bitmap != null) {
val container = LinearLayout(this)
container.orientation = LinearLayout.VERTICAL
@@ -492,29 +436,16 @@ class MainActivity : Activity(), ValidationRunner.Callback {
iv.layoutParams = LinearLayout.LayoutParams(250, 250) // Smaller thumbnails
iv.scaleType = ImageView.ScaleType.FIT_CENTER
iv.setBackgroundColor(0xFF404040.toInt())
if (isDiff) {
diffImageViews.add(iv)
applyEnhancementToView(iv, globalEnhancementFactor)
}
iv.setOnClickListener {
showImageDialog(testImages, label)
}
container.addView(iv)
imagesRow.addView(container)
}
}
addImage("Rendered", currentRenderedBitmap, false)
addImage("Golden", currentGoldenBitmap, false)
addImage("Rendered", currentRenderedBitmap)
addImage("Golden", currentGoldenBitmap)
if (!result.passed) {
addImage("Diff", currentDiffBitmap, true)
}
if (currentAlphaDiffBitmap != null) {
addImage("Alpha Diff", currentAlphaDiffBitmap, true)
addImage("Diff", currentDiffBitmap)
}
resultContainer.addView(imagesRow)
@@ -524,14 +455,12 @@ class MainActivity : Activity(), ValidationRunner.Callback {
currentRenderedBitmap = null
currentGoldenBitmap = null
currentDiffBitmap = null
currentAlphaDiffBitmap = null
}
}
override fun onAllTestsFinished() {
runOnUiThread {
statusTextView.text = "All tests finished!"
enhancementSlider.isEnabled = true
Log.i(TAG, "All tests finished " + if (currentInput?.autoExport == true) "Exporting bundle" else "x")
if (currentInput?.autoExport == true) {
@@ -546,7 +475,7 @@ class MainActivity : Activity(), ValidationRunner.Callback {
private fun exportTestBundleAction() {
currentInput?.let { input ->
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
val rm = resultManager ?: createResultManager(input.outputDir)
val rm = resultManager ?: ValidationResultManager(input.outputDir)
val zip = rm.exportTestBundle(input.config, timestamp)
if (zip != null) {
val msg = "Exported Bundle: ${zip.name}"
@@ -562,7 +491,7 @@ class MainActivity : Activity(), ValidationRunner.Callback {
private fun exportTestResultsAction() {
currentInput?.let { input ->
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
val rm = resultManager ?: createResultManager(input.outputDir)
val rm = resultManager ?: ValidationResultManager(input.outputDir)
val zip = rm.exportTestResults(input.sourceZip, timestamp)
if (zip != null) {
val msg = "Exported Results: ${zip.name}"
@@ -594,165 +523,7 @@ class MainActivity : Activity(), ValidationRunner.Callback {
"Diff" -> {
currentDiffBitmap = bitmap
}
"Alpha Diff" -> {
currentAlphaDiffBitmap = bitmap
}
}
}
}
private fun applyEnhancementToView(iv: ImageView, factor: Float) {
val cm = android.graphics.ColorMatrix()
cm.setScale(factor, factor, factor, 1.0f)
iv.colorFilter = android.graphics.ColorMatrixColorFilter(cm)
}
private fun applyGlobalEnhancement() {
for (iv in diffImageViews) {
applyEnhancementToView(iv, globalEnhancementFactor)
}
}
private fun showImageDialog(images: TestImages, initialLabel: String) {
val dialogView = layoutInflater.inflate(R.layout.dialog_image_viewer, null)
val dialog = AlertDialog.Builder(this)
.setView(dialogView)
.create()
val titleView = dialogView.findViewById<TextView>(R.id.dialog_title)
val typeView = dialogView.findViewById<TextView>(R.id.dialog_image_type)
val imageView = dialogView.findViewById<ImageView>(R.id.dialog_image)
val btnClose = dialogView.findViewById<View>(R.id.btn_close)
val btnReset = dialogView.findViewById<View>(R.id.btn_reset)
val btnPrev = dialogView.findViewById<View>(R.id.btn_prev)
val btnNext = dialogView.findViewById<View>(R.id.btn_next)
val enhancementContainer = dialogView.findViewById<View>(R.id.dialog_enhancement_container)
val enhancementLabel = dialogView.findViewById<TextView>(R.id.dialog_enhancement_label)
val enhancementSlider = dialogView.findViewById<android.widget.SeekBar>(R.id.dialog_enhancement_slider)
titleView.text = images.testName
val availableImages = mutableListOf<Pair<String, Bitmap>>()
images.rendered?.let { availableImages.add(Pair("Rendered", it)) }
images.golden?.let { availableImages.add(Pair("Golden", it)) }
images.diff?.let { availableImages.add(Pair("Diff", it)) }
images.alphaDiff?.let { availableImages.add(Pair("Alpha Diff", it)) }
if (availableImages.isEmpty()) return
var currentIndex = availableImages.indexOfFirst { it.first == initialLabel }
if (currentIndex == -1) currentIndex = 0
var currentDialogEnhancement = globalEnhancementFactor
val matrix = android.graphics.Matrix()
// Save initial values for translation tracking
var lastTouchX = 0f
var lastTouchY = 0f
var isDragging = false
val scaleDetector = android.view.ScaleGestureDetector(this, object : android.view.ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: android.view.ScaleGestureDetector): Boolean {
matrix.postScale(detector.scaleFactor, detector.scaleFactor, detector.focusX, detector.focusY)
imageView.imageMatrix = matrix
return true
}
})
imageView.setOnTouchListener { _, event ->
scaleDetector.onTouchEvent(event)
when (event.actionMasked) {
android.view.MotionEvent.ACTION_DOWN -> {
lastTouchX = event.x
lastTouchY = event.y
isDragging = true
}
android.view.MotionEvent.ACTION_MOVE -> {
if (isDragging && !scaleDetector.isInProgress) {
val dx = event.x - lastTouchX
val dy = event.y - lastTouchY
matrix.postTranslate(dx, dy)
imageView.imageMatrix = matrix
}
lastTouchX = event.x
lastTouchY = event.y
}
android.view.MotionEvent.ACTION_UP, android.view.MotionEvent.ACTION_CANCEL -> {
isDragging = false
}
}
true
}
fun updateView() {
val (label, bitmap) = availableImages[currentIndex]
typeView.text = label
imageView.setImageBitmap(bitmap)
(imageView.drawable as? android.graphics.drawable.BitmapDrawable)?.setAntiAlias(false)
(imageView.drawable as? android.graphics.drawable.BitmapDrawable)?.setFilterBitmap(false)
imageView.imageMatrix = matrix
if (label == "Diff" || label == "Alpha Diff") {
enhancementContainer.visibility = View.VISIBLE
applyEnhancementToView(imageView, currentDialogEnhancement)
} else {
enhancementContainer.visibility = View.GONE
imageView.colorFilter = null
}
}
fun resetMatrix() {
val drawable = imageView.drawable ?: return
val width = imageView.width.toFloat()
val height = imageView.height.toFloat()
val dw = drawable.intrinsicWidth.toFloat()
val dh = drawable.intrinsicHeight.toFloat()
val scaleX = width / dw
val scaleY = height / dh
val scale = Math.min(scaleX, scaleY)
val dx = (width - dw * scale) / 2f
val dy = (height - dh * scale) / 2f
matrix.reset()
matrix.postScale(scale, scale)
matrix.postTranslate(dx, dy)
imageView.imageMatrix = matrix
}
btnClose.setOnClickListener { dialog.dismiss() }
btnReset.setOnClickListener { resetMatrix() }
btnPrev.setOnClickListener {
currentIndex = (currentIndex - 1 + availableImages.size) % availableImages.size
updateView()
}
btnNext.setOnClickListener {
currentIndex = (currentIndex + 1) % availableImages.size
updateView()
}
val defaultProgress = ((currentDialogEnhancement - 1.0f) / 49.0f * 100).toInt()
val safeProgress = Math.max(0, Math.min(100, defaultProgress))
enhancementSlider.progress = safeProgress
enhancementLabel.text = String.format(Locale.US, "Enhance: %.1fx", currentDialogEnhancement)
enhancementSlider.setOnSeekBarChangeListener(object : android.widget.SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: android.widget.SeekBar?, progress: Int, fromUser: Boolean) {
currentDialogEnhancement = 1.0f + (progress / 100f) * 49.0f
enhancementLabel.text = String.format(Locale.US, "Enhance: %.1fx", currentDialogEnhancement)
updateView()
}
override fun onStartTrackingTouch(seekBar: android.widget.SeekBar?) {}
override fun onStopTrackingTouch(seekBar: android.widget.SeekBar?) {}
})
imageView.post {
resetMatrix()
}
updateView()
dialog.show()
}
}
@@ -31,14 +31,7 @@ data class ValidationResult(
val diffMetric: Float = 0f
)
class ValidationResultManager(
private val outputDir: File,
private val gpuDriverInfo: String,
private val deviceName: String,
private val deviceCodeName: String,
private val androidVersion: String,
private val androidBuildNumber: String
) {
class ValidationResultManager(private val outputDir: File) {
companion object {
private const val TAG = "ValidationResultManager"
@@ -71,9 +64,9 @@ class ValidationResultManager(
return outputDir
}
fun finalizeResults(totalTimeMs: Long): File? {
fun finalizeResults(): File? {
// Write results JSON
writeResultsJson(totalTimeMs)
writeResultsJson()
return null
}
@@ -110,10 +103,10 @@ class ValidationResultManager(
zos.closeEntry()
}
// 3. Add images (only rendered images, exclude diffs)
outputDir.listFiles { _, name -> name.endsWith(".png") && !name.endsWith("_diff.png") }?.forEach { imgFile ->
zos.putNextEntry(ZipEntry(imgFile.name))
imgFile.inputStream().use { it.copyTo(zos) }
// 3. Add diff images (any file ending in _diff.png in outputDir)
outputDir.listFiles { _, name -> name.endsWith("_diff.png") }?.forEach { diffFile ->
zos.putNextEntry(ZipEntry(diffFile.name))
diffFile.inputStream().use { it.copyTo(zos) }
zos.closeEntry()
}
}
@@ -273,18 +266,7 @@ class ValidationResultManager(
}
}
private fun writeResultsJson(totalTimeMs: Long) {
val rootObject = JSONObject()
val metadataObject = JSONObject()
metadataObject.put("gpu_driver_info", gpuDriverInfo ?: "")
metadataObject.put("total_time_ms", totalTimeMs)
metadataObject.put("device_name", deviceName ?: "")
metadataObject.put("device_code_name", deviceCodeName ?: "")
metadataObject.put("android_version", androidVersion ?: "")
metadataObject.put("android_build_number", androidBuildNumber ?: "")
rootObject.put("metadata", metadataObject)
private fun writeResultsJson() {
val jsonArray = JSONArray()
for (result in results) {
val jsonObject = JSONObject()
@@ -293,12 +275,11 @@ class ValidationResultManager(
jsonObject.put("diff_metric", result.diffMetric)
jsonArray.put(jsonObject)
}
rootObject.put("results", jsonArray)
val jsonFile = File(outputDir, "results.json")
try {
FileOutputStream(jsonFile).use { out ->
out.write(rootObject.toString(4).toByteArray())
out.write(jsonArray.toString(4).toByteArray())
}
} catch (e: Exception) {
Log.e(TAG, "Failed to write results.json", e)
@@ -42,7 +42,6 @@ class ValidationRunner(
private var currentModelName: String? = null
private var frameCounter = 0
private var suiteStartTime: Long = 0
enum class State {
IDLE,
@@ -66,7 +65,6 @@ class ValidationRunner(
callback?.onAllTestsFinished()
return
}
suiteStartTime = System.currentTimeMillis()
currentTestIndex = 0
currentModelIndex = 0
startTest(config.tests[0])
@@ -83,17 +81,6 @@ class ValidationRunner(
}
private fun startModel(modelName: String) {
if (currentModelName == modelName) {
Log.i("ValidationRunner", "Reusing model $modelName")
callback?.onStatusChanged("Reusing $modelName for ${currentTestConfig?.name}")
modelViewer.resetToDefaultState()
frameCounter = 0
currentState = State.WARMUP
return
}
currentModelName = modelName
val modelPath = config.models[modelName]
if (modelPath == null) {
@@ -128,6 +115,10 @@ class ValidationRunner(
}
fun onFrame(frameTimeNanos: Long) {
if (frameCounter % 60 == 0) {
Log.i("ValidationRunner", "onFrame: $currentState (frame: $frameCounter)")
}
when (currentState) {
State.IDLE -> {}
State.WAITING_FOR_RESOURCES -> {
@@ -145,6 +136,7 @@ class ValidationRunner(
}
}
State.RUNNING_TEST -> {
// Log.i("ValidationRunner", "Running test...")
currentEngine?.let { engine ->
val content = AutomationEngine.ViewerContent()
content.view = modelViewer.view
@@ -265,54 +257,10 @@ class ValidationRunner(
passed = (result.status == ImageDiff.Result.Status.PASSED)
diffMetric = result.failingPixelCount.toFloat()
if (!passed) {
if (!passed) {
if (result.diffImage != null) {
val diffImg = result.diffImage!!
val width = diffImg.width
val height = diffImg.height
val pixels = IntArray(width * height)
diffImg.getPixels(pixels, 0, width, 0, 0, width, height)
var hasAlphaDiff = false
val alphaPixels = IntArray(width * height)
for (i in pixels.indices) {
val color = pixels[i]
val a = android.graphics.Color.alpha(color)
val r = android.graphics.Color.red(color)
val g = android.graphics.Color.green(color)
val b = android.graphics.Color.blue(color)
if (a > 0) {
hasAlphaDiff = true
}
// Map alpha diff to grayscale RGB
alphaPixels[i] = android.graphics.Color.argb(255, a, a, a)
// Force main diff image alpha to 255
pixels[i] = android.graphics.Color.argb(255, r, g, b)
}
// Apply updated pixels to diff image
diffImg.setPixels(pixels, 0, width, 0, 0, width, height)
// The C++ ImageDiff code sets isPremultiplied to false so Android
// doesn't erase RGB diff values when Alpha diff is 0. However, Android's
// Canvas will crash if we try to draw a non-premultiplied bitmap.
// Since we just forced all alpha values to 255 (fully opaque) in the
// loop above, we can safely mark it as premultiplied again here.
diffImg.isPremultiplied = true
callback?.onImageResult("Diff", diffImg)
resultManager.saveImage("${testFullName}_diff", diffImg)
if (hasAlphaDiff) {
val alphaDiffImg = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
alphaDiffImg.setPixels(alphaPixels, 0, width, 0, 0, width, height)
callback?.onImageResult("Alpha Diff", alphaDiffImg)
resultManager.saveImage("${testFullName}_alpha_diff", alphaDiffImg)
}
callback?.onImageResult("Diff", result.diffImage!!)
resultManager.saveImage("${testFullName}_diff", result.diffImage!!)
}
}
} else {
@@ -358,10 +306,7 @@ class ValidationRunner(
startTest(config.tests[currentTestIndex])
} else {
currentState = State.IDLE
val totalTimeMs = System.currentTimeMillis() - suiteStartTime
resultManager.finalizeResults(totalTimeMs)
resultManager.finalizeResults()
callback?.onAllTestsFinished()
}
}
@@ -81,33 +81,6 @@
android:contentDescription="More Options"
android:layout_marginStart="8dp"/>
</LinearLayout>
<!-- Global Enhancement Controls (Hidden by default) -->
<LinearLayout
android:id="@+id/enhancement_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:visibility="gone"
android:paddingBottom="8dp">
<TextView
android:id="@+id/enhancement_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enhancement: 1.0x"
android:textSize="12sp"
android:minWidth="120dp" />
<SeekBar
android:id="@+id/enhancement_slider"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:max="100"
android:progress="0" />
</LinearLayout>
</LinearLayout>
<TextView
@@ -1,130 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#FF202020"
android:padding="8dp">
<!-- Header with title and close button -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:id="@+id/dialog_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Test Result"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:textStyle="bold" />
<ImageButton
android:id="@+id/btn_close"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@android:drawable/ic_menu_close_clear_cancel" />
</LinearLayout>
<!-- Subtitle for Image Type -->
<TextView
android:id="@+id/dialog_image_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:text="Rendered"
android:textColor="#BBBBBB"
android:textSize="14sp"
android:paddingBottom="8dp" />
<!-- Image Area with Arrows -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintDimensionRatio="1:1">
<ImageView
android:id="@+id/dialog_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="matrix" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- Navigation Arrows -->
<LinearLayout
android:id="@+id/dialog_arrow_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<ImageButton
android:id="@+id/btn_prev"
android:layout_width="64dp"
android:layout_height="64dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@android:drawable/ic_media_previous"
app:tint="#FFFFFF" />
<ImageButton
android:id="@+id/btn_reset"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginHorizontal="16dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@android:drawable/ic_menu_revert"
app:tint="#FFFFFF" />
<ImageButton
android:id="@+id/btn_next"
android:layout_width="64dp"
android:layout_height="64dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@android:drawable/ic_media_next"
app:tint="#FFFFFF" />
</LinearLayout>
<!-- Enhancement Controls (only for diff images) -->
<LinearLayout
android:id="@+id/dialog_enhancement_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingTop="12dp"
android:paddingBottom="8dp"
android:visibility="gone">
<TextView
android:id="@+id/dialog_enhancement_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enhance: 1.0x"
android:textColor="#FFFFFF"
android:textSize="12sp"
android:minWidth="100dp" />
<SeekBar
android:id="@+id/dialog_enhancement_slider"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:max="100"
android:progress="0" />
</LinearLayout>
</LinearLayout>
+1 -7
View File
@@ -54,13 +54,8 @@ if [[ "$OS_NAME" == "Linux" ]]; then
# is constantly being updated and sometimes not compatible with the current
# linux platform.
# Note that we assume this platform is compatible with ubuntu-22.04 x86_64
DPKG_ARCH=$(dpkg --print-architecture)
EXTRA_PACKAGES=""
if [[ "$DPKG_ARCH" == "amd64" ]]; then
EXTRA_PACKAGES="lib32gcc-s1 lib32stdc++6 libc6-i386"
fi
sudo apt-get -y install \
autoconf automake autopoint autotools-dev bindgen bison build-essential bzip2 cpp cpp-11 debhelper debugedit dh-autoreconf dh-strip-nondeterminism diffstat directx-headers-dev dpkg-dev dwz flex g++ g++-11 gcc gcc-11 gcc-11-base:${DPKG_ARCH} gettext glslang-tools icu-devtools intltool-debian ${EXTRA_PACKAGES} libarchive-zip-perl libasan6:${DPKG_ARCH} libatomic1:${DPKG_ARCH} libc-dev-bin libc6-dbg:${DPKG_ARCH} libc6-dev:${DPKG_ARCH} libcc1-0:${DPKG_ARCH} libclang-${GITHUB_CLANG_VERSION}-dev libclang-common-${GITHUB_CLANG_VERSION}-dev libclang-cpp${GITHUB_CLANG_VERSION} libclang-cpp${GITHUB_CLANG_VERSION}-dev libclang1-14 libclang1-${GITHUB_CLANG_VERSION} libclc-${GITHUB_CLANG_VERSION} libclc-${GITHUB_CLANG_VERSION}-dev libcrypt-dev:${DPKG_ARCH} libdebhelper-perl libdpkg-perl libdrm-amdgpu1:${DPKG_ARCH} libdrm-dev:${DPKG_ARCH} libdrm-intel1:${DPKG_ARCH} libdrm-nouveau2:${DPKG_ARCH} libdrm-radeon1:${DPKG_ARCH} libelf-dev:${DPKG_ARCH} libexpat1-dev:${DPKG_ARCH} libffi-dev:${DPKG_ARCH} libfile-stripnondeterminism-perl libgc1:${DPKG_ARCH} libgcc-11-dev:${DPKG_ARCH} libgl1:${DPKG_ARCH} libgl1-mesa-dri:${DPKG_ARCH} libglapi-mesa:${DPKG_ARCH} libglvnd-core-dev:${DPKG_ARCH} libglvnd0:${DPKG_ARCH} libglx-mesa0:${DPKG_ARCH} libglx0:${DPKG_ARCH} libgomp1:${DPKG_ARCH} libicu-dev:${DPKG_ARCH} libisl23:${DPKG_ARCH} libitm1:${DPKG_ARCH} libllvm14:${DPKG_ARCH} libllvm${GITHUB_CLANG_VERSION}:${DPKG_ARCH} libllvmspirvlib-${GITHUB_CLANG_VERSION}-dev:${DPKG_ARCH} libllvmspirvlib${GITHUB_CLANG_VERSION}:${DPKG_ARCH} liblsan0:${DPKG_ARCH} libmpc3:${DPKG_ARCH} libncurses-dev:${DPKG_ARCH} libnsl-dev:${DPKG_ARCH} libobjc-11-dev:${DPKG_ARCH} libobjc4:${DPKG_ARCH} libpciaccess-dev:${DPKG_ARCH} libpciaccess0f:${DPKG_ARCH} libpfm4:${DPKG_ARCH} libpthread-stubs0-dev:${DPKG_ARCH} libquadmath0:${DPKG_ARCH} libsensors-config libsensors-dev:${DPKG_ARCH} libsensors5:${DPKG_ARCH} libset-scalar-perl libstd-rust-1.75:${DPKG_ARCH} libstd-rust-dev:${DPKG_ARCH} libstdc++-11-dev:${DPKG_ARCH} libsub-override-perl libtinfo-dev:${DPKG_ARCH} libtirpc-dev:${DPKG_ARCH} libtool libtsan0:${DPKG_ARCH} libubsan1:${DPKG_ARCH} libva-dev:${DPKG_ARCH} libva-drm2:${DPKG_ARCH} libva-glx2:${DPKG_ARCH} libva-wayland2:${DPKG_ARCH} libva-x11-2:${DPKG_ARCH} libva2:${DPKG_ARCH} libvdpau-dev:${DPKG_ARCH} libvdpau1:${DPKG_ARCH} libvulkan-dev:${DPKG_ARCH} libvulkan1:${DPKG_ARCH} libwayland-bin libwayland-client0:${DPKG_ARCH} libwayland-cursor0:${DPKG_ARCH} libwayland-dev:${DPKG_ARCH} libwayland-egl-backend-dev:${DPKG_ARCH} libwayland-egl1:${DPKG_ARCH} libwayland-server0:${DPKG_ARCH} libx11-dev:${DPKG_ARCH} libx11-xcb-dev:${DPKG_ARCH} libx11-xcb1:${DPKG_ARCH} libxau-dev:${DPKG_ARCH} libxcb-dri2-0:${DPKG_ARCH} libxcb-dri2-0-dev:${DPKG_ARCH} libxcb-dri3-0:${DPKG_ARCH} libxcb-dri3-dev:${DPKG_ARCH} libxcb-glx0:${DPKG_ARCH} libxcb-glx0-dev:${DPKG_ARCH} libxcb-present-dev:${DPKG_ARCH} libxcb-present0:${DPKG_ARCH} libxcb-randr0:${DPKG_ARCH} libxcb-randr0-dev:${DPKG_ARCH} libxcb-render0:${DPKG_ARCH} libxcb-render0-dev:${DPKG_ARCH} libxcb-shape0:${DPKG_ARCH} libxcb-shape0-dev:${DPKG_ARCH} libxcb-shm0:${DPKG_ARCH} libxcb-shm0-dev:${DPKG_ARCH} libxcb-sync-dev:${DPKG_ARCH} libxcb-sync1:${DPKG_ARCH} libxcb-xfixes0:${DPKG_ARCH} libxcb-xfixes0-dev:${DPKG_ARCH} libxcb1-dev:${DPKG_ARCH} libxdmcp-dev:${DPKG_ARCH} libxext-dev:${DPKG_ARCH} libxfixes-dev:${DPKG_ARCH} libxfixes3:${DPKG_ARCH} libxml2-dev:${DPKG_ARCH} libxrandr-dev:${DPKG_ARCH} libxrandr2:${DPKG_ARCH} libxrender-dev:${DPKG_ARCH} libxrender1:${DPKG_ARCH} libxshmfence-dev:${DPKG_ARCH} libxshmfence1:${DPKG_ARCH} libxxf86vm-dev:${DPKG_ARCH} libxxf86vm1:${DPKG_ARCH} libz3-4:${DPKG_ARCH} libz3-dev:${DPKG_ARCH} libzstd-dev:${DPKG_ARCH} linux-libc-dev:${DPKG_ARCH} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev llvm-${LLVM_VERSION}-linker-tools llvm-${LLVM_VERSION}-runtime llvm-${LLVM_VERSION}-tools llvm-spirv-${LLVM_VERSION} lto-disabled-list m4 make meson ninja-build pkg-config po-debconf python3-mako python3-ply python3-pygments quilt rpcsvc-proto rustc spirv-tools valgrind wayland-protocols x11proto-dev xorg-sgml-doctools xtrans-dev zlib1g-dev:${DPKG_ARCH} \
autoconf automake autopoint autotools-dev bindgen bison build-essential bzip2 cpp cpp-11 debhelper debugedit dh-autoreconf dh-strip-nondeterminism diffstat directx-headers-dev dpkg-dev dwz flex g++ g++-11 gcc gcc-11 gcc-11-base:amd64 gettext glslang-tools icu-devtools intltool-debian lib32gcc-s1 lib32stdc++6 libarchive-zip-perl libasan6:amd64 libatomic1:amd64 libc-dev-bin libc6-dbg:amd64 libc6-dev:amd64 libc6-i386 libcc1-0:amd64 libclang-${GITHUB_CLANG_VERSION}-dev libclang-common-${GITHUB_CLANG_VERSION}-dev libclang-cpp${GITHUB_CLANG_VERSION} libclang-cpp${GITHUB_CLANG_VERSION}-dev libclang1-14 libclang1-${GITHUB_CLANG_VERSION} libclc-${GITHUB_CLANG_VERSION} libclc-${GITHUB_CLANG_VERSION}-dev libcrypt-dev:amd64 libdebhelper-perl libdpkg-perl libdrm-amdgpu1:amd64 libdrm-dev:amd64 libdrm-intel1:amd64 libdrm-nouveau2:amd64 libdrm-radeon1:amd64 libelf-dev:amd64 libexpat1-dev:amd64 libffi-dev:amd64 libfile-stripnondeterminism-perl libgc1:amd64 libgcc-11-dev:amd64 libgl1:amd64 libgl1-mesa-dri:amd64 libglapi-mesa:amd64 libglvnd-core-dev:amd64 libglvnd0:amd64 libglx-mesa0:amd64 libglx0:amd64 libgomp1:amd64 libicu-dev:amd64 libisl23:amd64 libitm1:amd64 libllvm14:amd64 libllvm${GITHUB_CLANG_VERSION}:amd64 libllvmspirvlib-${GITHUB_CLANG_VERSION}-dev:amd64 libllvmspirvlib${GITHUB_CLANG_VERSION}:amd64 liblsan0:amd64 libmpc3:amd64 libncurses-dev:amd64 libnsl-dev:amd64 libobjc-11-dev:amd64 libobjc4:amd64 libpciaccess-dev:amd64 libpciaccess0f:amd64 libpfm4:amd64 libpthread-stubs0-dev:amd64 libquadmath0:amd64 libsensors-config libsensors-dev:amd64 libsensors5:amd64 libset-scalar-perl libstd-rust-1.75:amd64 libstd-rust-dev:amd64 libstdc++-11-dev:amd64 libsub-override-perl libtinfo-dev:amd64 libtirpc-dev:amd64 libtool libtsan0:amd64 libubsan1:amd64 libva-dev:amd64 libva-drm2:amd64 libva-glx2:amd64 libva-wayland2:amd64 libva-x11-2:amd64 libva2:amd64 libvdpau-dev:amd64 libvdpau1:amd64 libvulkan-dev:amd64 libvulkan1:amd64 libwayland-bin libwayland-client0:amd64 libwayland-cursor0:amd64 libwayland-dev:amd64 libwayland-egl-backend-dev:amd64 libwayland-egl1:amd64 libwayland-server0:amd64 libx11-dev:amd64 libx11-xcb-dev:amd64 libx11-xcb1:amd64 libxau-dev:amd64 libxcb-dri2-0:amd64 libxcb-dri2-0-dev:amd64 libxcb-dri3-0:amd64 libxcb-dri3-dev:amd64 libxcb-glx0:amd64 libxcb-glx0-dev:amd64 libxcb-present-dev:amd64 libxcb-present0:amd64 libxcb-randr0:amd64 libxcb-randr0-dev:amd64 libxcb-render0:amd64 libxcb-render0-dev:amd64 libxcb-shape0:amd64 libxcb-shape0-dev:amd64 libxcb-shm0:amd64 libxcb-shm0-dev:amd64 libxcb-sync-dev:amd64 libxcb-sync1:amd64 libxcb-xfixes0:amd64 libxcb-xfixes0-dev:amd64 libxcb1-dev:amd64 libxdmcp-dev:amd64 libxext-dev:amd64 libxfixes-dev:amd64 libxfixes3:amd64 libxml2-dev:amd64 libxrandr-dev:amd64 libxrandr2:amd64 libxrender-dev:amd64 libxrender1:amd64 libxshmfence-dev:amd64 libxshmfence1:amd64 libxxf86vm-dev:amd64 libxxf86vm1:amd64 libz3-4:amd64 libz3-dev:amd64 libzstd-dev:amd64 linux-libc-dev:amd64 llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev llvm-${LLVM_VERSION}-linker-tools llvm-${LLVM_VERSION}-runtime llvm-${LLVM_VERSION}-tools llvm-spirv-${LLVM_VERSION} lto-disabled-list m4 make meson ninja-build pkg-config po-debconf python3-mako python3-ply python3-pygments quilt rpcsvc-proto rustc spirv-tools valgrind wayland-protocols x11proto-dev xorg-sgml-doctools xtrans-dev zlib1g-dev:amd64 \
clang-$GITHUB_CLANG_VERSION libc++-$GITHUB_CLANG_VERSION-dev libc++abi-$GITHUB_CLANG_VERSION-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100
@@ -85,7 +80,6 @@ elif [[ "$OS_NAME" == "Darwin" ]]; then
if command -v brew > /dev/null 2>&1; then
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=true brew install autoconf automake libx11 libxext libxrandr \
llvm@${LLVM_VERSION} ninja meson pkg-config libxshmfence
brew link --overwrite llvm@${LLVM_VERSION}
# For reasons unknown, this is necessary for pkg-config to find homebrew's packages
LOCAL_PKG_CONFIG_PATH="/opt/homebrew/lib/pkgconfig:$PKG_CONFIG_PATH"
elif command -v port > /dev/null 2>&1; then
+3 -14
View File
@@ -63,10 +63,6 @@ function _preferred_os_filename() {
function download_vulkan_installer() {
local os=$(_get_os)
if [[ "$os" == "linux" ]]; then
echo "Linux uses apt to install vulkan dependencies, skipping tarball download." >&2
return 0
fi
local dl_filename=$(_os_filename ${VULKAN_SDK_VERSION})
local filename=$(_preferred_os_filename)
local url=https://sdk.lunarg.com/sdk/download/$VULKAN_SDK_VERSION/$os/$dl_filename?Human=true
@@ -83,22 +79,15 @@ function download_vulkan_installer() {
function unpack_vulkan_installer() {
local os=$(_get_os)
if [[ "$os" == "linux" ]]; then
install_${os}
return 0
fi
local filename=$(_preferred_os_filename $os)
test -f $filename
install_${os}
}
function install_linux() {
echo "Installing Vulkan dependencies via apt for Linux..." >&2
sudo apt-get update -y
sudo apt-get install -y libvulkan-dev vulkan-validationlayers glslang-tools spirv-tools
# Create a dummy SDK dir to satisfy scripts expecting a VulkanSDK folder structure
mkdir -p ~/VulkanSDK/${VULKAN_SDK_VERSION}
test -d $VULKAN_SDK_DIR && test -f vulkan_sdk.tar.gz
echo "extract just the SDK's prebuilt binaries ($VULKAN_SDK_VERSION/x86_64) from vulkan_sdk.tar.gz into $VULKAN_SDK" >&2
tar -C "$VULKAN_SDK_DIR" --strip-components 2 -xf vulkan_sdk.tar.gz $VULKAN_SDK_VERSION/x86_64
}
function install_mac() {
@@ -46,7 +46,6 @@ list(SORT NDK_VERSIONS)
list(GET NDK_VERSIONS -1 NDK_VERSION)
get_filename_component(NDK_VERSION ${NDK_VERSION} NAME)
set(TOOLCHAIN ${ANDROID_HOME_UNIX}/ndk/${NDK_VERSION}/toolchains/llvm/prebuilt/${HOST_NAME_L}-x86_64)
set(CMAKE_ANDROID_NDK_VERSION ${NDK_VERSION})
# specify the cross compiler
set(COMPILER_SUFFIX)
-1
View File
@@ -47,7 +47,6 @@ list(SORT NDK_VERSIONS)
list(GET NDK_VERSIONS -1 NDK_VERSION)
get_filename_component(NDK_VERSION ${NDK_VERSION} NAME)
set(TOOLCHAIN ${ANDROID_HOME_UNIX}/ndk/${NDK_VERSION}/toolchains/llvm/prebuilt/${HOST_NAME_L}-x86_64)
set(CMAKE_ANDROID_NDK_VERSION ${NDK_VERSION})
# specify the cross compiler
set(COMPILER_SUFFIX)
-1
View File
@@ -46,7 +46,6 @@ list(SORT NDK_VERSIONS)
list(GET NDK_VERSIONS -1 NDK_VERSION)
get_filename_component(NDK_VERSION ${NDK_VERSION} NAME)
set(TOOLCHAIN ${ANDROID_HOME_UNIX}/ndk/${NDK_VERSION}/toolchains/llvm/prebuilt/${HOST_NAME_L}-x86_64)
set(CMAKE_ANDROID_NDK_VERSION ${NDK_VERSION})
# specify the cross compiler
set(COMPILER_SUFFIX)
@@ -46,7 +46,6 @@ list(SORT NDK_VERSIONS)
list(GET NDK_VERSIONS -1 NDK_VERSION)
get_filename_component(NDK_VERSION ${NDK_VERSION} NAME)
set(TOOLCHAIN ${ANDROID_HOME_UNIX}/ndk/${NDK_VERSION}/toolchains/llvm/prebuilt/${HOST_NAME_L}-x86_64)
set(CMAKE_ANDROID_NDK_VERSION ${NDK_VERSION})
# specify the cross compiler
set(COMPILER_SUFFIX)
+2 -2
View File
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.70.0'
implementation 'com.google.android.filament:filament-android:1.69.5'
}
</code></pre>
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
@@ -195,7 +195,7 @@ dependencies {
</div>
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
<p>iOS projects can use CocoaPods to install the latest release:</p>
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.70.0'
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.69.5'
</code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul>
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3 -2
View File
@@ -600,7 +600,6 @@ if (FILAMENT_BUILD_TESTING)
test/test_CircularBuffer.cpp
test/test_CommandBufferQueue.cpp
test/test_Template.cpp
test/test_Platform.cpp
)
if (FILAMENT_SUPPORTS_WEBGPU)
list(APPEND BACKEND_TEST_SRC
@@ -616,6 +615,7 @@ if (FILAMENT_BUILD_TESTING)
set(BACKEND_TEST_LIBS
absl::str_format
backend
getopt
gtest
imageio
filamat
@@ -724,9 +724,10 @@ if (FILAMENT_BUILD_TESTING)
target_link_libraries(metal_utils_test PRIVATE
backend
getopt
gtest
)
set_target_properties(metal_utils_test PROPERTIES FOLDER Tests)
endif()
endif()
endif()
+1 -23
View File
@@ -216,18 +216,6 @@ public:
MULTIVIEW,
};
/**
* Types of device/driver information that can be queried from the platform.
*/
enum class DeviceInfoType {
OPENGL_RENDERER, //!< glGetString(GL_RENDERER)
OPENGL_VENDOR, //!< glGetString(GL_VENDOR)
OPENGL_VERSION, //!< glGetString(GL_VERSION)
VULKAN_DEVICE_NAME, //!< VkPhysicalDeviceProperties::deviceName
VULKAN_DRIVER_NAME, //!< VkPhysicalDeviceDriverProperties::driverName
VULKAN_DRIVER_INFO, //!< VkPhysicalDeviceDriverProperties::driverInfo
};
/**
* This controls the priority level for GPU work scheduling, which helps prioritize the
* submitted GPU work and enables preemption.
@@ -328,7 +316,7 @@ public:
*/
StereoscopicType stereoscopicType = StereoscopicType::NONE;
/**
/*
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
* between 1 and Engine::getMaxStereoscopicEyes() (inclusive).
*/
@@ -389,16 +377,6 @@ public:
*/
virtual int getOSVersion() const noexcept = 0;
/**
* Queries device/driver information of the graphics API.
* @param infoType the type of information to query.
* @param driver a pointer to the current driver.
* @return a CString containing the requested information.
*/
virtual utils::CString getDeviceInfo(DeviceInfoType infoType,
Driver* UTILS_NULLABLE driver) const noexcept = 0;
/**
* Creates and initializes the low-level API (e.g. an OpenGL context or Vulkan instance),
* then creates the concrete Driver.
@@ -52,9 +52,6 @@ protected:
~OpenGLPlatform() noexcept override;
utils::CString getDeviceInfo(DeviceInfoType infoType,
Driver* UTILS_NULLABLE driver) const noexcept override;
public:
struct ExternalTexture {
unsigned int target; // GLenum target
@@ -73,12 +70,6 @@ public:
*/
static utils::CString getRendererString(Driver const* UTILS_NONNULL driver);
/**
* Return the OpenGL version string of the specified Driver instance.
* @return The GL_VERSION string
*/
static utils::CString getVersionString(Driver const* UTILS_NONNULL driver);
/**
* Called by the driver to destroy the OpenGL context. This should clean up any windows
* or buffers from initialization. This is for instance where `eglDestroyContext` would be
@@ -159,12 +159,6 @@ protected:
EGLConfig getEglConfig() const noexcept { return mEGLConfig; }
EGLConfig getSuitableConfigForSwapChain(uint64_t flags, bool window, bool pbuffer) const;
// Sets the EGLDisplay to be used by this platform. This should only be called by derived
// classes before invoking createDriver. Calling it after that point will result in
// undefined behaviour. This class will take ownership of the display and call eglTerminate
// on it during shutdown.
void setEglDisplay(EGLDisplay display) noexcept;
// supported extensions detected at runtime
struct {
struct {
@@ -185,7 +185,7 @@ private:
};
int mOSVersion;
ExternalStreamManagerAndroid* mExternalStreamManager = nullptr;
ExternalStreamManagerAndroid& mExternalStreamManager;
AndroidDetails& mAndroidDetails;
utils::PerformanceHintManager mPerformanceHintManager;
utils::PerformanceHintManager::Session mPerformanceHintSession;
@@ -38,7 +38,6 @@ public:
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) override;
int getOSVersion() const noexcept override { return 0; }
utils::CString getDeviceInfo(DeviceInfoType, Driver*) const noexcept override { return {}; }
/**
* Optionally initializes the Metal platform by acquiring resources necessary for rendering.
@@ -31,6 +31,7 @@
#include <cstring>
#include <cstddef>
#include <functional>
#include <string>
#include <tuple>
#include <unordered_set>
@@ -68,7 +69,7 @@ public:
struct ExtensionHashFn {
std::size_t operator()(utils::CString const& s) const noexcept {
return std::hash<utils::CString>{}(s.data());
return std::hash<std::string>{}(s.data());
}
};
// Note: utils::CString::operator== has an edge case that breaks for the extension set.
@@ -142,8 +143,6 @@ public:
return 0;
}
utils::CString getDeviceInfo(DeviceInfoType infoType, Driver* driver) const noexcept override;
// ----------------------------------------------------
// ---------- Platform Customization options ----------
struct Customization {
@@ -176,12 +175,6 @@ public:
* presentation. Default is true.
*/
bool transitionSwapChainImageLayoutForPresent = true;
/**
* The number of frames before an unused framebuffer is evicted from the cache.
* Default is 3.
*/
uint32_t timeBeforeEvictionFbo = 3;
};
/**
@@ -46,9 +46,6 @@ public:
~WebGPUPlatform() override = default;
[[nodiscard]] int getOSVersion() const noexcept final { return 0; }
[[nodiscard]] utils::CString getDeviceInfo(DeviceInfoType, Driver*) const noexcept override {
return {};
}
[[nodiscard]] wgpu::Instance& getInstance() noexcept { return mInstance; }
+1 -1
View File
@@ -32,7 +32,7 @@
#include <cstddef>
#include <functional>
#include <string_view>
#include <string>
#include <utility>
#ifdef __ANDROID__
-1
View File
@@ -26,7 +26,6 @@ class PlatformNoop final : public Platform {
public:
int getOSVersion() const noexcept final { return 0; }
utils::CString getDeviceInfo(DeviceInfoType, Driver*) const noexcept override { return {}; }
~PlatformNoop() noexcept override = default;
@@ -341,44 +341,7 @@ void GLDescriptorSet::bind(
if (arg.handle) {
GLTexture const* const t = handleAllocator.handle_cast<GLTexture*>(arg.handle);
gl.bindTexture(unit, t->gl.target, t->gl.id, t->gl.external);
SamplerParams params = arg.params;
#if defined(__EMSCRIPTEN__)
// From https://registry.khronos.org/OpenGL-Refpages/es2.0/
// GLES 2.0 will draw mipmapped samplers as black if the following conditions are not met:
//
// "...if the width or height of a texture image are not powers of two and either the
// GL_TEXTURE_MIN_FILTER is set to one of the functions that requires mipmaps or the
// GL_TEXTURE_WRAP_S or GL_TEXTURE_WRAP_T is not set to GL_CLAMP_TO_EDGE, then the
// texture image unit will return (R, G, B, A) = (0, 0, 0, 1)."
//
// "If the texture has dimensions w × h , there are [floor(log2(max(w, h))) + 1] mipmap levels.
// Level 0 is the original texture; level [floor(log2(max(w, h)))] is the final 1 × 1 mipmap."
// "Suppose that a texture is accessed from a fragment shader or vertex shader and has set GL_TEXTURE_MIN_FILTER
// to one of the functions that requires mipmaps. If either the dimensions of the texture images currently
// defined (with previous calls to glTexImage2D, glCompressedTexImage2D, or glCopyTexImage2D) do not follow
// the proper sequence for mipmaps (described above), or there are fewer texture images defined than are needed,
// or the set of texture images were defined with different formats or types, then the texture image unit
// will return (R, G, B, A) = (0, 0, 0, 1)."
//
// So we force the sampler to a valid state in those cases.
auto isPowerOfTwo = [](uint32_t x) -> bool { return (x & (x - 1)) == 0 && x > 0; };
bool textureIsPowerOfTwo = isPowerOfTwo(t->width) && isPowerOfTwo(t->height);
uint32_t requiredMipLevels = (std::floor(std::log2(std::max(t->width, std::max(t->height, t->depth)))) + 1);
bool textureHasRequiredMipLevels = t->levels == requiredMipLevels;
bool samplerCanBeInvalid = params.filterMin > SamplerMinFilter::LINEAR;
if (samplerCanBeInvalid && (!textureIsPowerOfTwo || !textureHasRequiredMipLevels)) {
if (params.filterMin == SamplerMinFilter::NEAREST_MIPMAP_NEAREST
|| params.filterMin == SamplerMinFilter::LINEAR_MIPMAP_NEAREST) {
params.filterMin = SamplerMinFilter::NEAREST;
} else {
params.filterMin = SamplerMinFilter::LINEAR;
}
}
#endif
SamplerParams const params = arg.params;
glTexParameteri(t->gl.target, GL_TEXTURE_MIN_FILTER,
(GLint)GLUtils::getTextureFilter(params.filterMin));
glTexParameteri(t->gl.target, GL_TEXTURE_MAG_FILTER,
@@ -222,15 +222,6 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
#endif
#endif
#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_disjoint_timer_query)
if (ext.EXT_disjoint_timer_query) {
mGpuTimeSupported = true;
} else
#endif
if (platform.canCreateFence()) {
mGpuTimeSupported = true;
}
// in practice KHR_Debug has never been useful, and actually is confusing. We keep this
// only for our own debugging, in case we need it some day.
#if false && !defined(NDEBUG) && defined(GL_KHR_debug)
+1 -5
View File
@@ -158,10 +158,8 @@ public:
constexpr static inline size_t getIndexForBufferTarget(GLenum target) noexcept;
ShaderModel getShaderModel() const noexcept { return mShaderModel; }
void resetState() noexcept;
bool isGpuTimeSupported() const noexcept { return mGpuTimeSupported; }
inline void useProgram(GLuint program) noexcept;
@@ -544,8 +542,6 @@ private:
Platform::DriverConfig const mDriverConfig;
bool mGpuTimeSupported = false;
void bindFramebufferResolved(GLenum target, GLuint buffer) noexcept;
const std::array<std::tuple<bool const&, char const*, char const*>, sizeof(bugs)> mBugDatabase{{
+3 -8
View File
@@ -467,8 +467,7 @@ void OpenGLDriver::bindTexture(GLuint const unit, GLTexture const* t) noexcept {
bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
bool success = true;
if (mBoundProgram != p) {
// compile/link the program if needed and call glUseProgram.
// This call may block until the program linking process is complete.
// compile/link the program if needed and call glUseProgram
success = p->use(this, mContext);
assert_invariant(success == p->isValid());
if (success) {
@@ -717,11 +716,7 @@ Handle<HwSwapChain> OpenGLDriver::createSwapChainHeadlessS() noexcept {
}
Handle<HwTimerQuery> OpenGLDriver::createTimerQueryS() noexcept {
Handle<HwTimerQuery> tqh = initHandle<GLTimerQuery>();
// The state must be constructed here, as a synchronous call to getTimerQueryValue might happen
// before createTimerQueryR is executed on the backend thread.
handle_cast<GLTimerQuery*>(tqh)->state = std::make_shared<GLTimerQuery::State>();
return tqh;
return initHandle<GLTimerQuery>();
}
Handle<HwDescriptorSetLayout> OpenGLDriver::createDescriptorSetLayoutS() noexcept {
@@ -2860,7 +2855,7 @@ bool OpenGLDriver::isFrameBufferFetchMultiSampleSupported() {
}
bool OpenGLDriver::isFrameTimeSupported() {
return mContext.isGpuTimeSupported();
return TimerQueryFactory::isGpuTimeSupported();
}
bool OpenGLDriver::isAutoDepthResolveSupported() {
@@ -250,10 +250,6 @@ private:
return utils::CString{ mContext.state.renderer };
}
utils::CString getVersionString() const noexcept override {
return utils::CString{ mContext.state.version };
}
JobQueue* getJobQueue() const noexcept { return mJobQueue.get(); }
JobWorker* getJobWorker() const noexcept { return mJobWorker.get(); }
@@ -34,7 +34,6 @@ protected:
public:
virtual utils::CString getVendorString() const noexcept = 0;
virtual utils::CString getRendererString() const noexcept = 0;
virtual utils::CString getVersionString() const noexcept = 0;
};
} // filament::backend
@@ -45,21 +45,6 @@ Driver* OpenGLPlatform::createDefaultDriver(OpenGLPlatform* platform,
OpenGLPlatform::~OpenGLPlatform() noexcept = default;
utils::CString OpenGLPlatform::getDeviceInfo(DeviceInfoType infoType,
Driver* driver) const noexcept {
switch (infoType) {
case DeviceInfoType::OPENGL_RENDERER:
return getRendererString(driver);
case DeviceInfoType::OPENGL_VENDOR:
return getVendorString(driver);
case DeviceInfoType::OPENGL_VERSION:
return getVersionString(driver);
default:
FILAMENT_CHECK_POSTCONDITION(false) << "Unsupported DeviceInfoType for OpenGLPlatform";
return {};
}
}
utils::CString OpenGLPlatform::getVendorString(Driver const* driver) {
auto const p = static_cast<OpenGLDriverBase const*>(driver);
#if UTILS_HAS_RTTI
@@ -78,15 +63,6 @@ utils::CString OpenGLPlatform::getRendererString(Driver const* driver) {
return p->getRendererString();
}
utils::CString OpenGLPlatform::getVersionString(Driver const* driver) {
auto const p = static_cast<OpenGLDriverBase const*>(driver);
#if UTILS_HAS_RTTI
FILAMENT_CHECK_POSTCONDITION(dynamic_cast<OpenGLDriverBase const*>(driver))
<< "Driver* has not been allocated with OpenGLPlatform";
#endif
return p->getVersionString();
}
void OpenGLPlatform::makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
utils::Invocable<void()>, utils::Invocable<void(size_t)>) {
makeCurrent(getCurrentContextType(), drawSwapChain, readSwapChain);
@@ -48,7 +48,7 @@ class OpenGLDriver;
// ------------------------------------------------------------------------------------------------
bool TimerQueryFactory::mGpuTimeSupported = false;
TimerQueryFactoryInterface* TimerQueryFactory::init(
OpenGLPlatform& platform, OpenGLContext& context) {
@@ -65,14 +65,17 @@ TimerQueryFactoryInterface* TimerQueryFactory::init(
} else {
impl = new(std::nothrow) TimerQueryNativeFactory(context);
}
mGpuTimeSupported = true;
} else
#endif
if (platform.canCreateFence()) {
// no timer queries, but we can use fences
impl = new(std::nothrow) TimerQueryFenceFactory(platform);
mGpuTimeSupported = true;
} else {
// no queries, no fences -- that's a problem
impl = new(std::nothrow) TimerQueryFallbackFactory();
mGpuTimeSupported = false;
}
assert_invariant(impl);
return impl;
@@ -85,14 +88,15 @@ TimerQueryFactoryInterface::~TimerQueryFactoryInterface() = default;
// This is a backend synchronous call
TimerQueryResult TimerQueryFactoryInterface::getTimerQueryValue(
GLTimerQuery* tq, uint64_t* elapsedTime) noexcept {
assert_invariant(tq->state);
int64_t const elapsed = tq->state->elapsed.load(std::memory_order_relaxed);
if (elapsed > 0) {
*elapsedTime = elapsed;
return TimerQueryResult::AVAILABLE;
if (UTILS_LIKELY(tq->state)) {
int64_t const elapsed = tq->state->elapsed.load(std::memory_order_relaxed);
if (elapsed > 0) {
*elapsedTime = elapsed;
return TimerQueryResult::AVAILABLE;
}
return TimerQueryResult(elapsed);
}
return TimerQueryResult(elapsed);
return TimerQueryResult::ERROR;
}
// ------------------------------------------------------------------------------------------------
@@ -106,8 +110,9 @@ TimerQueryNativeFactory::TimerQueryNativeFactory(OpenGLContext& context)
TimerQueryNativeFactory::~TimerQueryNativeFactory() = default;
void TimerQueryNativeFactory::createTimerQuery(GLTimerQuery* tq) {
assert_invariant(tq->state);
assert_invariant(!tq->state);
tq->state = std::make_shared<GLTimerQuery::State>();
mContext.procs.genQueries(1u, &tq->state->gl.query);
CHECK_GL_ERROR()
}
@@ -176,7 +181,8 @@ TimerQueryFenceFactory::~TimerQueryFenceFactory() {
}
void TimerQueryFenceFactory::createTimerQuery(GLTimerQuery* tq) {
assert_invariant(tq->state);
assert_invariant(!tq->state);
tq->state = std::make_shared<GLTimerQuery::State>();
}
void TimerQueryFenceFactory::destroyTimerQuery(GLTimerQuery* tq) {
@@ -232,7 +238,8 @@ TimerQueryFallbackFactory::TimerQueryFallbackFactory() = default;
TimerQueryFallbackFactory::~TimerQueryFallbackFactory() = default;
void TimerQueryFallbackFactory::createTimerQuery(GLTimerQuery* tq) {
assert_invariant(tq->state);
assert_invariant(!tq->state);
tq->state = std::make_shared<GLTimerQuery::State>();
}
void TimerQueryFallbackFactory::destroyTimerQuery(GLTimerQuery* tq) {
@@ -61,9 +61,14 @@ struct GLTimerQuery : public HwTimerQuery {
*/
class TimerQueryFactory {
static bool mGpuTimeSupported;
public:
static TimerQueryFactoryInterface* init(
OpenGLPlatform& platform, OpenGLContext& context);
static bool isGpuTimeSupported() noexcept {
return mGpuTimeSupported;
}
};
class TimerQueryFactoryInterface {
@@ -117,13 +117,6 @@ struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
cond.wait(l, [this] { return signaled; });
}
// Used in THREAD_POOL mode. Returns true if the token is signaled, meaning it's ready to be
// used.
bool isReady() const noexcept {
std::unique_lock const l(lock);
return signaled;
}
// This is invoked upon token completion, which occurs after a successful `gl.program`
// population or upon cancellation. In either scenario, the callback handle must be submitted
// to notify the caller that resource loading has concluded.
@@ -367,15 +360,14 @@ void ShaderCompilerService::compileProgram(
<< " failed to link or compile";
#endif
}
// The program blob is cached prior to signaling to ensure data integrity.
// Since the receiving thread may immediately modify gl.program (e.g., via
// glUniformBlockBinding) upon receipt of the signal, caching must be
// finalized first.
tryCachingProgram(mBlobCache, mDriver.mPlatform, token);
// Now `token->gl.program` must be populated, so we signal the completion
// of the linking. We don't need to check the result of the program here
// because it'll be done in the engine thread.
token->signal();
// We try caching the program blob after sending the signal. This allows us
// to unblock the engine thread as soon as the token is ready while
// performing an expensive caching operation still in the pool.
tryCachingProgram(mBlobCache, mDriver.mPlatform, token);
// Updates the token's state. If the token is canceled while this function
// executes, this update notifies `tick` that GL resource loading is
// complete, allowing `tick` to proceed with resource destruction.
@@ -532,15 +524,8 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) {
}
void ShaderCompilerService::ensureTokenIsReady(program_token_t const& token) {
if (mMode == Mode::THREAD_POOL) {
// Check if `token->gl.program` is populated
if (token->isReady()) {
return;
}
} else {
if (token->gl.program) {
return;
}
if (token->gl.program) {
return;// It's ready.
}
switch (mMode) {
@@ -950,7 +935,6 @@ void ShaderCompilerService::cancelPendingSynchronousProgram(program_token_t cons
return false;
}
token->retrievedFromBlobCache = true;
token->signal(); // notify that `token->gl.program` is ready to use
return true;
}
@@ -80,9 +80,6 @@ JNIEnv* ExternalStreamManagerAndroid::getEnvironmentSlow() noexcept {
Stream* ExternalStreamManagerAndroid::acquire(jobject surfaceTexture) noexcept {
// note: This is called on the application thread (not the GL thread)
// The application thread *MUST* be attached to the JVM to pass the java 'surfaceTexture' down
// to filament natively, so we are guaranteed to get a valid JNIEnv from getThreadEnvironment()
// without needing AttachCurrentThread.
JNIEnv* env = VirtualMachineEnv::getThreadEnvironment();
if (!env) {
return nullptr; // this should not happen
@@ -34,7 +34,6 @@
#include <utils/Invocable.h>
#include <utils/Logger.h>
#include <utils/Panic.h>
#include <utils/debug.h>
#include <utils/ostream.h>
@@ -120,41 +119,31 @@ bool PlatformEGL::isOpenGL() const noexcept {
PlatformEGL::ExternalImageEGL::~ExternalImageEGL() = default;
void PlatformEGL::setEglDisplay(EGLDisplay display) noexcept {
FILAMENT_CHECK_PRECONDITION(mEGLDisplay == EGL_NO_DISPLAY)
<< "EGL Display has already been set.";
FILAMENT_CHECK_PRECONDITION(display != EGL_NO_DISPLAY)
<< "Must specify a valid EGL Display.";
mEGLDisplay = display;
}
Driver* PlatformEGL::createDriver(void* sharedContext, const DriverConfig& driverConfig) {
if (mEGLDisplay == EGL_NO_DISPLAY) {
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
assert_invariant(mEGLDisplay != EGL_NO_DISPLAY);
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
assert_invariant(mEGLDisplay != EGL_NO_DISPLAY);
EGLint major, minor;
EGLBoolean initialized = eglInitialize(mEGLDisplay, &major, &minor);
EGLint major, minor;
EGLBoolean initialized = eglInitialize(mEGLDisplay, &major, &minor);
if (!initialized) {
EGLDeviceEXT eglDevice;
EGLint numDevices;
PFNEGLQUERYDEVICESEXTPROC const 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, nullptr);
initialized = eglInitialize(mEGLDisplay, &major, &minor);
}
if (!initialized) {
EGLDeviceEXT eglDevice;
EGLint numDevices;
PFNEGLQUERYDEVICESEXTPROC const 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, nullptr);
initialized = eglInitialize(mEGLDisplay, &major, &minor);
}
}
}
if (UTILS_UNLIKELY(!initialized)) {
LOG(ERROR) << "eglInitialize failed";
return nullptr;
}
if (UTILS_UNLIKELY(!initialized)) {
LOG(ERROR) << "eglInitialize failed";
return nullptr;
}
#if defined(FILAMENT_IMPORT_ENTRY_POINTS)
@@ -120,7 +120,8 @@ struct PlatformEGLAndroid::AndroidDetails {
// ---------------------------------------------------------------------------------------------
PlatformEGLAndroid::PlatformEGLAndroid() noexcept
: mAndroidDetails(*(new(std::nothrow) AndroidDetails{})) {
: mExternalStreamManager(ExternalStreamManagerAndroid::create()),
mAndroidDetails(*(new(std::nothrow) AndroidDetails{})) {
mOSVersion = android_get_device_api_level();
if (mOSVersion < 0) {
mOSVersion = __ANDROID_API_FUTURE__;
@@ -134,10 +135,7 @@ PlatformEGLAndroid::~PlatformEGLAndroid() noexcept {
void PlatformEGLAndroid::terminate() noexcept {
mPerformanceHintManager.terminate();
mAndroidDetails.androidFrameCallback.terminate();
if (mExternalStreamManager) {
ExternalStreamManagerAndroid::destroy(mExternalStreamManager);
mExternalStreamManager = nullptr;
}
ExternalStreamManagerAndroid::destroy(&mExternalStreamManager);
PlatformEGL::terminate();
}
@@ -227,10 +225,6 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
mPerformanceHintSession = PerformanceHintManager::Session{
mPerformanceHintManager, &tid, 1, 16'666'667 };
mExternalStreamManager = &ExternalStreamManagerAndroid::create();
FILAMENT_CHECK_POSTCONDITION(mExternalStreamManager)
<< "Failed to create ExternalStreamManagerAndroid";
Driver* driver = PlatformEGL::createDriver(sharedContext, driverConfig);
auto const extensions = GLUtils::split(eglQueryString(getEglDisplay(), EGL_EXTENSIONS));
@@ -617,13 +611,11 @@ void PlatformEGLAndroid::setPresentationTime(int64_t const presentationTimeInNan
}
Platform::Stream* PlatformEGLAndroid::createStream(void* nativeStream) noexcept {
assert_invariant(mExternalStreamManager);
return mExternalStreamManager->acquire(static_cast<jobject>(nativeStream));
return mExternalStreamManager.acquire(static_cast<jobject>(nativeStream));
}
void PlatformEGLAndroid::destroyStream(Stream* stream) noexcept {
assert_invariant(mExternalStreamManager);
mExternalStreamManager->release(stream);
mExternalStreamManager.release(stream);
}
Platform::Sync* PlatformEGLAndroid::createSync() noexcept {
@@ -675,23 +667,19 @@ void PlatformEGLAndroid::destroySync(Sync* sync) noexcept {
}
void PlatformEGLAndroid::attach(Stream* stream, intptr_t const tname) noexcept {
assert_invariant(mExternalStreamManager);
mExternalStreamManager->attach(stream, tname);
mExternalStreamManager.attach(stream, tname);
}
void PlatformEGLAndroid::detach(Stream* stream) noexcept {
assert_invariant(mExternalStreamManager);
mExternalStreamManager->detach(stream);
mExternalStreamManager.detach(stream);
}
void PlatformEGLAndroid::updateTexImage(Stream* stream, int64_t* timestamp) noexcept {
assert_invariant(mExternalStreamManager);
mExternalStreamManager->updateTexImage(stream, timestamp);
mExternalStreamManager.updateTexImage(stream, timestamp);
}
math::mat3f PlatformEGLAndroid::getTransformMatrix(Stream* stream) noexcept {
assert_invariant(mExternalStreamManager);
return mExternalStreamManager->getTransformMatrix(stream);
return mExternalStreamManager.getTransformMatrix(stream);
}
int PlatformEGLAndroid::getOSVersion() const noexcept {
@@ -137,14 +137,6 @@ public:
return mFenceExportFlags;
}
inline const char* getPhysicalDeviceName() const noexcept {
return mPhysicalDeviceProperties.properties.deviceName;
}
inline const char* getDriverName() const noexcept { return mDriverProperties.driverName; }
inline const char* getDriverInfo() const noexcept { return mDriverProperties.driverInfo; }
inline bool isImageCubeArraySupported() const noexcept {
return mPhysicalDeviceFeatures.features.imageCubeArray == VK_TRUE;
}
@@ -220,16 +212,11 @@ public:
return mGlobalPrioritySupported;
}
inline bool isDriverPropertiesSupported() const noexcept { return mDriverPropertiesSupported; }
private:
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
};
VkPhysicalDeviceDriverProperties mDriverProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES,
};
VkPhysicalDeviceVulkan11Features mPhysicalDeviceVk11Features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
};
@@ -259,7 +246,6 @@ private:
bool mProtectedMemorySupported = false;
bool mVertexInputDynamicStateSupported = false;
bool mGlobalPrioritySupported = false;
bool mDriverPropertiesSupported = false;
// These are options that can be enabled or disabled at an application level.
bool mAsyncPipelineCachePrewarmingEnabled = false;
+1 -2
View File
@@ -260,8 +260,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
mPipelineCache(*this, mPlatform->getDevice(), mContext),
mStagePool(mAllocator, &mResourceManager, &mCommands, &mContext.getPhysicalDeviceLimits()),
mBufferCache(mContext, mResourceManager, mAllocator),
mFramebufferCache(mPlatform->getDevice(),
mPlatform->getCustomization().timeBeforeEvictionFbo),
mFramebufferCache(mPlatform->getDevice()),
mYcbcrConversionCache(mPlatform->getDevice()),
mSamplerCache(mPlatform->getDevice()),
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
+22 -29
View File
@@ -20,7 +20,6 @@
#include "VulkanHandles.h"
#include "vulkan/utils/Image.h"
#include <utils/compiler.h>
#include <utils/Panic.h>
// If any VkRenderPass or VkFramebuffer is unused for more than TIME_BEFORE_EVICTION frames, it
@@ -63,9 +62,8 @@ bool VulkanFboCache::FboKeyEqualFn::operator()(const FboKey& k1, const FboKey& k
return true;
}
VulkanFboCache::VulkanFboCache(VkDevice device, uint32_t timeBeforeEvictionFbo)
: mDevice(device),
mTimeBeforeEvictionFbo(timeBeforeEvictionFbo) {}
VulkanFboCache::VulkanFboCache(VkDevice device)
: mDevice(device) {}
VulkanFboCache::~VulkanFboCache() {
FILAMENT_CHECK_POSTCONDITION(mFramebufferCache.empty() && mRenderPassCache.empty())
@@ -378,36 +376,31 @@ void VulkanFboCache::gc() noexcept {
FVK_SYSTRACE_START("fbocache::gc");
// If this is one of the first few frames, return early to avoid wrapping unsigned integers.
++mCurrentTime;
if (++mCurrentTime <= TIME_BEFORE_EVICTION) {
return;
}
const uint32_t evictTime = mCurrentTime - TIME_BEFORE_EVICTION;
if (UTILS_UNLIKELY(mCurrentTime > mTimeBeforeEvictionFbo)) {
const uint32_t evictTimeFbo = mCurrentTime - mTimeBeforeEvictionFbo;
for (FboMap::iterator iter = mFramebufferCache.begin(); iter != mFramebufferCache.end();) {
const FboVal fbo = iter->second;
if (fbo.timestamp < evictTimeFbo && fbo.handle) {
mRenderPassRefCount[iter->first.renderPass]--;
for (FboMap::iterator iter = mFramebufferCache.begin(); iter != mFramebufferCache.end(); ) {
const FboVal fbo = iter->second;
if (fbo.timestamp < evictTime && fbo.handle) {
mRenderPassRefCount[iter->first.renderPass]--;
// erase(iterator) returns the iterator to the next element.
iter = mFramebufferCache.erase(iter);
} else {
++iter;
}
// erase(iterator) returns the iterator to the next element.
iter = mFramebufferCache.erase(iter);
} else {
++iter;
}
}
if (UTILS_UNLIKELY(mCurrentTime > TIME_BEFORE_EVICTION)) {
const uint32_t evictTimeRp = mCurrentTime - TIME_BEFORE_EVICTION;
for (RenderPassMap::iterator iter = mRenderPassCache.begin();
iter != mRenderPassCache.end();) {
const VkRenderPass handle = iter->second.handle->getVkRenderPass();
if (iter->second.timestamp < evictTimeRp && handle &&
mRenderPassRefCount[handle] == 0) {
// erase(iterator) returns the iterator to the next element.
iter = mRenderPassCache.erase(iter);
mRenderPassRefCount.erase(handle);
} else {
++iter;
}
for (RenderPassMap::iterator iter = mRenderPassCache.begin(); iter != mRenderPassCache.end(); ) {
const VkRenderPass handle = iter->second.handle->getVkRenderPass();
if (iter->second.timestamp < evictTime && handle && mRenderPassRefCount[handle] == 0) {
// erase(iterator) returns the iterator to the next element.
iter = mRenderPassCache.erase(iter);
mRenderPassRefCount.erase(handle);
} else {
++iter;
}
}
+1 -2
View File
@@ -100,7 +100,7 @@ public:
bool operator()(const FboKey& k1, const FboKey& k2) const;
};
explicit VulkanFboCache(VkDevice device, uint32_t timeBeforeEvictionFbo);
explicit VulkanFboCache(VkDevice device);
~VulkanFboCache();
// Retrieves or creates a VkFramebuffer handle.
@@ -130,7 +130,6 @@ private:
RenderPassMap mRenderPassCache;
tsl::robin_map<VkRenderPass, uint32_t> mRenderPassRefCount;
uint32_t mCurrentTime = 0;
uint32_t mTimeBeforeEvictionFbo;
};
} // namespace filament::backend
@@ -704,30 +704,6 @@ VulkanPlatform::VulkanPlatform() = default;
VulkanPlatform::~VulkanPlatform() = default;
utils::CString VulkanPlatform::getDeviceInfo(DeviceInfoType infoType,
Driver* driver) const noexcept {
if (mImpl->mPhysicalDevice == VK_NULL_HANDLE) {
return {};
}
auto& context = mImpl->mContext;
switch (infoType) {
case DeviceInfoType::VULKAN_DEVICE_NAME: {
return utils::CString(context.getPhysicalDeviceName());
}
case DeviceInfoType::VULKAN_DRIVER_NAME: {
return context.isDriverPropertiesSupported() ? utils::CString(context.getDriverName())
: utils::CString();
}
case DeviceInfoType::VULKAN_DRIVER_INFO: {
return context.isDriverPropertiesSupported() ? utils::CString(context.getDriverInfo())
: utils::CString();
}
default:
FILAMENT_CHECK_POSTCONDITION(false) << "Unsupported DeviceInfoType for VulkanPlatform";
return {};
}
}
VulkanPlatform::SwapChainBundle VulkanPlatform::getSwapChainBundle(SwapChainPtr handle) {
return static_cast<VulkanPlatformSwapChainBase*>(handle)->getSwapChainBundle();
}
@@ -976,11 +952,6 @@ void VulkanPlatform::queryAndSetDeviceFeatures(Platform::DriverConfig const& dri
chainStruct(&context.mPhysicalDeviceFeatures, &globalPriorityFeatures);
}
if (vkGetPhysicalDeviceProperties2) {
chainStruct(&context.mPhysicalDeviceProperties, &context.mDriverProperties);
context.mDriverPropertiesSupported = true;
}
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
// physicalDeviceFeatures.
vkGetPhysicalDeviceProperties2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties);
+34 -28
View File
@@ -192,15 +192,8 @@ void WebGPUDriver::flush(int) {
}
void WebGPUDriver::finish(int /* dummy */) {
mReadPixelMapsCounter.waitForAllToFinish();
mQueueManager.finish();
// We use polling to advance webgpu's callback counter until all the read backs have been
// processed. Note that blocking with mReadPixelMapsCounter.waitForAllToFinish will only
// deadlock since we could not advance the counter.
while (!mReadPixelMapsCounter.isIdle()) {
mAdapter.GetInstance().ProcessEvents();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
void WebGPUDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
@@ -947,6 +940,10 @@ size_t WebGPUDriver::getUniformBufferOffsetAlignment(){
void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> indexBufferHandle,
BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) {
// make sure command elements (draws, etc.) prior to the buffer update are processed before the
// update on the GPU, otherwise the expected data may not be available at the time certain
// draw calls are made.
flush();
handleCast<WebGPUIndexBuffer>(indexBufferHandle)
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager, &mStagePool);
scheduleDestroy(std::move(bufferDescriptor));
@@ -961,6 +958,10 @@ void WebGPUDriver::updateIndexBufferAsyncR(AsyncCallId jobId,
void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> bufferObjectHandle,
BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) {
// make sure command elements (draws, etc.) prior to the buffer update are processed before the
// update on the GPU, otherwise the expected data may not be available at the time certain
// draw calls are made.
flush();
handleCast<WebGPUBufferObject>(bufferObjectHandle)
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager, &mStagePool);
scheduleDestroy(std::move(bufferDescriptor));
@@ -1007,6 +1008,10 @@ void WebGPUDriver::update3DImage(Handle<HwTexture> textureHandle, const uint32_t
const uint32_t xoffset, const uint32_t yoffset, const uint32_t zoffset,
const uint32_t width, const uint32_t height, const uint32_t depth,
PixelBufferDescriptor&& pixelBufferDescriptor) {
// one way or another this function writes texture(s), thus any commands (draw calls etc.) should
// get submitted prior to these updates so that subsequent commands/draws run with the
// image/texture(s) updated as expected.
flush();
PixelBufferDescriptor* inputData{ &pixelBufferDescriptor };
PixelBufferDescriptor reshapedData;
if (reshape(pixelBufferDescriptor, reshapedData)) {
@@ -1178,6 +1183,11 @@ void WebGPUDriver::generateMipmaps(Handle<HwTexture> textureHandle) {
return; // nothing to do
}
// make sure command elements (draws, etc.) prior to the texture update are processed before the
// update on the GPU.
// this ensures subsequent draw calls are run after the mipmaps have been generated as expected
flush();
const auto usage = wgpuTexture.GetUsage();
FILAMENT_CHECK_PRECONDITION(usage & wgpu::TextureUsage::TextureBinding)
<< "Texture for mipmap generation must have TextureBinding usage.";
@@ -1366,9 +1376,6 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
customDepthStencilMsaaSidecarTextureView);
mRenderPassEncoder = commandEncoder.BeginRenderPass(&renderPassDescriptor);
// TODO: there's a bug here because the webgpu viewport has the origin at top-left, whereas
// filament expects it to be bottom left.
mRenderPassEncoder.SetViewport(
static_cast<float>(params.viewport.left),
static_cast<float>(params.viewport.bottom),
@@ -1456,6 +1463,7 @@ void WebGPUDriver::stopCapture(int /* dummy */) {
void WebGPUDriver::readPixels(Handle<HwRenderTarget> sourceRenderTargetHandle, const uint32_t x,
const uint32_t y, const uint32_t width, const uint32_t height,
PixelBufferDescriptor&& pixelBufferDescriptor) {
flush();
const auto srcTarget{ handleCast<WebGPURenderTarget>(sourceRenderTargetHandle) };
assert_invariant(srcTarget);
@@ -1513,7 +1521,10 @@ void WebGPUDriver::readTextureToBuffer(wgpu::Texture srcTexture, uint32_t level,
return;
}
auto commandEncoder = mQueueManager.getCommandEncoder();
const wgpu::CommandEncoderDescriptor commandEncoderDescriptor{
.label = "read_texture_to_buffer_command",
};
auto commandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
FILAMENT_CHECK_POSTCONDITION(commandEncoder)
<< "Failed to create command encoder for readTextureToBuffer?";
@@ -1635,6 +1646,9 @@ void WebGPUDriver::readTextureToBuffer(wgpu::Texture srcTexture, uint32_t level,
.depthOrArrayLayers = 1,
};
commandEncoder.CopyTextureToBuffer(&source, &destination, &copySize);
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
assert_invariant(commandBuffer);
mDevice.GetQueue().Submit(1, &commandBuffer);
// Map the buffer to read the data
struct UserData final {
@@ -1655,9 +1669,6 @@ void WebGPUDriver::readTextureToBuffer(wgpu::Texture srcTexture, uint32_t level,
});
mReadPixelMapsCounter.startTask();
// We need to flush here before we can readback from the staging buffer since the copy op is
// pending to be submitted.
mQueueManager.flush();
userData->buffer.MapAsync(
wgpu::MapMode::Read, 0, bufferSize, wgpu::CallbackMode::AllowSpontaneous,
[](wgpu::MapAsyncStatus status, const char* message, UserData* userdata) {
@@ -1750,6 +1761,7 @@ void WebGPUDriver::blitDEPRECATED(TargetBufferFlags buffers,
const wgpu::Extent2D destinationSize{ static_cast<uint32_t>(destinationViewport.width),
static_cast<uint32_t>(destinationViewport.height) };
mQueueManager.flush();
wgpu::CommandEncoder commandEncoder = mQueueManager.getCommandEncoder();
const WebGPUBlitter::BlitArgs blitArgs{
.source = { .texture = sourceTexture->getTexture(),
@@ -1769,6 +1781,8 @@ void WebGPUDriver::blitDEPRECATED(TargetBufferFlags buffers,
.filter = filter,
};
mBlitter.blit(mDevice.GetQueue(), commandEncoder, blitArgs);
mQueueManager.flush();
}
void WebGPUDriver::resolve(Handle<HwTexture> destinationTextureHandle, const uint8_t sourceLevel,
@@ -1808,6 +1822,7 @@ void WebGPUDriver::blit(Handle<HwTexture> destinationTextureHandle, const uint8_
Handle<HwTexture> sourceTextureHandle, const uint8_t destinationLevel,
const uint8_t destinationLayer, const math::uint2 sourceOrigin, const math::uint2 size) {
FWGPU_SYSTRACE_SCOPE();
mQueueManager.flush();
wgpu::CommandEncoder commandEncoder = mQueueManager.getCommandEncoder();
const auto sourceTexture{ handleCast<WebGPUTexture>(sourceTextureHandle) };
const auto destinationTexture{ handleCast<WebGPUTexture>(destinationTextureHandle) };
@@ -1831,6 +1846,7 @@ void WebGPUDriver::blit(Handle<HwTexture> destinationTextureHandle, const uint8_
.filter = SamplerMagFilter::NEAREST,
};
mBlitter.blit(mDevice.GetQueue(), commandEncoder, blitArgs);
mQueueManager.flush();
}
void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
@@ -1970,19 +1986,9 @@ void WebGPUDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGr
//todo
}
void WebGPUDriver::scissor(Viewport scissor) {
assert_invariant(mRenderPassEncoder);
assert_invariant(mCurrentRenderTarget);
// The WebGPU scissor starts from the top-left corner
assert_invariant(scissor.left >= 0 &&
mCurrentRenderTarget->height >= scissor.bottom + scissor.height /*top >= 0*/ &&
scissor.width <= mCurrentRenderTarget->width &&
scissor.height <= mCurrentRenderTarget->height);
mRenderPassEncoder.SetScissorRect(scissor.left,
mCurrentRenderTarget->height - scissor.bottom - scissor.height /*top*/, scissor.width,
scissor.height);
void WebGPUDriver::scissor(
Viewport scissor) {
//todo
}
void WebGPUDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
@@ -50,9 +50,6 @@ public:
[[nodiscard]] bool isHeadless() const { return mType == SwapChainType::HEADLESS; }
[[nodiscard]] uint32_t getWidth() const { return mConfig.width; }
[[nodiscard]] uint32_t getHeight() const { return mConfig.height; }
void present(DriverBase& driver);
void setFrameScheduledCallback(CallbackHandler* handler, FrameScheduledCallback&& callback) {
@@ -41,10 +41,5 @@ void AsyncTaskCounter::waitForAllToFinish() {
mFinishedCondition.wait(lock, [this] { return mTasksInProgress == 0; });
}
bool AsyncTaskCounter::isIdle() {
std::lock_guard<std::mutex> lock{ mMutex };
return mTasksInProgress == 0;
}
} // namespace filament::backend::webgpuutils
@@ -51,11 +51,6 @@ public:
*/
void waitForAllToFinish();
/**
* Check if all tasks have finished (work counter is 0) (thread-safe)
*/
bool isIdle();
private:
std::mutex mMutex;
std::condition_variable mFinishedCondition;
+8 -8
View File
@@ -16,7 +16,7 @@
#include "PlatformRunner.h"
#include <utils/getopt.h>
#include <getopt/getopt.h>
#include <iostream>
#include <string>
@@ -30,18 +30,18 @@ TestArguments parseArguments(int argc, char* argv[]) {
// The first colon in OPTSTR turns on silent error reporting. This is important, as the
// arguments may also contain gtest parameters we don't know about.
static constexpr const char* OPTSTR = ":a:kc";
static const utils::getopt::option OPTIONS[] = {
{ "api", utils::getopt::required_argument, nullptr, 'a' },
{ "headless_only", utils::getopt::no_argument, nullptr, 'k' },
{ "ci", utils::getopt::no_argument, nullptr, 'c' },
{ nullptr, 0, nullptr, 0 } // termination of the utils::getopt::option list
static const struct option OPTIONS[] = {
{ "api", required_argument, nullptr, 'a' },
{ "headless_only", no_argument, nullptr, 'k' },
{ "ci", no_argument, nullptr, 'c' },
{ nullptr, 0, nullptr, 0 } // termination of the option list
};
int opt;
int optionIndex = 0;
while ((opt = utils::getopt::getopt_long(argc, argv, OPTSTR, OPTIONS, &optionIndex)) >= 0) {
std::string arg(utils::getopt::optarg ? utils::getopt::optarg : "");
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &optionIndex)) >= 0) {
std::string arg(optarg ? optarg : "");
switch (opt) {
case 'a':
if (arg == "opengl") {
+3 -4
View File
@@ -35,10 +35,9 @@ test::NativeView getNativeView() {
namespace {
std::array<test::Backend, 3> const VALID_BACKENDS{
test::Backend::OPENGL,
test::Backend::VULKAN,
test::Backend::WEBGPU
std::array<test::Backend, 2> const VALID_BACKENDS{
test::Backend::OPENGL,
test::Backend::VULKAN,
};
}// namespace
-50
View File
@@ -1,50 +0,0 @@
/*
* Copyright (C) 2026 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 <backend/Platform.h>
#include <gtest/gtest.h>
#include <private/backend/PlatformFactory.h>
namespace filament::backend {
TEST(PlatformTest, GetDeviceInfo) {
Backend backend = Backend::DEFAULT;
Platform* platform = PlatformFactory::create(&backend);
ASSERT_NE(platform, nullptr);
// Test valid queries for the current platform (will be either GL, Vulkan, or Metal depending on
// host)
if (backend == Backend::OPENGL) {
platform->getDeviceInfo(Platform::DeviceInfoType::OPENGL_RENDERER, nullptr);
platform->getDeviceInfo(Platform::DeviceInfoType::OPENGL_VENDOR, nullptr);
// Death tests for Vulkan info on OpenGL platform
EXPECT_DEATH(platform->getDeviceInfo(Platform::DeviceInfoType::VULKAN_DEVICE_NAME, nullptr),
"Unsupported DeviceInfoType");
} else if (backend == Backend::VULKAN) {
platform->getDeviceInfo(Platform::DeviceInfoType::VULKAN_DEVICE_NAME, nullptr);
platform->getDeviceInfo(Platform::DeviceInfoType::VULKAN_DRIVER_NAME, nullptr);
platform->getDeviceInfo(Platform::DeviceInfoType::VULKAN_DRIVER_INFO, nullptr);
// Death tests for OpenGL info on Vulkan platform
EXPECT_DEATH(platform->getDeviceInfo(Platform::DeviceInfoType::OPENGL_RENDERER, nullptr),
"Unsupported DeviceInfoType");
}
PlatformFactory::destroy(&platform);
}
} // namespace filament::backend
+1
View File
@@ -32,6 +32,7 @@ using namespace filament;
using namespace filament::backend;
TEST_F(BackendTest, ScissorViewportRegion) {
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
auto& api = getDriverApi();
constexpr int kSrcTexWidth = 1024;
+1 -5
View File
@@ -318,8 +318,6 @@ public:
/**
* Sets how many samples are to be used for MSAA in the post-process stage.
* Default is 1 and disables MSAA.
* Note that post-processing is disabled at FL0. If the feature level is
* set to 0, values passed to this function are ignored.
*
* @param count number of samples to use for multi-sampled anti-aliasing.\n
* 0: treated as 1
@@ -410,9 +408,7 @@ public:
/**
* Enables or disable multi-sample anti-aliasing (MSAA). Disabled by default.
* Note that MSAA is a post-processing effect, and post-processing is disabled at FL0.
* If the feature level is set to 0, values passed to this function are ignored.
*
*
* @param options multi-sample anti-aliasing options
*/
void setMultiSampleAntiAliasingOptions(MultiSampleAntiAliasingOptions options) noexcept;
+1
View File
@@ -227,6 +227,7 @@ bool Froxelizer::prepare(
const mat4f& projection, float const projectionNear, float const projectionFar,
float4 const& clipTransform) noexcept {
assert_invariant(projectionFar > projectionNear);
assert_invariant(projectionNear > 0);
setViewport(viewport);
setProjection(projection, projectionNear, projectionFar);
+8 -14
View File
@@ -106,25 +106,19 @@ Handle<HwProgram> LocalProgramCache::prepareProgramSlow(DriverApi& driver, Varia
assert_invariant(mMaterial != nullptr);
FEngine& engine = mMaterial->getEngine();
Handle<HwProgram> result;
if (mMaterial->isSharedVariant(variant)) {
FMaterial const* defaultMaterial = engine.getDefaultMaterial();
assert_invariant(defaultMaterial);
LocalProgramCache const& defaultPrograms = defaultMaterial->getPrograms();
result = defaultPrograms.mCachedPrograms[variant.key];
if (!result) {
result = defaultPrograms.prepareProgram(driver, variant, priorityQueue);
Handle<HwProgram> program = defaultPrograms.mCachedPrograms[variant.key];
if (program) {
return mCachedPrograms[variant.key] = program;
}
} else {
result = mMaterial->getDefinition().prepareProgram(engine, driver,
mMaterial->getMaterialParser(), getProgramSpecialization(variant), priorityQueue);
return mCachedPrograms[variant.key] =
defaultPrograms.prepareProgram(driver, variant, priorityQueue);
}
FILAMENT_CHECK_POSTCONDITION(result) << "Requested variant " << (uint32_t)variant.key
<< " does not exist for material " << mMaterial->getName();
return mCachedPrograms[variant.key] = result;
return mCachedPrograms[variant.key] = mMaterial->getDefinition().prepareProgram(engine, driver,
mMaterial->getMaterialParser(), getProgramSpecialization(variant), priorityQueue);
}
ProgramSpecialization LocalProgramCache::getProgramSpecialization(Variant variant) const noexcept {
@@ -188,7 +182,7 @@ Program::SpecializationConstant LocalProgramCache::getConstantImpl(
return getConstantImpl(it->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS);
}
CString name_cstring(name);
std::string name_cstring(name);
PANIC_PRECONDITION("No such constant exists: %s", name_cstring.c_str());
return {};
}
+4 -7
View File
@@ -28,11 +28,7 @@ namespace filament {
size_t MaterialCache::MaterialKey::Hash::operator()(
filament::MaterialCache::MaterialKey const& key) const noexcept {
uint32_t crc;
if (key.parser->getMaterialCrc32(&crc)) {
return size_t(crc);
}
return size_t(key.parser->computeCrc32());
return size_t(key.parser->getCrc32());
}
bool MaterialCache::MaterialKey::operator==(MaterialKey const& rhs) const noexcept {
@@ -60,8 +56,9 @@ void MaterialCache::terminate(FEngine& engine) {
MaterialDefinition* UTILS_NULLABLE MaterialCache::acquireMaterial(FEngine& engine,
const void* UTILS_NONNULL data, size_t size) noexcept {
std::unique_ptr<MaterialParser> parser = MaterialDefinition::createParser(engine.getBackend(),
engine.getShaderLanguage(), data, size);
bool const checkCrc32 = engine.features.material.check_crc32_after_loading;
std::unique_ptr<MaterialParser> parser =
MaterialDefinition::createParser(engine, data, size, checkCrc32);
assert_invariant(parser);
// The `key` must be constructed using parser.get() before parser is moved into the lambda
+25 -22
View File
@@ -162,11 +162,30 @@ void releaseProgramsImpl(FEngine& engine, utils::Slice<Handle<HwProgram>> progra
} // namespace
std::unique_ptr<MaterialParser> MaterialDefinition::createParser(Backend const backend,
FixedCapacityVector<ShaderLanguage> languages, const void* data, size_t size) {
std::unique_ptr<MaterialParser> MaterialDefinition::createParser(FEngine const& engine,
const void* data, size_t size, bool checkCrc32) {
auto const& languages = engine.getShaderLanguage();
auto const backend = engine.getBackend();
// unique_ptr so we don't leak MaterialParser on failures below
auto materialParser = std::make_unique<MaterialParser>(languages, data, size);
// Try checking CRC32 value for the package and skip if it's unavailable.
if (checkCrc32) {
uint32_t parsedCrc32 = 0;
bool const foundParsedCrc = materialParser->getMaterialCrc32(&parsedCrc32);
uint32_t const expectedCrc32 = materialParser->computeCrc32();
if (foundParsedCrc && parsedCrc32 != expectedCrc32) {
CString name;
materialParser->getName(&name);
LOG(ERROR) << "The material '" << name.c_str_safe()
<< "' is corrupted: crc32_expected=" << expectedCrc32
<< ", crc32_parsed=" << parsedCrc32;
return nullptr;
}
}
MaterialParser::ParseResult const materialResult = materialParser->parse();
CString name;
@@ -174,7 +193,7 @@ std::unique_ptr<MaterialParser> MaterialDefinition::createParser(Backend const b
if (UTILS_UNLIKELY(materialResult == MaterialParser::ParseResult::ERROR_MISSING_BACKEND)) {
CString languageNames;
for (auto it = languages.begin(); it != languages.end(); ++it) {
languageNames.append(CString{shaderLanguageToString(*it)});
languageNames.append(CString{ shaderLanguageToString(*it) });
if (std::next(it) != languages.end()) {
languageNames.append(", ");
}
@@ -182,8 +201,9 @@ std::unique_ptr<MaterialParser> MaterialDefinition::createParser(Backend const b
FILAMENT_CHECK_POSTCONDITION(
materialResult != MaterialParser::ParseResult::ERROR_MISSING_BACKEND)
<< "the material " << name.c_str_safe() << " was not built for any of the " << to_string(backend)
<< " backend's supported shader languages (" << languageNames.c_str() << ")\n";
<< "the material " << name.c_str_safe() << " was not built for any of the "
<< to_string(backend) << " backend's supported shader languages ("
<< languageNames.c_str() << ")\n";
}
if (backend == Backend::NOOP) {
@@ -206,23 +226,6 @@ std::unique_ptr<MaterialParser> MaterialDefinition::createParser(Backend const b
std::unique_ptr<MaterialDefinition> MaterialDefinition::create(FEngine& engine,
std::unique_ptr<MaterialParser> parser) {
// Try checking CRC32 value for the package and skip if it's unavailable.
if (downcast(engine).features.material.check_crc32_after_loading) {
uint32_t parsedCrc32 = 0;
parser->getMaterialCrc32(&parsedCrc32);
uint32_t const expectedCrc32 = parser->computeCrc32();
if (parsedCrc32 != expectedCrc32) {
CString name;
parser->getName(&name);
LOG(ERROR) << "The material '" << name.c_str_safe()
<< "' is corrupted: crc32_expected=" << expectedCrc32
<< ", crc32_parsed=" << parsedCrc32;
return nullptr;
}
}
uint32_t v = 0;
parser->getShaderModels(&v);
bitset32 shaderModels;
+2 -3
View File
@@ -165,9 +165,8 @@ private:
friend class MaterialCache;
friend class FMaterial; // for onEditCallback
static std::unique_ptr<MaterialParser> createParser(backend::Backend const backend,
utils::FixedCapacityVector<backend::ShaderLanguage> languages,
const void* UTILS_NONNULL data, size_t size);
static std::unique_ptr<MaterialParser> createParser(FEngine const& engine,
const void* UTILS_NONNULL data, size_t size, bool checkCrc32);
static std::unique_ptr<MaterialDefinition> create(FEngine& engine,
std::unique_ptr<MaterialParser> parser);
+32 -33
View File
@@ -44,6 +44,7 @@
#include <array>
#include <atomic>
#include <mutex>
#include <optional>
#include <tuple>
#include <utility>
@@ -120,12 +121,10 @@ bool MaterialParser::operator==(MaterialParser const& rhs) const noexcept {
if (mImpl.mManagedBuffer.size() != rhs.mImpl.mManagedBuffer.size()) {
return false;
}
std::optional<uint32_t> lhsCrc32 = getPrecomputedCrc32();
if (lhsCrc32) {
std::optional<uint32_t> rhsCrc32 = rhs.getPrecomputedCrc32();
if (rhsCrc32 && *lhsCrc32 != *rhsCrc32) {
return false;
}
uint32_t const lhsCrc32 = getCrc32();
uint32_t const rhsCrc32 = rhs.getCrc32();
if (lhsCrc32 != rhsCrc32) {
return false;
}
return !memcmp(mImpl.mManagedBuffer.data(), rhs.mImpl.mManagedBuffer.data(),
mImpl.mManagedBuffer.size());
@@ -187,12 +186,34 @@ MaterialParser::ParseResult MaterialParser::parse() noexcept {
return ParseResult::SUCCESS;
}
uint32_t MaterialParser::computeCrc32() const noexcept {
uint32_t crc32 = mCrc32.load(std::memory_order_relaxed);
if (crc32) {
return crc32;
uint32_t MaterialParser::getCrc32() const noexcept {
if (mCrc32Cached.load(std::memory_order_relaxed)) {
return mCrc32;
}
std::lock_guard<utils::Mutex> lock(mCrc32CachedLock);
// If we enter this section after another thread has passed through it, we need to check whether
// crc32 has been cached or not. This is the slow path that will happen hopefully just once.
if (mCrc32Cached.load(std::memory_order_relaxed)) {
return mCrc32;
}
// First check whether the compiled material contains the crc32 already.
if (uint32_t parsedCrc32 = 0; getMaterialCrc32(&parsedCrc32)) {
mCrc32 = parsedCrc32;
mCrc32Cached.store(true);
return parsedCrc32;
}
// No crc32 found, so we compute it.
mCrc32 = computeCrc32();
mCrc32Cached.store(true);
return mCrc32;
}
uint32_t MaterialParser::computeCrc32() const noexcept {
const size_t size = mImpl.mManagedBuffer.size();
const void* const UTILS_NONNULL payload = mImpl.mManagedBuffer.data();
@@ -202,29 +223,7 @@ uint32_t MaterialParser::computeCrc32() const noexcept {
std::vector<uint32_t> crc32Table;
utils::hash::crc32GenerateTable(crc32Table);
crc32 = utils::hash::crc32Update(0, payload, originalSize, crc32Table);
mCrc32.store(crc32, std::memory_order_relaxed);
return crc32;
}
std::optional<uint32_t> MaterialParser::getPrecomputedCrc32() const noexcept {
uint32_t cachedCrc32 = mCrc32.load(std::memory_order_relaxed);
if (cachedCrc32) {
return cachedCrc32;
}
uint32_t parsedCrc32;
if (getMaterialCrc32(&parsedCrc32)) {
return parsedCrc32;
}
return std::nullopt;
}
uint32_t MaterialParser::getCrc32() const noexcept {
std::optional<uint32_t> crc32 = getPrecomputedCrc32();
if (crc32) {
return *crc32;
}
return computeCrc32();
return utils::hash::crc32Update(0, payload, originalSize, crc32Table);
}
ShaderLanguage MaterialParser::getShaderLanguage() const noexcept {
+10 -8
View File
@@ -30,6 +30,7 @@
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Mutex.h>
#include <array>
#include <optional>
@@ -70,13 +71,13 @@ public:
ParseResult parse() noexcept;
// Compute the CRC32 of the material or return the cached value.
uint32_t computeCrc32() const noexcept;
// Return the cached computed CRC32 or the CRC32 built into the material file if one exists.
std::optional<uint32_t> getPrecomputedCrc32() const noexcept;
// Return the CRC32 of the material.
// Return the CRC32 of the material. This will use a cached value if it is available.
uint32_t getCrc32() const noexcept;
// Return the CRC32 of the material. This will *always* compute the crc32 from the full compiled
// binary content (i.e. this is the slow path).
uint32_t computeCrc32() const noexcept;
backend::ShaderLanguage getShaderLanguage() const noexcept;
// Accessors
@@ -196,9 +197,10 @@ private:
filaflat::ChunkContainer& getChunkContainer() noexcept;
filaflat::ChunkContainer const& getChunkContainer() const noexcept;
MaterialParserDetails mImpl;
// 0 == not cached. This technically means that a file with a CRC32 of 0 will never be cached,
// but this is unlikely, and keeping it a 32-bit value guarantees that it will be lockless.
mutable std::atomic<uint32_t> mCrc32 = 0;
mutable uint32_t mCrc32 = 0;
mutable std::atomic<bool> mCrc32Cached;
mutable utils::Mutex mCrc32CachedLock;
};
struct ChunkUniformInterfaceBlock {
+3 -31
View File
@@ -49,7 +49,6 @@
#include "fsr.h"
#include "FrameHistory.h"
#include "RenderPass.h"
#include "ShadowMapManager.h"
#include "details/Camera.h"
#include "details/ColorGrading.h"
@@ -2248,18 +2247,6 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
float bloomHeight = float(inoutBloomOptions.resolution);
float bloomWidth = bloomHeight * aspect;
// With extreme aspect ratios the major axis can exceed the GPU's maximum
// texture dimension. Scale both axes down proportionally so that the larger
// one stays within the hardware limit.
const float maxDimension = float(
FTexture::getMaxTextureSize(mEngine, SamplerType::SAMPLER_2D));
const float bloomMajor = std::max(bloomWidth, bloomHeight);
if (bloomMajor > maxDimension) {
const float clampScale = maxDimension / bloomMajor;
bloomWidth *= clampScale;
bloomHeight *= clampScale;
}
// we might need to adjust the max # of levels
const uint32_t major = uint32_t(std::max(bloomWidth, bloomHeight));
const uint8_t maxLevels = FTexture::maxLevelCount(major);
@@ -3843,7 +3830,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
}
FrameGraphId<FrameGraphTexture> PostProcessManager::debugShadowCascades(FrameGraph& fg,
ShadowMapManager const& smm,
FrameGraphId<FrameGraphTexture> const input,
FrameGraphId<FrameGraphTexture> const depth) noexcept {
@@ -3861,31 +3847,17 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugShadowCascades(FrameGra
data.output = builder.createTexture("Shadow Cascade Debug", desc);
builder.declareRenderPass(data.output);
},
[=, &smm, this](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
[=, this](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto color = resources.getTexture(data.color);
auto depth = resources.getTexture(data.depth);
auto const out = resources.getRenderPassInfo();
auto const& smu = smm.getShadowMappingUniforms();
mat4f lightFromWorldMatrix[4] = {};
float4 scissorNormalized[4] = {};
for (size_t i = 0, c = std::max(4u, (smu.cascades & 0xF)) ; i < c; i++) {
auto const& csp = smm.getCascadeShaderParameters(i);
lightFromWorldMatrix[i] = csp.lightSpace;
scissorNormalized[i] = csp.scissorNormalized;
}
auto const& material = getPostProcessMaterial("debugShadowCascades");
FMaterialInstance* const mi = getMaterialInstance(mEngine, driver, material);
FMaterialInstance* const mi =
getMaterialInstance(mEngine, driver, material);
mi->setParameter("color", color, SamplerParams{}); // nearest
mi->setParameter("depth", depth, SamplerParams{}); // nearest
mi->setParameter("cascadeSplits", smu.cascadeSplits);
mi->setParameter("cascadeCount", smu.cascades & 0xF);
mi->setParameter("shadowAtlasResolution", smu.atlasResolution);
mi->setParameter("lightFromWorldMatrix", lightFromWorldMatrix, 4);
mi->setParameter("scissorNormalized", scissorNormalized, 4);
commitAndRenderFullScreenQuad(driver, out, mi);
unbindAllDescriptorSets(driver);
});
-2
View File
@@ -66,7 +66,6 @@ class FMaterialInstance;
class FrameGraph;
class RenderPass;
class RenderPassBuilder;
class ShadowMapManager;
class UboManager;
struct CameraInfo;
@@ -323,7 +322,6 @@ public:
bool reinhard, size_t kernelWidth, float sigma) noexcept;
FrameGraphId<FrameGraphTexture> debugShadowCascades(FrameGraph& fg,
ShadowMapManager const& smm,
FrameGraphId<FrameGraphTexture> input,
FrameGraphId<FrameGraphTexture> depth) noexcept;
+7 -11
View File
@@ -681,7 +681,6 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
ShadowTechnique shadowTechnique{};
uint32_t directionalShadowsMask = 0;
uint32_t cascadeHasVisibleShadows = 0;
float4 wsSplitPositionUniform = { -std::numeric_limits<float>::infinity() };
if (hasVisibleShadows) {
uint32_t const cascadeCount = cascadedShadowMaps.size();
@@ -705,8 +704,11 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
// (which is the near plane, and doesn't need to be communicated to the shaders).
static_assert(CONFIG_MAX_SHADOW_CASCADES <= 5,
"At most, a float4 can fit 4 split positions for 5 shadow cascades");
float4 wsSplitPositionUniform{ -std::numeric_limits<float>::infinity() };
std::copy(splits.begin() + 1, splits.end(), &wsSplitPositionUniform[0]);
mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform;
// When computing the required bias we need a half-texel size, so we multiply by 0.5 here.
// note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0);
@@ -731,8 +733,6 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo,
canUseDepthClamp);
mCascadesShaderParameters[i] = shaderParameters;
if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex();
assert_invariant(shadowIndex == i);
@@ -775,14 +775,10 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
cascades |= uint32_t(cascadedShadowMaps.size());
cascades |= cascadeHasVisibleShadows << 8u;
mShadowMappingUniforms = {
.cascadeSplits = wsSplitPositionUniform,
.ssContactShadowDistance = screenSpaceShadowDistance,
.directionalShadows = directionalShadowsMask,
.cascades = cascades,
.atlasResolution = { mTextureAtlasRequirements.size, 1.0f / float(mTextureAtlasRequirements.size) }
};
mShadowMappingUniforms.directionalShadows = directionalShadowsMask;
mShadowMappingUniforms.ssContactShadowDistance = screenSpaceShadowDistance;
mShadowMappingUniforms.cascades = cascades;
return shadowTechnique;
}
-8
View File
@@ -48,7 +48,6 @@
#include <math/mat4.h>
#include <math/half.h>
#include <math/vec2.h>
#include <math/vec4.h>
#include <algorithm>
@@ -76,7 +75,6 @@ struct ShadowMappingUniforms {
float ssContactShadowDistance;
uint32_t directionalShadows;
uint32_t cascades;
math::float2 atlasResolution;
};
class ShadowMapManager {
@@ -172,10 +170,6 @@ public:
return std::min(targetExponent, filterCeiling);
}
ShadowMap::ShaderParameters const& getCascadeShaderParameters(size_t index) const noexcept {
return mCascadesShaderParameters[index];
}
private:
explicit ShadowMapManager(FEngine& engine);
@@ -259,8 +253,6 @@ private:
ShadowMap::SceneInfo mSceneInfo;
ShadowMap::ShaderParameters mCascadesShaderParameters[4]{};
// Inline storage for all our ShadowMap objects, we can't easily use a std::array<> directly.
// Because ShadowMap doesn't have a default ctor, and we avoid out-of-line allocations.
// Each ShadowMap is currently 88 bytes (total of ~12KB for 128 shadow maps)
+2 -8
View File
@@ -820,7 +820,7 @@ void FEngine::submitFrame() {
void FEngine::flush() {
// flush the command buffer
flushCommandBuffer(mCommandBufferQueue);
// In single-threaded mode, we have to call execute() to drain the command
// buffer to really free up space
if constexpr (!UTILS_HAS_THREADING) {
@@ -889,14 +889,8 @@ int FEngine::loop() {
#endif
if (portString != nullptr) {
const int port = atoi(portString);
ShaderLanguage preferredLanguage = ShaderLanguage::UNSPECIFIED;
if (mBackend == Backend::METAL) {
preferredLanguage = ShaderLanguage::MSL;
}
debug.server = new matdbg::DebugServer(mBackend,
mDriver->getShaderLanguages(preferredLanguage).front(),
mDriver->getShaderLanguages(ShaderLanguage::UNSPECIFIED).front(),
matdbg::DbgShaderModel((uint8_t) mDriver->getShaderModel()), port);
// Sometimes the server can fail to spin up (e.g. if the above port is already in use).
+4 -2
View File
@@ -414,8 +414,10 @@ void FMaterial::onEditCallback(void* userdata, const CString&, const void* packa
// This is called on a web server thread, so we defer clearing the program cache
// and swapping out the MaterialParser until the next getProgram call.
std::unique_ptr<MaterialParser> pending = MaterialDefinition::createParser(
engine.getBackend(), engine.getShaderLanguage(), packageData, packageSize);
// Also note that we don't check for crc32 because it's not meant for a dynamically generated
// material package.
std::unique_ptr<MaterialParser> pending = MaterialDefinition::createParser(engine, packageData,
packageSize, false /* checkCrc32 */);
material->setPendingEdits(std::move(pending));
}
+1 -1
View File
@@ -1362,7 +1362,7 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
// Debug: CSM visualisation
if (UTILS_UNLIKELY(engine.debug.shadowmap.visualize_cascades &&
view.hasShadowing() && view.hasDirectionalLighting())) {
input = ppm.debugShadowCascades(fg, view.getShadowMapManager(), input, depth);
input = ppm.debugShadowCascades(fg, input, depth);
}
// TODO: DoF should be applied here, before TAA -- but if we do this it'll result in a lot of
+3 -9
View File
@@ -106,8 +106,6 @@ FView::FView(FEngine& engine)
{
DriverApi& driver = engine.getDriverApi();
mFeatureLevel = engine.getSupportedFeatureLevel();
auto const& layout = engine.getPerRenderableDescriptorSetLayout();
// initialize the common descriptor set with dummy descriptors
@@ -1140,7 +1138,6 @@ void FView::prepareShadowMapping() const noexcept {
constexpr uint32_t SHADOW_SAMPLING_RUNTIME_PCSS = 3u;
auto& s = mUniforms.edit();
s.cascadeSplits = uniforms.cascadeSplits;
s.shadowAtlasResolution = uniforms.atlasResolution;
s.ssContactShadowDistance = uniforms.ssContactShadowDistance;
s.directionalShadows = int32_t(uniforms.directionalShadows);
s.cascades = int32_t(uniforms.cascades);
@@ -1440,12 +1437,9 @@ void FView::setTemporalAntiAliasingOptions(TemporalAntiAliasingOptions options)
}
void FView::setMultiSampleAntiAliasingOptions(MultiSampleAntiAliasingOptions options) noexcept {
// MSAA is a post-process effect, and post-processing is disabled at FL0
if (mFeatureLevel >= backend::FeatureLevel::FEATURE_LEVEL_1) {
options.sampleCount = uint8_t(options.sampleCount < 1u ? 1u : options.sampleCount);
mMultiSampleAntiAliasingOptions = options;
assert_invariant(!options.enabled || !mRenderTarget || !mRenderTarget->hasSampleableDepth());
}
options.sampleCount = uint8_t(options.sampleCount < 1u ? 1u : options.sampleCount);
mMultiSampleAntiAliasingOptions = options;
assert_invariant(!options.enabled || !mRenderTarget || !mRenderTarget->hasSampleableDepth());
}
void FView::setScreenSpaceReflectionsOptions(ScreenSpaceReflectionsOptions options) noexcept {
+3 -10
View File
@@ -262,12 +262,9 @@ public:
}
void setSampleCount(uint8_t count) noexcept {
// MSAA is a post-process effect, and post-processing is disabled at FL0
if (mFeatureLevel >= backend::FeatureLevel::FEATURE_LEVEL_1) {
count = uint8_t(count < 1u ? 1u : count);
mMultiSampleAntiAliasingOptions.sampleCount = count;
mMultiSampleAntiAliasingOptions.enabled = count > 1u;
}
count = uint8_t(count < 1u ? 1u : count);
mMultiSampleAntiAliasingOptions.sampleCount = count;
mMultiSampleAntiAliasingOptions.enabled = count > 1u;
}
uint8_t getSampleCount() const noexcept {
@@ -509,8 +506,6 @@ public:
MaterialGlobals getMaterialGlobals() const { return mMaterialGlobals; }
ShadowMapManager const& getShadowMapManager() const noexcept { return *mShadowMapManager; }
private:
struct FPickingQuery : public PickingQuery {
private:
@@ -632,8 +627,6 @@ private:
utils::CString mName;
backend::FeatureLevel mFeatureLevel = backend::FeatureLevel::FEATURE_LEVEL_1;
// the following values are set by prepare()
Range mVisibleRenderables;
Range mVisibleDirectionalShadowCasters;
+2
View File
@@ -27,6 +27,8 @@
#include <utils/debug.h>
#include <utils/CString.h>
#include <string>
using namespace filament::backend;
namespace filament {
+7 -25
View File
@@ -18,24 +18,9 @@ material {
precision: high
},
{
type : int,
type : uint,
name : cascadeCount,
precision: high
},
{
type : float2,
name : shadowAtlasResolution,
precision: high
},
{
type : mat4[4],
name : lightFromWorldMatrix,
precision: high
},
{
type : float4[4],
name : scissorNormalized,
precision: high
}
],
variables : [
@@ -74,10 +59,9 @@ fragment {
return vec3(0.0, 0.0, 0.0);
}
int getShadowCascade(highp vec4 position) {
highp float z = position.z / position.w;
ivec4 greaterZ = ivec4(greaterThan(materialParams.cascadeSplits, vec4(z)));
int cascadeCount = materialParams.cascadeCount;
int getShadowCascade(highp float z) {
ivec4 greaterZ = ivec4(greaterThan(frameUniforms.cascadeSplits, vec4(z)));
int cascadeCount = frameUniforms.cascades & 0xF;
return clamp(greaterZ.x + greaterZ.y + greaterZ.z + greaterZ.w, 0, cascadeCount - 1);
}
@@ -86,13 +70,11 @@ fragment {
// depth from the depth buffer
highp float depth = textureLod(materialParams_depth, variable_vertex.xy, 0.0).r;
highp vec2 clip = variable_vertex.xy * 2.0 - vec2(1.0);
// convert to view-space (linear z).
highp vec4 p = mulMat4x4Float3(getViewFromClipMatrix(), vec3(clip, depth));
highp vec4 p = mulMat4x4Float3(getViewFromClipMatrix(), vec3(0, 0, depth));
highp float z = p.z / p.w;
color.rgb *= uintToColorDebug(getShadowCascade(p));
color.rgb *= uintToColorDebug(getShadowCascade(z));
postProcess.color = color;
}
}
+1 -3
View File
@@ -40,10 +40,9 @@ fragment {
sky = vec4(textureCube(materialParams_skybox, variable_eyeDirection.xyz).rgb, 1.0);
#else
sky = vec4(textureLod(materialParams_skybox, variable_eyeDirection.xyz, 0.0).rgb, 1.0);
#endif
sky.rgb *= frameUniforms.iblLuminance;
#endif // MATERIAL_FEATURE_LEVEL == 0
}
#if MATERIAL_FEATURE_LEVEL > 0
if (materialParams.showSun != 0 && frameUniforms.sun.w >= 0.0) {
vec3 direction = normalize(variable_eyeDirection.xyz);
// Assume the sun is a sphere
@@ -54,7 +53,6 @@ fragment {
float gradient = pow(1.0 - saturate(x), frameUniforms.sun.w);
sky.rgb = sky.rgb + gradient * sun;
}
#endif // MATERIAL_FEATURE_LEVEL > 0
material.baseColor = sky;
}
}
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.70.2"
spec.version = "1.69.5"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.70.2/filament-v1.70.2-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.69.5/filament-v1.69.5-ios.tgz" }
spec.libraries = 'c++'
@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 70;
static constexpr size_t MATERIAL_VERSION = 69;
// Those are the api levels that are used in the source material file (.mat)
//
@@ -153,17 +153,13 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
int32_t directionalShadows;
float ssContactShadowDistance;
math::float2 shadowAtlasResolution; // dim, 1/dim
float shadowReserved0;
float shadowReserved1;
// position of cascade splits, in world space (not including the near plane)
// -Inf stored in unused components
math::float4 cascadeSplits;
// bit 0-3: cascade count
// bit 8-11: cascade has visible shadows
int32_t cascades;
float shadowPenumbraRatioScale; // For DPCF or PCSS, scale penumbra ratio for artistic use
float shadowPenumbraRatioScale; // For DPCF or PCSS, scale penumbra ratio for artistic use
math::float2 lightFarAttenuationParams; // a, a/far (a=1/pct-of-far)
// --------------------------------------------------------------------------------------------
@@ -217,7 +213,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
float es2Reserved2;
// bring PerViewUib to 2 KiB
math::float4 reserved[21];
math::float4 reserved[22];
};
// 2 KiB == 128 float4s
+23 -23
View File
@@ -18,7 +18,7 @@
#define TNT_ENUMMANAGER_H
#include <algorithm>
#include <string_view>
#include <string>
#include <unordered_map>
#include <filamat/MaterialBuilder.h>
@@ -37,55 +37,55 @@ using OutputType = MaterialBuilder::OutputType;
using ConstantType = MaterialBuilder::ConstantType;
using ShaderStageType = MaterialBuilder::ShaderStageFlags;
// Convenience methods to convert std::string_view to Enum and also iterate over Enum values.
// Convenience methods to convert std::string to Enum and also iterate over Enum values.
class Enums {
public:
// Returns true if string "s" is a valid string representation of an element of enum T.
template<typename T>
static bool isValid(const std::string_view& s) noexcept {
std::unordered_map<std::string_view, T>& map = getMap<T>();
static bool isValid(const std::string& s) noexcept {
std::unordered_map<std::string, T>& map = getMap<T>();
return map.find(s) != map.end();
}
// Return enum matching its string representation. Returns undefined if s is not a valid enum T
// value. You should always call isValid() first to validate a string before calling toEnum().
template<typename T>
static T toEnum(const std::string_view& s) noexcept {
std::unordered_map<std::string_view, T>& map = getMap<T>();
static T toEnum(const std::string& s) noexcept {
std::unordered_map<std::string, T>& map = getMap<T>();
return map.at(s);
}
template<typename T>
static std::string_view toString(T t) noexcept;
static std::string toString(T t) noexcept;
// Return a map of all values in an enum with their string representation.
template<typename T>
static std::unordered_map<std::string_view, T>& map() noexcept {
auto& map = getMap<T>();
static std::unordered_map<std::string, T>& map() noexcept {
std::unordered_map<std::string, T>& map = getMap<T>();
return map;
};
private:
template<typename T>
static std::unordered_map<std::string_view, T>& getMap() noexcept;
static std::unordered_map<std::string, T>& getMap() noexcept;
static std::unordered_map<std::string_view, Property> mStringToProperty;
static std::unordered_map<std::string_view, UniformType> mStringToUniformType;
static std::unordered_map<std::string_view, SamplerType> mStringToSamplerType;
static std::unordered_map<std::string_view, SubpassType> mStringToSubpassType;
static std::unordered_map<std::string_view, SamplerFormat> mStringToSamplerFormat;
static std::unordered_map<std::string_view, ParameterPrecision> mStringToSamplerPrecision;
static std::unordered_map<std::string_view, OutputTarget> mStringToOutputTarget;
static std::unordered_map<std::string_view, OutputQualifier> mStringToOutputQualifier;
static std::unordered_map<std::string_view, OutputType> mStringToOutputType;
static std::unordered_map<std::string_view, ConstantType> mStringToConstantType;
static std::unordered_map<std::string_view, ShaderStageType> mStringToShaderStageType;
static std::unordered_map<std::string, Property> mStringToProperty;
static std::unordered_map<std::string, UniformType> mStringToUniformType;
static std::unordered_map<std::string, SamplerType> mStringToSamplerType;
static std::unordered_map<std::string, SubpassType> mStringToSubpassType;
static std::unordered_map<std::string, SamplerFormat> mStringToSamplerFormat;
static std::unordered_map<std::string, ParameterPrecision> mStringToSamplerPrecision;
static std::unordered_map<std::string, OutputTarget> mStringToOutputTarget;
static std::unordered_map<std::string, OutputQualifier> mStringToOutputQualifier;
static std::unordered_map<std::string, OutputType> mStringToOutputType;
static std::unordered_map<std::string, ConstantType> mStringToConstantType;
static std::unordered_map<std::string, ShaderStageType> mStringToShaderStageType;
};
template<typename T>
std::string_view Enums::toString(T t) noexcept {
auto& map = getMap<T>();
std::string Enums::toString(T t) noexcept {
std::unordered_map<std::string, T>& map = getMap<T>();
auto result = std::find_if(map.begin(), map.end(), [t](auto& pair) {
return pair.second == t;
});
+22 -24
View File
@@ -18,11 +18,9 @@
#include "filamat/MaterialBuilder.h"
#include <string_view>
namespace filamat {
std::unordered_map<std::string_view, Property> Enums::mStringToProperty = {
std::unordered_map<std::string, Property> Enums::mStringToProperty = {
{ "baseColor", Property::BASE_COLOR },
{ "roughness", Property::ROUGHNESS },
{ "metallic", Property::METALLIC },
@@ -57,11 +55,11 @@ std::unordered_map<std::string_view, Property> Enums::mStringToProperty = {
};
template <>
std::unordered_map<std::string_view, Property>& Enums::getMap<Property>() noexcept {
std::unordered_map<std::string, Property>& Enums::getMap<Property>() noexcept {
return mStringToProperty;
};
std::unordered_map<std::string_view, UniformType> Enums::mStringToUniformType = {
std::unordered_map<std::string, UniformType> Enums::mStringToUniformType = {
{ "bool", UniformType::BOOL },
{ "bool2", UniformType::BOOL2 },
{ "bool3", UniformType::BOOL3 },
@@ -85,11 +83,11 @@ std::unordered_map<std::string_view, UniformType> Enums::mStringToUniformType =
};
template <>
std::unordered_map<std::string_view, UniformType>& Enums::getMap<UniformType>() noexcept {
std::unordered_map<std::string, UniformType>& Enums::getMap<UniformType>() noexcept {
return mStringToUniformType;
};
std::unordered_map<std::string_view, SamplerType> Enums::mStringToSamplerType = {
std::unordered_map<std::string, SamplerType> Enums::mStringToSamplerType = {
{ "sampler2d", SamplerType::SAMPLER_2D },
{ "sampler2dArray", SamplerType::SAMPLER_2D_ARRAY },
{ "sampler3d", SamplerType::SAMPLER_3D },
@@ -99,20 +97,20 @@ std::unordered_map<std::string_view, SamplerType> Enums::mStringToSamplerType =
};
template <>
std::unordered_map<std::string_view, SamplerType>& Enums::getMap<SamplerType>() noexcept {
std::unordered_map<std::string, SamplerType>& Enums::getMap<SamplerType>() noexcept {
return mStringToSamplerType;
};
std::unordered_map<std::string_view, SubpassType> Enums::mStringToSubpassType = {
std::unordered_map<std::string, SubpassType> Enums::mStringToSubpassType = {
{ "subpassInput", SubpassType::SUBPASS_INPUT },
};
template <>
std::unordered_map<std::string_view, SubpassType>& Enums::getMap<SubpassType>() noexcept {
std::unordered_map<std::string, SubpassType>& Enums::getMap<SubpassType>() noexcept {
return mStringToSubpassType;
};
std::unordered_map<std::string_view, ParameterPrecision> Enums::mStringToSamplerPrecision = {
std::unordered_map<std::string, ParameterPrecision> Enums::mStringToSamplerPrecision = {
{ "default", ParameterPrecision::DEFAULT },
{ "low", ParameterPrecision::LOW },
{ "medium", ParameterPrecision::MEDIUM },
@@ -120,30 +118,30 @@ std::unordered_map<std::string_view, ParameterPrecision> Enums::mStringToSampler
};
template <>
std::unordered_map<std::string_view, ParameterPrecision>& Enums::getMap<ParameterPrecision>() noexcept {
std::unordered_map<std::string, ParameterPrecision>& Enums::getMap<ParameterPrecision>() noexcept {
return mStringToSamplerPrecision;
};
std::unordered_map<std::string_view, OutputTarget> Enums::mStringToOutputTarget = {
std::unordered_map<std::string, OutputTarget> Enums::mStringToOutputTarget = {
{ "color", OutputTarget::COLOR },
{ "depth", OutputTarget::DEPTH }
};
template <>
std::unordered_map<std::string_view, OutputTarget>& Enums::getMap<OutputTarget>() noexcept {
std::unordered_map<std::string, OutputTarget>& Enums::getMap<OutputTarget>() noexcept {
return mStringToOutputTarget;
};
std::unordered_map<std::string_view, OutputQualifier> Enums::mStringToOutputQualifier = {
std::unordered_map<std::string, OutputQualifier> Enums::mStringToOutputQualifier = {
{ "out", OutputQualifier::OUT }
};
template <>
std::unordered_map<std::string_view, OutputQualifier>& Enums::getMap<OutputQualifier>() noexcept {
std::unordered_map<std::string, OutputQualifier>& Enums::getMap<OutputQualifier>() noexcept {
return mStringToOutputQualifier;
};
std::unordered_map<std::string_view, OutputType> Enums::mStringToOutputType = {
std::unordered_map<std::string, OutputType> Enums::mStringToOutputType = {
{ "float", OutputType::FLOAT },
{ "float2", OutputType::FLOAT2 },
{ "float3", OutputType::FLOAT3 },
@@ -151,11 +149,11 @@ std::unordered_map<std::string_view, OutputType> Enums::mStringToOutputType = {
};
template <>
std::unordered_map<std::string_view, OutputType>& Enums::getMap<OutputType>() noexcept {
std::unordered_map<std::string, OutputType>& Enums::getMap<OutputType>() noexcept {
return mStringToOutputType;
};
std::unordered_map<std::string_view, SamplerFormat> Enums::mStringToSamplerFormat = {
std::unordered_map<std::string, SamplerFormat> Enums::mStringToSamplerFormat = {
{ "int", SamplerFormat::INT },
{ "uint", SamplerFormat::UINT },
{ "float", SamplerFormat::FLOAT },
@@ -163,29 +161,29 @@ std::unordered_map<std::string_view, SamplerFormat> Enums::mStringToSamplerForma
};
template <>
std::unordered_map<std::string_view, SamplerFormat>& Enums::getMap<SamplerFormat>() noexcept {
std::unordered_map<std::string, SamplerFormat>& Enums::getMap<SamplerFormat>() noexcept {
return mStringToSamplerFormat;
};
std::unordered_map<std::string_view, ConstantType > Enums::mStringToConstantType = {
std::unordered_map<std::string, ConstantType > Enums::mStringToConstantType = {
{ "int", ConstantType::INT },
{ "float", ConstantType::FLOAT },
{ "bool", ConstantType::BOOL },
};
template <>
std::unordered_map<std::string_view, ConstantType>& Enums::getMap<ConstantType>() noexcept {
std::unordered_map<std::string, ConstantType>& Enums::getMap<ConstantType>() noexcept {
return mStringToConstantType;
};
std::unordered_map<std::string_view, ShaderStageType> Enums::mStringToShaderStageType = {
std::unordered_map<std::string, ShaderStageType> Enums::mStringToShaderStageType = {
{ "fragment", ShaderStageType::FRAGMENT },
{ "vertex", ShaderStageType::VERTEX },
{ "compute", ShaderStageType::COMPUTE },
};
template <>
std::unordered_map<std::string_view, ShaderStageType>& Enums::getMap<ShaderStageType>() noexcept {
std::unordered_map<std::string, ShaderStageType>& Enums::getMap<ShaderStageType>() noexcept {
return mStringToShaderStageType;
};
+15 -10
View File
@@ -849,16 +849,17 @@ static void showErrorMessage(const char* materialName, filament::Variant const v
const char* targetApiString = "unknown";
switch (targetApi) {
case TargetApi::OPENGL:
targetApiString = (featureLevel == MaterialBuilder::FeatureLevel::FEATURE_LEVEL_0) ? "GLES 2.0" : "OpenGL";
targetApiString = (featureLevel == MaterialBuilder::FeatureLevel::FEATURE_LEVEL_0)
? "GLES 2.0.\n" : "OpenGL.\n";
break;
case TargetApi::VULKAN:
targetApiString = "Vulkan";
targetApiString = "Vulkan.\n";
break;
case TargetApi::METAL:
targetApiString = "Metal";
targetApiString = "Metal.\n";
break;
case TargetApi::WEBGPU:
targetApiString = "WebGPU";
targetApiString = "WebGPU.\n";
break;
case TargetApi::ALL:
assert_invariant(false); // Unreachable.
@@ -868,20 +869,24 @@ static void showErrorMessage(const char* materialName, filament::Variant const v
const char* shaderStageString = "unknown";
switch (shaderType) {
case ShaderStage::VERTEX:
shaderStageString = "Vertex Shader";
shaderStageString = "Vertex Shader\n";
break;
case ShaderStage::FRAGMENT:
shaderStageString = "Fragment Shader";
shaderStageString = "Fragment Shader\n";
break;
case ShaderStage::COMPUTE:
shaderStageString = "Compute Shader";
shaderStageString = "Compute Shader\n";
break;
}
LOG(ERROR)
<< "Error in \"" << materialName << "\""
<< ", " << shaderStageString
<< ", Variant " << io::hex << +variant.key;
<< ", Variant 0x" << io::hex << +variant.key
<< ", " << targetApiString
<< "=========================\n"
<< "Generated " << shaderStageString
<< "=========================\n"
<< shaderCode;
}
bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Variant>& variants,
@@ -1412,7 +1417,7 @@ bool MaterialBuilder::checkMaterialLevelFeatures(MaterialInfo const& info) const
auto const* stage = to_string(sib.getStageFlags());
for (auto const& sampler: samplers) {
LOG(ERROR) << "\"" << sampler.name.c_str() << "\" "
<< Enums::toString(sampler.type) << " " << stage << '\n';
<< Enums::toString(sampler.type).c_str() << " " << stage << '\n';
}
};
+4 -7
View File
@@ -148,16 +148,16 @@ BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
{ "dynReserved2", 0, Type::INT },
{ "iblLuminance", 0, Type::FLOAT, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 },
{ "iblRoughnessOneLevel", 0, Type::FLOAT },
{ "iblRoughnessOneLevel", 0, Type::FLOAT, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 },
{ "iblSH", 9, Type::FLOAT3 },
// ------------------------------------------------------------------------------------
// Directional Lighting [variant: DIR]
// ------------------------------------------------------------------------------------
{ "lightDirection", 0, Type::FLOAT3, Precision::HIGH },
{ "lightDirection", 0, Type::FLOAT3, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 },
{ "padding0", 0, Type::FLOAT },
{ "lightColorIntensity", 0, Type::FLOAT4, Precision::DEFAULT },
{ "sun", 0, Type::FLOAT4, Precision::DEFAULT },
{ "lightColorIntensity", 0, Type::FLOAT4, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 },
{ "sun", 0, Type::FLOAT4, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 },
{ "shadowFarAttenuationParams", 0, Type::FLOAT2, Precision::HIGH },
// ------------------------------------------------------------------------------------
@@ -165,9 +165,6 @@ BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
// ------------------------------------------------------------------------------------
{ "directionalShadows", 0, Type::INT },
{ "ssContactShadowDistance", 0, Type::FLOAT },
{ "shadowAtlasResolution", 0, Type::FLOAT2, Precision::HIGH },
{ "shadowReserved0", 0, Type::FLOAT },
{ "shadowReserved1", 0, Type::FLOAT },
{ "cascadeSplits", 0, Type::FLOAT4, Precision::HIGH },
{ "cascades", 0, Type::INT },
+1 -1
View File
@@ -44,7 +44,7 @@ target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
target_include_directories(${TARGET} PRIVATE ${filamat_SOURCE_DIR}/src)
set_target_properties(${TARGET} PROPERTIES FOLDER Libs)
target_link_libraries(${TARGET} filamat filabridge utils)
target_link_libraries(${TARGET} getopt filamat filabridge utils)
# ==================================================================================================
# Installation
+2 -2
View File
@@ -41,13 +41,13 @@ bool DirIncluder::operator()(const utils::CString& includedBy, IncludeResult& re
const utils::Path headerPath = getHeaderPath();
if (!headerPath.isFile()) {
utils::slog.e << "File " << headerPath.c_str() << " does not exist." << utils::io::endl;
utils::slog.e << "File " << headerPath << " does not exist." << utils::io::endl;
return false;
}
std::ifstream stream(headerPath.getPath(), std::ios::binary);
if (!stream) {
utils::slog.e << "Unable to open " << headerPath.c_str() << "." << utils::io::endl;
utils::slog.e << "Unable to open " << headerPath << "." << utils::io::endl;
return false;
}

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