Compare commits

..

1 Commits

Author SHA1 Message Date
bridgewaterrobbie
cc22348a08 Texture and Texture View creation 2025-04-28 13:51:27 -04:00
88 changed files with 273 additions and 1071 deletions

View File

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

View File

@@ -1,38 +0,0 @@
name: 'Linux Preqrequisites'
runs:
using: "composite"
steps:
- uses: ./.github/actions/dep-versions
- name: Install Linux Prerequisites
shell: bash
run: |
set -xe
# See https://askubuntu.com/questions/272248/processing-triggers-for-man-db/1476024#1476024
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
# For dawn
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libx11-xcb-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100
set +xe

View File

@@ -1,23 +0,0 @@
name: 'Mac Preqrequisites'
runs:
using: "composite"
steps:
- uses: ./.github/actions/dep-versions
- name: Set up Homebrew
id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Cache Brew
id: brew-cache
uses: actions/cache@v4 # Use a specific version
with:
path: $HOME/Library/Caches/Homebrew
key: ${{ runner.os }}-brew-20250424
- name: Install Mac Prerequisites
shell: bash
run: |
# Install ninja
source ./build/common/get-ninja.sh

View File

@@ -16,9 +16,6 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- name: Run Android Continuous
uses: ./.github/actions/android-continuous
with:

View File

@@ -14,9 +14,6 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- name: Run build script
run: |
cd build/ios && printf "y" | ./build.sh continuous

View File

@@ -14,9 +14,6 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- name: Run build script
run: |
cd build/linux && printf "y" | ./build.sh continuous

View File

@@ -14,9 +14,6 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- name: Run build script
run: |
cd build/mac && printf "y" | ./build.sh continuous

View File

@@ -9,32 +9,22 @@ on:
- main
jobs:
build-desktop-mac:
name: build-mac
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- name: Run build script
run: |
cd build/mac && printf "y" | ./build.sh presubmit
- name: Test material parser
run: |
out/cmake-release/filament/test/test_material_parser
build-desktop:
name: build-desktop
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-14-xlarge, ubuntu-22.04-16core]
build-desktop-linux:
name: build-linux
runs-on: ubuntu-22.04-16core
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- name: Run build script
run: |
cd build/linux && printf "y" | ./build.sh presubmit
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
cd build/$WORKFLOW_OS && printf "y" | ./build.sh presubmit
- name: Test material parser
run: |
out/cmake-release/filament/test/test_material_parser
@@ -45,8 +35,6 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- name: Run build script
run: |
build\windows\build-github.bat presubmit
@@ -60,7 +48,6 @@ jobs:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
@@ -79,7 +66,6 @@ jobs:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- name: Run build script
run: |
cd build/ios && printf "y" | ./build.sh presubmit
@@ -93,9 +79,6 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- name: Run build script
run: |
cd build/web && printf "y" | ./build.sh presubmit
@@ -118,20 +101,30 @@ jobs:
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
- name: Set up Homebrew
id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master
- name: Set up Python
uses: actions/setup-python@v5
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
python-version: '3.x'
- name: Cache Mesa and deps
id: mesa-cache
uses: actions/cache@v4 # Use a specific version
with:
path: mesa
key: ${{ runner.os }}-mesa-deps-2-${{ vars.MESA_VERSION }}
path: |
$HOME/Library/Caches/Homebrew
mesa
key: ${{ runner.os }}-mesa-deps-${{ vars.MESA_VERSION }}
- name: Get Mesa
id: mesa-prereq
run: bash test/utils/get_mesa.sh
env:
MESA_VERSION: ${{ vars.MESA_VERSION }}
run: |
bash test/utils/get_mesa.sh
- name: Run Test
run: bash test/renderdiff/test.sh
run: |
bash test/renderdiff/test.sh
- uses: actions/upload-artifact@v4
with:
name: presubmit-renderdiff-result
@@ -140,13 +133,11 @@ jobs:
validate-wgsl-webgpu:
name: validate-wgsl-webgpu
runs-on: 'ubuntu-24.04-8core'
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- name: Run build script
run: ./build.sh -W debug test_filamat filament
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat filament
- name: Run test
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
@@ -157,16 +148,23 @@ jobs:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- name: Install clang-tidy and deps
- name: Set up Homebrew
id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install prerequisites
run: |
pip install pyyaml
brew install llvm@${GITHUB_LLVM_VERSION}
sudo ln -s "$(brew --prefix llvm)@${GITHUB_LLVM_VERSION}/bin/clang-tidy" "/usr/local/bin/clang-tidy"
brew install llvm
sudo ln -s "$(brew --prefix llvm)/bin/clang-tidy" "/usr/local/bin/clang-tidy"
- name: Run build script
# We need to build before clang-tidy can run analysis
run: |
# This will build for all three desktop backends on mac
./build.sh -p desktop debug gltf_viewer
- name: Run test
run: bash test/code-correctness/test.sh
run: |
bash test/code-correctness/test.sh

View File

@@ -29,42 +29,8 @@ on:
types: [created]
jobs:
build-linux:
name: build-linux
runs-on: ubuntu-22.04-32core
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}-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
build-desktop:
name: build-desktop
runs-on: ${{ matrix.os }}
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
@@ -83,14 +49,15 @@ jobs:
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- uses: ./.github/actions/mac-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
cd build/mac && printf "y" | ./build.sh release
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
cd build/$WORKFLOW_OS && printf "y" | ./build.sh release
cd ../..
mv out/filament-release-darwin.tgz out/filament-${TAG}-mac.tgz
if [ -f out/filament-release-darwin.tgz ]; then mv out/filament-release-darwin.tgz out/filament-${TAG}-mac.tgz; fi;
if [ -f out/filament-release-linux.tgz ]; then mv out/filament-release-linux.tgz out/filament-${TAG}-linux.tgz; fi;
- uses: actions/github-script@v6
env:
TAG: ${{ steps.git_ref.outputs.tag }}
@@ -117,7 +84,6 @@ jobs:
- 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 }}
@@ -155,7 +121,6 @@ jobs:
with:
distribution: 'temurin'
java-version: '17'
- uses: ./.github/actions/linux-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
@@ -206,7 +171,6 @@ jobs:
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- uses: ./.github/actions/mac-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}

View File

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

View File

@@ -14,12 +14,12 @@ echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
*)
exit 0
;;
esac
@@ -30,27 +30,30 @@ set -x
UNAME=`echo $(uname)`
LC_UNAME=`echo $UNAME | tr '[:upper:]' '[:lower:]'`
FILAMENT_ANDROID_CI_BUILD=true
# build-common.sh will generate the following variables:
# $GENERATE_ARCHIVES
# $BUILD_DEBUG
# $BUILD_RELEASE
source `dirname $0`/../common/ci-common.sh
if [[ "$LC_UNAME" == "linux" ]]; then
source `dirname $0`/../linux/ci-common.sh
elif [[ "$LC_UNAME" == "darwin" ]]; then
source `dirname $0`/../mac/ci-common.sh
fi
source `dirname $0`/../common/build-common.sh
if [[ "$GITHUB_WORKFLOW" ]]; then
java_version=$(java -version 2>&1 | head -1 | cut -d'"' -f2 | sed '/^1\./s///' | cut -d'.' -f1)
if [[ "$java_version" < 17 ]]; then
echo "Android builds require Java 17, found version ${java_version} instead"
exit 1
exit 0
fi
fi
# Unless explicitly specified, NDK version will be set to match exactly the required one
FILAMENT_NDK_VERSION=${GITHUB_NDK_VERSION:-27.0.11718014}
(! grep "${FILAMENT_NDK_VERSION}" `dirname $0`/../../android/build.gradle > /dev/null) &&
echo "Mismatch of NDK versions: want ${FILAMENT_NDK_VERSION} and not found in android/build.gradle" &&
exit 1
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/ndk.version)}
# Install the required NDK version specifically (if not present)
if [[ ! -d "${ANDROID_HOME}/ndk/$FILAMENT_NDK_VERSION" ]]; then

View File

@@ -0,0 +1 @@
27.0.11718014

View File

@@ -2,4 +2,5 @@
if [[ "$GITHUB_WORKFLOW" ]]; then
echo "Running workflow $GITHUB_WORKFLOW (event: $GITHUB_EVENT_NAME, action: $GITHUB_ACTION)"
CONTINUOUS_INTEGRATION=true
fi

View File

@@ -1,18 +0,0 @@
#!/bin/bash
if [[ "$GITHUB_WORKFLOW" ]]; then
OS_NAME=$(uname -s)
NINJA_DL_DIR=/tmp/ninja-dl
NINJA_PLATFORM=
if [[ "$OS_NAME" == "Linux" ]]; then
NINJA_PLATFORM=linux
elif [[ "$OS_NAME" == "Darwin" ]]; then
NINJA_PLATFORM=mac
fi
curl -L -o /tmp/ninja-dl.zip https://github.com/ninja-build/ninja/releases/download/v${GITHUB_NINJA_VERSION}/ninja-${NINJA_PLATFORM}.zip
mkdir -p $NINJA_DL_DIR
unzip -q /tmp/ninja-dl.zip -d $NINJA_DL_DIR
chmod +x ${NINJA_DL_DIR}/ninja
# Install ninja globally for AGP
sudo cp ${NINJA_DL_DIR}/ninja /usr/local/bin/
fi

View File

@@ -1,6 +0,0 @@
GITHUB_CLANG_VERSION=14
GITHUB_CMAKE_VERSION=3.19.5
GITHUB_NINJA_VERSION=1.10.2
GITHUB_MESA_VERSION=24.2.1
GITHUB_LLVM_VERSION=16
GITHUB_NDK_VERSION=27.0.11718014

View File

@@ -28,6 +28,7 @@ set -e
set -x
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/ci-common.sh
source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null
@@ -40,3 +41,4 @@ if [[ "${GENERATE_ARCHIVES}" ]]; then
fi
./build.sh -i -p ios -c $BUILD_SIMULATOR $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE

6
build/ios/ci-common.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
curl -OL https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip
unzip -q ninja-mac.zip
chmod +x ninja
export PATH="$PWD:$PATH"

View File

@@ -32,6 +32,7 @@ set -x
# $BUILD_DEBUG
# $BUILD_RELEASE
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/ci-common.sh
source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null

38
build/linux/ci-common.sh Executable file
View File

@@ -0,0 +1,38 @@
#!/bin/bash
# version of clang we want to use
export GITHUB_CLANG_VERSION=14
# version of CMake to use instead of the default one
export GITHUB_CMAKE_VERSION=3.19.5
# version of ninja to use
export GITHUB_NINJA_VERSION=1.10.2
# Steps for GitHub Workflows
if [[ "$GITHUB_WORKFLOW" ]]; then
# Install ninja
wget -q https://github.com/ninja-build/ninja/releases/download/v$GITHUB_NINJA_VERSION/ninja-linux.zip
unzip -q ninja-linux.zip
export PATH="$PWD:$PATH"
# 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
# For dawn
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libx11-xcb-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100
fi

View File

@@ -28,6 +28,7 @@ set -e
set -x
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/ci-common.sh
source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null

8
build/mac/ci-common.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
curl -OL https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip
unzip -q ninja-mac.zip
chmod +x ninja
# Install ninja globally for AGP
cp ninja /usr/local/bin
export PATH="$PWD:$PATH"

View File

@@ -31,16 +31,10 @@ set(DIST_ARCH arm64-v8a)
string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} HOST_NAME_L)
file(TO_CMAKE_PATH $ENV{ANDROID_HOME} ANDROID_HOME_UNIX)
message(STATUS "Try using NDK \'${FILAMENT_NDK_VERSION}\'")
if (NOT FILAMENT_NDK_VERSION)
file(READ "${CMAKE_CURRENT_LIST_DIR}/common/versions" VERSIONS_STR)
string(REGEX MATCH "GITHUB_NDK_VERSION=(\\d+)" _UNUSED ${VERSIONS_STR})
if(CMAKE_MATCH_1)
set(FILAMENT_NDK_VERSION "${CMAKE_MATCH_1}")
endif()
file(READ "${CMAKE_CURRENT_LIST_DIR}/android/ndk.version" FILAMENT_NDK_VERSION)
string(REGEX MATCH "^\\d+" FILAMENT_NDK_VERSION ${FILAMENT_NDK_VERSION})
endif()
message(STATUS "Using NDK \'${FILAMENT_NDK_VERSION}\'")
file(GLOB NDK_VERSIONS LIST_DIRECTORIES true ${ANDROID_HOME_UNIX}/ndk/${FILAMENT_NDK_VERSION}*)
list(SORT NDK_VERSIONS)
list(GET NDK_VERSIONS -1 NDK_VERSION)

View File

@@ -32,16 +32,10 @@ set(DIST_ARCH armeabi-v7a)
string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} HOST_NAME_L)
file(TO_CMAKE_PATH $ENV{ANDROID_HOME} ANDROID_HOME_UNIX)
message(STATUS "Try using NDK \'${FILAMENT_NDK_VERSION}\'")
if (NOT FILAMENT_NDK_VERSION)
file(READ "${CMAKE_CURRENT_LIST_DIR}/common/versions" VERSIONS_STR)
string(REGEX MATCH "GITHUB_NDK_VERSION=(\\d+)" _UNUSED ${VERSIONS_STR})
if(CMAKE_MATCH_1)
set(FILAMENT_NDK_VERSION "${CMAKE_MATCH_1}")
endif()
file(READ "${CMAKE_CURRENT_LIST_DIR}/android/ndk.version" FILAMENT_NDK_VERSION)
string(REGEX MATCH "^\\d+" FILAMENT_NDK_VERSION ${FILAMENT_NDK_VERSION})
endif()
message(STATUS "Using NDK \'${FILAMENT_NDK_VERSION}\'")
file(GLOB NDK_VERSIONS LIST_DIRECTORIES true ${ANDROID_HOME_UNIX}/ndk/${FILAMENT_NDK_VERSION}*)
list(SORT NDK_VERSIONS)
list(GET NDK_VERSIONS -1 NDK_VERSION)

View File

@@ -31,16 +31,10 @@ set(DIST_ARCH x86)
string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} HOST_NAME_L)
file(TO_CMAKE_PATH $ENV{ANDROID_HOME} ANDROID_HOME_UNIX)
message(STATUS "Try using NDK \'${FILAMENT_NDK_VERSION}\'")
if (NOT FILAMENT_NDK_VERSION)
file(READ "${CMAKE_CURRENT_LIST_DIR}/common/versions" VERSIONS_STR)
string(REGEX MATCH "GITHUB_NDK_VERSION=(\\d+)" _UNUSED ${VERSIONS_STR})
if(CMAKE_MATCH_1)
set(FILAMENT_NDK_VERSION "${CMAKE_MATCH_1}")
endif()
file(READ "${CMAKE_CURRENT_LIST_DIR}/android/ndk.version" FILAMENT_NDK_VERSION)
string(REGEX MATCH "^\\d+" FILAMENT_NDK_VERSION ${FILAMENT_NDK_VERSION})
endif()
message(STATUS "Using NDK \'${FILAMENT_NDK_VERSION}\'")
file(GLOB NDK_VERSIONS LIST_DIRECTORIES true ${ANDROID_HOME_UNIX}/ndk/${FILAMENT_NDK_VERSION}*)
list(SORT NDK_VERSIONS)
list(GET NDK_VERSIONS -1 NDK_VERSION)

View File

@@ -31,16 +31,10 @@ set(DIST_ARCH x86_64)
string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} HOST_NAME_L)
file(TO_CMAKE_PATH $ENV{ANDROID_HOME} ANDROID_HOME_UNIX)
message(STATUS "Try using NDK \'${FILAMENT_NDK_VERSION}\'")
if (NOT FILAMENT_NDK_VERSION)
file(READ "${CMAKE_CURRENT_LIST_DIR}/common/versions" VERSIONS_STR)
string(REGEX MATCH "GITHUB_NDK_VERSION=(\\d+)" _UNUSED ${VERSIONS_STR})
if(CMAKE_MATCH_1)
set(FILAMENT_NDK_VERSION "${CMAKE_MATCH_1}")
endif()
file(READ "${CMAKE_CURRENT_LIST_DIR}/android/ndk.version" FILAMENT_NDK_VERSION)
string(REGEX MATCH "^\\d+" FILAMENT_NDK_VERSION ${FILAMENT_NDK_VERSION})
endif()
message(STATUS "Using NDK \'${FILAMENT_NDK_VERSION}\'")
file(GLOB NDK_VERSIONS LIST_DIRECTORIES true ${ANDROID_HOME_UNIX}/ndk/${FILAMENT_NDK_VERSION}*)
list(SORT NDK_VERSIONS)
list(GET NDK_VERSIONS -1 NDK_VERSION)

View File

@@ -1,4 +1,16 @@
#!/bin/bash
if [ `uname` == "Linux" ];then
source `dirname $0`/../linux/ci-common.sh
elif [ `uname` == "Darwin" ];then
curl -OL https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip
unzip -q ninja-mac.zip
else
echo "Unsupported OS"
exit 1
fi
chmod +x ninja
export PATH="$PWD:$PATH"
# Install emscripten.
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.60.zip > emsdk.zip

View File

@@ -259,8 +259,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPUDriver.h
src/webgpu/WebGPUHandles.cpp
src/webgpu/WebGPUHandles.h
src/webgpu/WebGPUPipelineCreation.cpp
src/webgpu/WebGPUPipelineCreation.h
src/webgpu/WebGPUSwapChain.cpp
src/webgpu/WebGPUSwapChain.h
src/webgpu/WGPUProgram.cpp

View File

@@ -66,62 +66,53 @@ namespace {
};
wgpu::ShaderModule module = device.CreateShaderModule(&descriptor);
FILAMENT_CHECK_POSTCONDITION(module != nullptr) << "Failed to create " << descriptor.label;
wgpu::Instance instance = device.GetAdapter().GetInstance();
instance.WaitAny(
module.GetCompilationInfo(wgpu::CallbackMode::WaitAnyOnly,
[&descriptor](auto const& status,
wgpu::CompilationInfo const* info) {
switch (status) {
case wgpu::CompilationInfoRequestStatus::CallbackCancelled:
FWGPU_LOGW << "Shader compilation info callback cancelled for "
<< descriptor.label << "?" << utils::io::endl;
return;
case wgpu::CompilationInfoRequestStatus::Success:
module.GetCompilationInfo(wgpu::CallbackMode::AllowSpontaneous,
[&descriptor](auto const& status, wgpu::CompilationInfo const* info) {
switch (status) {
case wgpu::CompilationInfoRequestStatus::CallbackCancelled:
FWGPU_LOGW << "Shader compilation info callback cancelled for "
<< descriptor.label << "?" << utils::io::endl;
return;
case wgpu::CompilationInfoRequestStatus::Success:
break;
}
if (info != nullptr) {
std::stringstream errorStream;
int errorCount = 0;
for (size_t msgIndex = 0; msgIndex < info->messageCount; msgIndex++) {
wgpu::CompilationMessage const& message = info->messages[msgIndex];
switch (message.type) {
case wgpu::CompilationMessageType::Info:
FWGPU_LOGI << descriptor.label << ": " << message.message
<< " line#:" << message.lineNum
<< " linePos:" << message.linePos
<< " offset:" << message.offset
<< " length:" << message.length << utils::io::endl;
break;
case wgpu::CompilationMessageType::Warning:
FWGPU_LOGW << "Warning compiling " << descriptor.label << ": "
<< message.message << " line#:" << message.lineNum
<< " linePos:" << message.linePos
<< " offset:" << message.offset
<< " length:" << message.length << utils::io::endl;
break;
case wgpu::CompilationMessageType::Error:
errorCount++;
errorStream << "Error " << errorCount << " : "
<< std::string_view(message.message)
<< " line#:" << message.lineNum
<< " linePos:" << message.linePos
<< " offset:" << message.offset
<< " length:" << message.length << "\n";
break;
}
if (info != nullptr) {
std::stringstream errorStream;
int errorCount = 0;
for (size_t msgIndex = 0; msgIndex < info->messageCount; msgIndex++) {
wgpu::CompilationMessage const& message = info->messages[msgIndex];
switch (message.type) {
case wgpu::CompilationMessageType::Info:
FWGPU_LOGI << descriptor.label << ": " << message.message
<< " line#:" << message.lineNum
<< " linePos:" << message.linePos
<< " offset:" << message.offset
<< " length:" << message.length
<< utils::io::endl;
break;
case wgpu::CompilationMessageType::Warning:
FWGPU_LOGW
<< "Warning compiling " << descriptor.label << ": "
<< message.message << " line#:" << message.lineNum
<< " linePos:" << message.linePos
<< " offset:" << message.offset
<< " length:" << message.length << utils::io::endl;
break;
case wgpu::CompilationMessageType::Error:
errorCount++;
errorStream << "Error " << errorCount << " : "
<< std::string_view(message.message)
<< " line#:" << message.lineNum
<< " linePos:" << message.linePos
<< " offset:" << message.offset
<< " length:" << message.length << "\n";
break;
}
}
FILAMENT_CHECK_POSTCONDITION(errorCount < 1)
<< errorCount << " error(s) compiling " << descriptor.label
<< ":\n"
<< errorStream.str();
}
FWGPU_LOGD << descriptor.label << " compiled successfully"
<< utils::io::endl;
}),
UINT16_MAX);
}
FILAMENT_CHECK_POSTCONDITION(errorCount < 1)
<< errorCount << " error(s) compiling " << descriptor.label << ":\n"
<< errorStream.str();
}
FWGPU_LOGD << descriptor.label << " compiled successfully" << utils::io::endl;
});
return module;
}

View File

@@ -16,7 +16,6 @@
#include "webgpu/WebGPUDriver.h"
#include "WebGPUPipelineCreation.h"
#include "WebGPUSwapChain.h"
#include <backend/platforms/WebGPUPlatform.h>
@@ -25,20 +24,18 @@
#include "private/backend/Dispatcher.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/TargetBufferInfo.h>
#include <math/mat3.h>
#include <utils/CString.h>
#include <utils/Panic.h>
#include <utils/ostream.h>
#include <dawn/webgpu_cpp_print.h>
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <sstream>
#include <string_view>
#include <utility>
@@ -503,7 +500,7 @@ void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> sr
uint8_t baseLevel, uint8_t levelCount) {
auto source = handleCast<WGPUTexture>(srch);
constructHandle<WGPUTexture>(th, source, baseLevel, levelCount);
constructHandle<WGPUTexture>(th, source, baseLevel, levelCount, mDevice);
}
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
@@ -876,44 +873,6 @@ void WebGPUDriver::blit(
}
void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
const auto* program = handleCast<WGPUProgram>(pipelineState.program);
assert_invariant(program);
assert_invariant(program->computeShaderModule == nullptr &&
"WebGPU backend does not (yet) support compute pipelines.");
FILAMENT_CHECK_POSTCONDITION(program->vertexShaderModule)
<< "WebGPU backend requires a vertex shader module for a render pipeline";
std::array<wgpu::BindGroupLayout, MAX_DESCRIPTOR_SET_COUNT> bindGroupLayouts{};
assert_invariant(bindGroupLayouts.size() >= pipelineState.pipelineLayout.setLayout.size());
size_t bindGroupLayoutCount = 0;
for (size_t i = 0; i < bindGroupLayouts.size(); i++) {
const auto handle = pipelineState.pipelineLayout.setLayout[bindGroupLayoutCount];
if (handle.getId() == HandleBase::nullid) {
continue;
}
bindGroupLayouts[bindGroupLayoutCount++] =
handleCast<WebGPUDescriptorSetLayout>(handle)->getLayout();
}
std::stringstream layoutLabelStream;
layoutLabelStream << program->name.c_str() << " layout";
const auto layoutLabel = layoutLabelStream.str();
const wgpu::PipelineLayoutDescriptor layoutDescriptor{
.label = wgpu::StringView(layoutLabel),
.bindGroupLayoutCount = bindGroupLayoutCount,
.bindGroupLayouts = bindGroupLayouts.data()
// TODO investigate immediateDataRangeByteSize
};
const wgpu::PipelineLayout layout = mDevice.CreatePipelineLayout(&layoutDescriptor);
FILAMENT_CHECK_POSTCONDITION(layout)
<< "Failed to create wgpu::PipelineLayout for render pipeline for "
<< layoutDescriptor.label;
auto const* vertexBufferInfo = handleCast<WGPUVertexBufferInfo>(pipelineState.vertexBufferInfo);
assert_invariant(vertexBufferInfo);
const wgpu::RenderPipeline pipeline = createWebGPURenderPipeline(mDevice, *program,
*vertexBufferInfo, layout, pipelineState.rasterState, pipelineState.stencilState,
pipelineState.polygonOffset, pipelineState.primitiveType, mSwapChain->getColorFormat(),
mSwapChain->getDepthFormat());
// TODO: uncomment once we have a valid pipeline to set
// mRenderPassEncoder.SetPipeline(pipeline);
}
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {

View File

@@ -334,10 +334,11 @@ WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat forma
texture = device.CreateTexture(&desc);
// TODO should a default levelCount be something other than 0? Sample count?
texView = makeTextureView(0, 1);
texView = makeTextureView(0, 0);
}
// From createTextureViewR
WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept {
WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount,
wgpu::Device device) noexcept {
texture = src->texture;
texView = makeTextureView(baseLevel, levelCount);
}

View File

@@ -138,7 +138,10 @@ public:
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
wgpu::Device device) noexcept;
WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount,
wgpu::Device device) noexcept;
// TODO: createTextureExternalImage1/2 createTextureExternalImagePlaneR importTextureR cases
const wgpu::Texture& getTexture() const { return texture; }
const wgpu::Sampler& getSampler() const { return sampler; }

View File

@@ -1,254 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "WebGPUPipelineCreation.h"
#include "WebGPUHandles.h"
#include <backend/DriverEnums.h>
#include <backend/TargetBufferInfo.h>
#include <utils/Panic.h>
#include <utils/debug.h>
#include <webgpu/webgpu_cpp.h>
#include <array>
#include <cstdint>
#include <sstream>
namespace filament::backend {
namespace {
constexpr wgpu::PrimitiveTopology toWebGPU(PrimitiveType primitiveType) {
switch (primitiveType) {
case PrimitiveType::POINTS:
return wgpu::PrimitiveTopology::PointList;
case PrimitiveType::LINES:
return wgpu::PrimitiveTopology::LineList;
case PrimitiveType::LINE_STRIP:
return wgpu::PrimitiveTopology::LineStrip;
case PrimitiveType::TRIANGLES:
return wgpu::PrimitiveTopology::TriangleList;
case PrimitiveType::TRIANGLE_STRIP:
return wgpu::PrimitiveTopology::TriangleStrip;
}
}
constexpr wgpu::CullMode toWebGPU(CullingMode cullMode) {
switch (cullMode) {
case CullingMode::NONE:
return wgpu::CullMode::None;
case CullingMode::FRONT:
return wgpu::CullMode::Front;
case CullingMode::BACK:
return wgpu::CullMode::Back;
case CullingMode::FRONT_AND_BACK:
// no WegGPU equivalent of front and back
FILAMENT_CHECK_POSTCONDITION(false)
<< "WebGPU does not support CullingMode::FRONT_AND_BACK";
return wgpu::CullMode::Undefined;
}
}
constexpr wgpu::CompareFunction toWebGPU(SamplerCompareFunc compareFunction) {
switch (compareFunction) {
case SamplerCompareFunc::LE:
return wgpu::CompareFunction::LessEqual;
case SamplerCompareFunc::GE:
return wgpu::CompareFunction::GreaterEqual;
case SamplerCompareFunc::L:
return wgpu::CompareFunction::Less;
case SamplerCompareFunc::G:
return wgpu::CompareFunction::Greater;
case SamplerCompareFunc::E:
return wgpu::CompareFunction::Equal;
case SamplerCompareFunc::NE:
return wgpu::CompareFunction::NotEqual;
case SamplerCompareFunc::A:
return wgpu::CompareFunction::Always;
case SamplerCompareFunc::N:
return wgpu::CompareFunction::Never;
}
}
constexpr wgpu::StencilOperation toWebGPU(StencilOperation stencilOp) {
switch (stencilOp) {
case StencilOperation::KEEP:
return wgpu::StencilOperation::Keep;
case StencilOperation::ZERO:
return wgpu::StencilOperation::Zero;
case StencilOperation::REPLACE:
return wgpu::StencilOperation::Replace;
case StencilOperation::INCR:
return wgpu::StencilOperation::IncrementClamp;
case StencilOperation::INCR_WRAP:
return wgpu::StencilOperation::IncrementWrap;
case StencilOperation::DECR:
return wgpu::StencilOperation::DecrementClamp;
case StencilOperation::DECR_WRAP:
return wgpu::StencilOperation::DecrementWrap;
case StencilOperation::INVERT:
return wgpu::StencilOperation::Invert;
}
}
constexpr wgpu::BlendOperation toWebGPU(BlendEquation blendOp) {
switch (blendOp) {
case BlendEquation::ADD:
return wgpu::BlendOperation::Add;
case BlendEquation::SUBTRACT:
return wgpu::BlendOperation::Subtract;
case BlendEquation::REVERSE_SUBTRACT:
return wgpu::BlendOperation::ReverseSubtract;
case BlendEquation::MIN:
return wgpu::BlendOperation::Min;
case BlendEquation::MAX:
return wgpu::BlendOperation::Max;
}
}
constexpr wgpu::BlendFactor toWebGPU(BlendFunction blendFunction) {
switch (blendFunction) {
case BlendFunction::ZERO:
return wgpu::BlendFactor::Zero;
case BlendFunction::ONE:
return wgpu::BlendFactor::One;
case BlendFunction::SRC_COLOR:
return wgpu::BlendFactor::Src;
case BlendFunction::ONE_MINUS_SRC_COLOR:
return wgpu::BlendFactor::OneMinusSrc;
case BlendFunction::DST_COLOR:
return wgpu::BlendFactor::Dst;
case BlendFunction::ONE_MINUS_DST_COLOR:
return wgpu::BlendFactor::OneMinusDst;
case BlendFunction::SRC_ALPHA:
return wgpu::BlendFactor::SrcAlpha;
case BlendFunction::ONE_MINUS_SRC_ALPHA:
return wgpu::BlendFactor::OneMinusSrcAlpha;
case BlendFunction::DST_ALPHA:
return wgpu::BlendFactor::DstAlpha;
case BlendFunction::ONE_MINUS_DST_ALPHA:
return wgpu::BlendFactor::OneMinusDstAlpha;
case BlendFunction::SRC_ALPHA_SATURATE:
return wgpu::BlendFactor::SrcAlphaSaturated;
}
}
}// namespace
wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
WGPUProgram const& program, WGPUVertexBufferInfo const& vertexBufferInfo,
wgpu::PipelineLayout const& layout, RasterState const& rasterState,
StencilState const& stencilState, PolygonOffset const& polygonOffset,
PrimitiveType primitiveType, wgpu::TextureFormat colorFormat,
wgpu::TextureFormat depthFormat) {
assert_invariant(program.vertexShaderModule);
const wgpu::DepthStencilState depthStencilState {
.format = depthFormat,
.depthWriteEnabled = rasterState.depthWrite,
.depthCompare = toWebGPU(rasterState.depthFunc),
.stencilFront = {
.compare = toWebGPU(stencilState.front.stencilFunc),
.failOp = toWebGPU(stencilState.front.stencilOpStencilFail),
.depthFailOp = toWebGPU(stencilState.front.stencilOpDepthFail),
.passOp = toWebGPU(stencilState.front.stencilOpDepthStencilPass),
},
.stencilBack = {
.compare = toWebGPU(stencilState.back.stencilFunc),
.failOp = toWebGPU(stencilState.back.stencilOpStencilFail),
.depthFailOp = toWebGPU(stencilState.back.stencilOpDepthFail),
.passOp = toWebGPU(stencilState.back.stencilOpDepthStencilPass),
},
.stencilReadMask = 0,
.stencilWriteMask = stencilState.stencilWrite ? 0xFFFFFFFF : 0,
.depthBias = static_cast<int32_t>(polygonOffset.constant),
.depthBiasSlopeScale = polygonOffset.slope,
.depthBiasClamp = 0.0f
};
std::stringstream pipelineLabelStream;
pipelineLabelStream << program.name.c_str() << " pipeline";
const auto pipelineLabel = pipelineLabelStream.str();
wgpu::RenderPipelineDescriptor pipelineDescriptor{
.label = wgpu::StringView(pipelineLabel),
.layout = layout,
.vertex = {
.module = program.vertexShaderModule,
.entryPoint = "main",
.constantCount = program.constants.size(),
.constants = program.constants.data(),
.bufferCount = vertexBufferInfo.getVertexBufferLayoutSize(),
.buffers = vertexBufferInfo.getVertexBufferLayout()
},
.primitive = {
.topology = toWebGPU(primitiveType),
// TODO should we assume some constant format here or is there a way to get
// this from PipelineState somehow or elsewhere?
// Perhaps, cache/assert format from index buffers as they are requested?
.stripIndexFormat = wgpu::IndexFormat::Undefined,
.frontFace = rasterState.inverseFrontFaces ? wgpu::FrontFace::CW : wgpu::FrontFace::CCW,
.cullMode = toWebGPU(rasterState.culling),
// TODO no depth clamp in WebGPU supported directly. unclippedDepth is close, so we are
// starting there
.unclippedDepth = !rasterState.depthClamp &&
device.HasFeature(wgpu::FeatureName::DepthClipControl)
},
.depthStencil = &depthStencilState,
.multisample = {
.count = 1, // TODO need to get this from the render target
.mask = 0xFFFFFFFF,
.alphaToCoverageEnabled = rasterState.alphaToCoverage
},
.fragment = nullptr // will add below if fragment module is included
};
wgpu::FragmentState fragmentState = {};
std::array<wgpu::ColorTargetState, MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT> colorTargets {};
const wgpu::BlendState blendState {
.color = {
.operation = toWebGPU(rasterState.blendEquationRGB),
.srcFactor = toWebGPU(rasterState.blendFunctionSrcRGB),
.dstFactor = toWebGPU(rasterState.blendFunctionDstRGB)
},
.alpha = {
.operation = toWebGPU(rasterState.blendEquationAlpha),
.srcFactor = toWebGPU(rasterState.blendFunctionSrcAlpha),
.dstFactor = toWebGPU(rasterState.blendFunctionDstAlpha)
}
};
if (program.fragmentShaderModule != nullptr) {
fragmentState.module = program.fragmentShaderModule;
fragmentState.entryPoint = "main";
fragmentState.constantCount = program.constants.size(),
fragmentState.constants = program.constants.data(),
fragmentState.targetCount = 1; // TODO need to get this from the render target
assert_invariant(fragmentState.targetCount <= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
for (size_t targetIndex = 0; targetIndex < fragmentState.targetCount; targetIndex++) {
auto& colorTarget = colorTargets[targetIndex];
colorTarget.format = colorFormat;
colorTarget.blend = rasterState.hasBlending() ? &blendState : nullptr;
colorTarget.writeMask =
rasterState.colorWrite ? wgpu::ColorWriteMask::All : wgpu::ColorWriteMask::None;
}
pipelineDescriptor.fragment = &fragmentState;
}
const wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
FILAMENT_CHECK_POSTCONDITION(pipeline)
<< "Failed to create render pipeline for " << pipelineDescriptor.label;
return pipeline;
}
}// namespace filament::backend

View File

@@ -1,46 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_WEBGPUPIPELINECREATION_H
#define TNT_FILAMENT_BACKEND_WEBGPUPIPELINECREATION_H
#include <cstdint>
namespace wgpu {
class Device;
class PipelineLayout;
class RenderPipeline;
enum class TextureFormat : uint32_t;
}// namespace wgpu
namespace filament::backend {
struct PolygonOffset;
enum class PrimitiveType : uint8_t;
struct RasterState;
struct StencilState;
class WGPUVertexBufferInfo;
class WGPUProgram;
[[nodiscard]] wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const&,
WGPUProgram const&, WGPUVertexBufferInfo const&, wgpu::PipelineLayout const&,
RasterState const&, StencilState const&, PolygonOffset const&, PrimitiveType,
wgpu::TextureFormat colorFormat, wgpu::TextureFormat depthFormat);
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_WEBGPUPIPELINECREATION_H

View File

@@ -73,8 +73,7 @@ void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabiliti
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
wgpu::TextureFormat depthFormat) {
void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config) {
std::stringstream formatStream{};
formatStream << config.format;
std::stringstream usageStream{};
@@ -83,8 +82,6 @@ void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
alphaModeStream << config.alphaMode;
std::stringstream presentModeStream{};
presentModeStream << config.presentMode;
std::stringstream depthFormatStream;
depthFormatStream << depthFormat;
FWGPU_LOGI << "WebGPU surface configuration:" << utils::io::endl;
FWGPU_LOGI << " surface format: " << formatStream.str() << utils::io::endl;
FWGPU_LOGI << " surface usage: " << usageStream.str() << utils::io::endl;
@@ -101,11 +98,10 @@ void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
FWGPU_LOGI << " surface width: " << config.width << utils::io::endl;
FWGPU_LOGI << " surface height: " << config.height << utils::io::endl;
FWGPU_LOGI << " surface present mode: " << presentModeStream.str() << utils::io::endl;
FWGPU_LOGI << "WebGPU selected depth format: " << depthFormatStream.str() << utils::io::endl;
}
#endif
constexpr wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
wgpu::TextureFormat const* availableFormats, bool useSRGBColorSpace) {
const std::array expectedColorFormats =
useSRGBColorSpace ?
@@ -123,21 +119,7 @@ constexpr wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
return *firstFoundColorFormat;
}
constexpr wgpu::TextureFormat selectDepthFormat(bool depth32FloatStencil8Enabled,
bool needStencil) {
if (needStencil) {
if (depth32FloatStencil8Enabled) {
return wgpu::TextureFormat::Depth32FloatStencil8;
} else {
return wgpu::TextureFormat::Depth24PlusStencil8;
}
} else {
// other options: Depth16Unorm or Depth24Plus
return wgpu::TextureFormat::Depth32Float;
}
}
constexpr wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
wgpu::PresentMode const* availablePresentModes) {
// Verify that our chosen present mode is supported. In practice all devices support the FIFO
// mode, but we check for it anyway for completeness. (and to avoid validation warnings)
@@ -151,7 +133,7 @@ constexpr wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
return desiredPresentMode;
}
constexpr wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
wgpu::CompositeAlphaMode const* availableAlphaModes) {
bool autoAvailable = false;
bool inheritAvailable = false;
@@ -227,7 +209,7 @@ void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
namespace filament::backend {
WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
wgpu::Adapter const& adapter, wgpu::Device const& device, uint64_t flags)
wgpu::Adapter& adapter, wgpu::Device& device, uint64_t flags)
: mSurface(surface) {
wgpu::SurfaceCapabilities capabilities = {};
if (!mSurface.GetCapabilities(adapter, &capabilities)) {
@@ -238,13 +220,7 @@ WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const&
#endif
}
const bool useSRGBColorSpace = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0;
const bool needStencil = (flags & SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0;
initConfig(mConfig, device, capabilities, surfaceSize, useSRGBColorSpace);
mDepthFormat = selectDepthFormat(device.HasFeature(wgpu::FeatureName::Depth32FloatStencil8),
needStencil);
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printSurfaceConfiguration(mConfig, mDepthFormat);
#endif
mSurface.Configure(&mConfig);
}
@@ -259,12 +235,12 @@ void WebGPUSwapChain::setExtent(wgpu::Extent2D const& currentSurfaceSize) {
if (mConfig.width != currentSurfaceSize.width || mConfig.height != currentSurfaceSize.height) {
mConfig.width = currentSurfaceSize.width;
mConfig.height = currentSurfaceSize.height;
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printSurfaceConfiguration(mConfig);
#endif
FWGPU_LOGD << "Resizing to width " << mConfig.width << " height " << mConfig.height
<< utils::io::endl;
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printSurfaceConfiguration(mConfig, mDepthFormat);
#endif
// TODO we may need to ensure no surface texture is in flight when we do this. some
// TODO we may need to ensure no surface texture is flight when we do this. some
// synchronization may be necessary
mSurface.Configure(&mConfig);
}
@@ -281,7 +257,7 @@ wgpu::TextureView WebGPUSwapChain::getCurrentSurfaceTextureView(
// Create a view for this surface texture
// TODO: review these initiliazations as webgpu pipeline gets mature
wgpu::TextureViewDescriptor textureViewDescriptor = {
.label = "surface_texture_view",
.label = "texture_view",
.format = surfaceTexture.texture.GetFormat(),
.dimension = wgpu::TextureViewDimension::e2D,
.baseMipLevel = 0,

View File

@@ -29,14 +29,10 @@ namespace filament::backend {
class WebGPUSwapChain final : public Platform::SwapChain, HwSwapChain {
public:
WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
wgpu::Adapter const& adapter, wgpu::Device const& device, uint64_t flags);
wgpu::Adapter& adapter, wgpu::Device& device, uint64_t flags);
~WebGPUSwapChain();
[[nodiscard]] wgpu::TextureFormat getColorFormat() const { return mConfig.format; }
[[nodiscard]] wgpu::TextureFormat getDepthFormat() const { return mDepthFormat; }
[[nodiscard]] wgpu::TextureView getCurrentSurfaceTextureView(wgpu::Extent2D const&);
wgpu::TextureView getCurrentSurfaceTextureView(wgpu::Extent2D const&);
void present();
@@ -45,7 +41,6 @@ private:
wgpu::Surface mSurface = {};
wgpu::SurfaceConfiguration mConfig = {};
wgpu::TextureFormat mDepthFormat = wgpu::TextureFormat::Undefined;
};
} // namespace filament::backend

View File

@@ -98,23 +98,10 @@ wgpu::Adapter WebGPUPlatform::requestAdapter(wgpu::Surface const& surface) {
}
wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
// TODO consider passing limits
constexpr std::array desiredFeatures = {
wgpu::FeatureName::DepthClipControl,
wgpu::FeatureName::Depth32FloatStencil8,
wgpu::FeatureName::CoreFeaturesAndLimits };
std::vector<wgpu::FeatureName> requiredFeatures;
requiredFeatures.reserve(desiredFeatures.size());
wgpu::SupportedFeatures supportedFeatures;
adapter.GetFeatures(&supportedFeatures);
std::set_intersection(supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount, desiredFeatures.begin(),
desiredFeatures.end(), std::back_inserter(requiredFeatures));
// TODO consider passing required features and/or limits
wgpu::DeviceDescriptor deviceDescriptor{};
deviceDescriptor.label = "graphics_device";
deviceDescriptor.defaultQueue.label = "default_queue";
deviceDescriptor.requiredFeatureCount = requiredFeatures.size();
deviceDescriptor.requiredFeatures = requiredFeatures.data();
deviceDescriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
[](wgpu::Device const&, wgpu::DeviceLostReason const& reason,
wgpu::StringView message) {

View File

@@ -33,10 +33,9 @@
#endif
ScreenshotParams::ScreenshotParams(int width, int height, std::string fileName,
uint32_t expectedHash, bool isSrgb)
uint32_t expectedHash)
: mWidth(width),
mHeight(height),
mIsSrgb(isSrgb),
mExpectedPixelHash(expectedHash),
mFileName(std::move(fileName)) {}
@@ -48,10 +47,6 @@ int ScreenshotParams::height() const {
return mHeight;
}
bool ScreenshotParams::isSrgb() const {
return mIsSrgb;
}
uint32_t ScreenshotParams::expectedHash() const {
return mExpectedPixelHash;
}
@@ -152,25 +147,13 @@ RenderTargetDump::RenderTargetDump(filament::backend::DriverApi& api,
auto* internal = static_cast<RenderTargetDump::Internal*>(user);
internal->bytesFilled = true;
#ifndef FILAMENT_IOS
image::LinearImage image;
if (internal->params.isSrgb()) {
image = image::toLinearWithAlpha<uint8_t>(internal->params.width(),
internal->params.height(),
internal->params.width() * 4, (uint8_t*)buffer);
} else {
// The image data is already linear, so pass in transforms that simply go from uint8_t
// to float. toLinearWithAlpha divides the float values by uint8_t max so there's no
// need to scale it to [0, 1]
image = image::toLinearWithAlpha<uint8_t>(
internal->params.width(), internal->params.height(),
internal->params.width() * 4, (uint8_t*) buffer,
[](uint8_t value) -> float { return value; },
[](filament::math::float4 rgba) -> filament::math::float4 { return rgba; });
}
image::LinearImage image(internal->params.width(), internal->params.width(), 4);
image = image::toLinearWithAlpha<uint8_t>(internal->params.width(),
internal->params.height(),
internal->params.width() * 4, (uint8_t*)buffer);
std::string filePath = internal->params.actualFilePath();
std::ofstream pngStream(filePath, std::ios::binary | std::ios::trunc);
// To avoid going from linear -> sRGB -> linear save the PNG as linear.
image::ImageEncoder::encode(pngStream, image::ImageEncoder::Format::PNG_LINEAR, image, "",
image::ImageEncoder::encode(pngStream, image::ImageEncoder::Format::PNG, image, "",
filePath);
#endif
};
@@ -192,12 +175,8 @@ RenderTargetDump::~RenderTargetDump() {
}
}
uint32_t RenderTargetDump::Internal::hash() const {
return utils::hash::murmur3((uint32_t*)bytes.data(), bytes.size() / 4, 0);
}
uint32_t RenderTargetDump::hash() const {
return mInternal->hash();
return utils::hash::murmur3((uint32_t*)mInternal->bytes.data(), mInternal->bytes.size() / 4, 0);
}
bool RenderTargetDump::bytesFilled() const {
@@ -225,15 +204,9 @@ LoadedPng::LoadedPng(std::string filePath) : mFilePath(std::move(filePath)) {
uint32_t LoadedPng::hash() const {
EXPECT_THAT(mBytes, testing::Not(testing::IsEmpty()))
<< "Failed to load expected test result: " << mFilePath << ".\n"
<< "Did you forget to sync CMake after updating the expected image in the source "
"directory?";
<< "Failed to load expected test result: " << mFilePath;
if (mBytes.empty()) {
return 0;
}
return utils::hash::murmur3((uint32_t*)mBytes.data(), mBytes.size() / 4, 0);
}
const std::vector<unsigned char>& LoadedPng::bytes() const {
return mBytes;
}

View File

@@ -40,12 +40,10 @@ do { \
*/
class ScreenshotParams {
public:
ScreenshotParams(int width, int height, std::string fileName, uint32_t expectedPixelHash,
bool isSrgb = false);
ScreenshotParams(int width, int height, std::string fileName, uint32_t expectedPixelHash);
int width() const;
int height() const;
bool isSrgb() const;
uint32_t expectedHash() const;
static std::string actualDirectoryPath();
@@ -58,7 +56,6 @@ public:
private:
int mWidth;
int mHeight;
bool mIsSrgb;
uint32_t mExpectedPixelHash;
std::string mFileName;
};
@@ -95,8 +92,6 @@ private:
ScreenshotParams params;
std::atomic<bool> bytesFilled = false;
std::vector<unsigned char> bytes;
uint32_t hash() const;
};
// We need a memory location that won't be invalidated to pass to GPU callbacks as they can't
@@ -110,8 +105,6 @@ public:
uint32_t hash() const;
const std::vector<unsigned char>& bytes() const;
private:
std::string mFilePath;
std::vector<unsigned char> mBytes;

View File

@@ -55,10 +55,8 @@ Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup
prog.descriptorBindings(1, bindingsInfo);
mProgram = cleanup.add(api.createProgram(std::move(prog)));
if (!kLayouts.empty()) {
mDescriptorSetLayout =
cleanup.add(api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
}
mDescriptorSetLayout = cleanup.add(
api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
}
filament::backend::DescriptorSetHandle Shader::createDescriptorSet(DriverApi& api) const {

View File

@@ -96,7 +96,6 @@ public:
protected:
Cleanup& mCleanup;
filament::backend::ProgramHandle mProgram;
// This will be a null handle if there are no uniforms.
filament::backend::DescriptorSetLayoutHandle mDescriptorSetLayout;
};

View File

@@ -21,14 +21,12 @@
#include "BackendTest.h"
// skipEnvironment must be a test::SkipEnvironment
// rationale must be a string
#define SKIP_IF(skipEnvironment, rationale) \
do { \
SkipEnvironment skip(skipEnvironment); \
if (skip.matches()) { \
GTEST_SKIP() << "Skipping test as the " << skip.describe() << "\n" \
<< " This test can't run there because " << rationale; \
} \
#define SKIP_IF(skipEnvironment) \
do { \
SkipEnvironment skip(skipEnvironment); \
if (skip.matches()) { \
GTEST_SKIP() << "Skipping test as the " << skip.describe(); \
} \
} while (false)
namespace test {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 B

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -1,62 +0,0 @@
import os, shutil, argparse, typing
def match_sufffix(file_name: str, suffix: str, accepted_prefixes: typing.List[str]) -> str:
"""
Check if the file name is one of the searched for ones with the given suffix and if so return it.
:param accepted_prefixes: If None accepts any prefix
:return: file_name with the suffix removed or "" if it doesn't match. This does mean a string that
is just the suffix is considered to not match as it will return the empty string.
"""
if file_name.endswith(suffix):
prefix = file_name.removesuffix(suffix)
if accepted_prefixes is None or prefix in accepted_prefixes:
return prefix
return ""
def replace_file_names(path: str, removed: str, replacement: str = "", output_path: str = "",
prefixes: typing.List[str] = None):
if not output_path:
output_path = path
for file_name in os.listdir(path=path):
prefix = match_sufffix(file_name, removed, prefixes)
if prefix:
# Remove the prefix from the list so that prefixes is the list of intended but not yet found
# files.
if prefixes is not None:
prefixes.remove(prefix)
new_file_name = prefix + replacement
new_file_path = os.path.join(output_path, new_file_name)
old_file_path = os.path.join(path, file_name)
print(f'{old_file_path} to {new_file_path}')
shutil.move(old_file_path, new_file_path)
if prefixes is not None:
for unfound_prefix in prefixes:
print(f'Failed to find {unfound_prefix}_actual.png')
if __name__ == "__main__":
parser = argparse.ArgumentParser(prog='Backend Test File Renamer',
description='Moves actual generated test images to the expected '
'images directory, to update the test requirements. '
'test_cases accepts multiple arguments that should '
'be the name of the expected image file without the '
'.png suffix. Also --all can be passed to copy all '
'images.\n'
'Remember to sync CMake after running this to move '
'the new expected images to the binary directory.')
parser.add_argument('-i', '--input_path')
parser.add_argument('-o', '--output_path', default="./expected_images")
parser.add_argument('-t', '--test_cases', action='extend', nargs='*')
parser.add_argument('-a', '--all', action='store_true')
args = parser.parse_args()
input_path = "."
if args.input_path:
input_path = args.input_path
prefixes = args.test_cases
if args.all:
prefixes = None
replace_file_names(path=input_path, output_path=args.output_path, removed="_actual.png",
replacement=".png", prefixes=prefixes)

View File

@@ -25,7 +25,7 @@ using namespace filament::backend;
namespace test {
TEST_F(BackendTest, FrameScheduledCallback) {
SKIP_IF(Backend::OPENGL, "Frame callbacks are unsupported in OpenGL");
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -84,7 +84,7 @@ TEST_F(BackendTest, FrameScheduledCallback) {
}
TEST_F(BackendTest, FrameCompletedCallback) {
SKIP_IF(Backend::OPENGL, "Frame callbacks are unsupported in OpenGL");
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
auto& api = getDriverApi();
Cleanup cleanup(api);

View File

@@ -18,7 +18,6 @@
#include "Lifetimes.h"
#include "Shader.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
#include <backend/DriverEnums.h>
@@ -99,9 +98,6 @@ struct MaterialParams {
// The problems are caused by both uploading and rendering into the same texture, since the OpenGL
// backend's readPixels does not work correctly with textures that have image data uploaded.
TEST_F(BackendTest, FeedbackLoops) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL),
"OpenGL image is upside down due to readPixels failing for texture with uploaded image "
"data");
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -243,7 +239,7 @@ TEST_F(BackendTest, FeedbackLoops) {
// seems to be un-reliable on some GPU's.
if (frame == kNumFrames - 1) {
EXPECT_IMAGE(renderTargets[0], getExpectations(),
ScreenshotParams(kTexWidth, kTexHeight, "FeedbackLoops", 4192780705));
ScreenshotParams(kTexWidth, kTexHeight, "FeedbackLoops", 0x70695aa1));
}
api.flush();

View File

@@ -80,7 +80,7 @@ void main() {
})";
TEST_F(BackendTest, PushConstants) {
SKIP_IF(Backend::OPENGL, "Push constants not supported on OpenGL");
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
auto& api = getDriverApi();

View File

@@ -20,7 +20,6 @@
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
#include <backend/DriverEnums.h>
@@ -46,7 +45,6 @@ Shader createShader(DriverApi& api, Cleanup& cleanup, Backend backend) {
// Rendering an external image without setting any data should not crash.
TEST_F(BackendTest, RenderExternalImageWithoutSet) {
SKIP_IF(Backend::METAL, "External images aren't supported in metal");
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -113,7 +111,6 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
}
TEST_F(BackendTest, RenderExternalImage) {
SKIP_IF(Backend::METAL, "External images aren't supported in metal");
auto& api = getDriverApi();
Cleanup cleanup(api);

View File

@@ -20,7 +20,6 @@
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
using namespace filament;
@@ -162,7 +161,6 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
}
TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL), "Stencil isn't applied");
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -239,7 +237,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
api.endFrame(0);
EXPECT_IMAGE(renderTarget1, getExpectations(),
ScreenshotParams(512, 512, "StencilBufferAutoResolve", 3353562179));
ScreenshotParams(512, 512, "StencilBufferAutoResolve", 0x6CEFAC8F));
flushAndWait();
getDriver().purge();

View File

@@ -139,7 +139,7 @@ void DependencyGraph::clear() noexcept {
mNodes.clear();
}
void DependencyGraph::export_graphviz(utils::io::ostream& out, char const* name) const noexcept {
void DependencyGraph::export_graphviz(utils::io::ostream& out, char const* name) {
#ifndef NDEBUG
const char* graphName = name ? name : "graph";
out << "digraph \"" << graphName << "\" {\n";

View File

@@ -476,7 +476,7 @@ bool FrameGraph::isAcyclic() const noexcept {
return mGraph.isAcyclic();
}
void FrameGraph::export_graphviz(utils::io::ostream& out, char const* name) const noexcept {
void FrameGraph::export_graphviz(utils::io::ostream& out, char const* name) {
mGraph.export_graphviz(out, name);
}
@@ -573,11 +573,6 @@ fgviewer::FrameGraphInfo FrameGraph::getFrameGraphInfo(const char *viewName) con
info.setResources(std::move(resources));
info.setPasses(std::move(passes));
// Generate GraphViz DOT data
utils::io::sstream out;
this->export_graphviz(out, viewName);
info.setGraphvizData(utils::CString(out.c_str()));
return info;
#else
return fgviewer::FrameGraphInfo();

View File

@@ -440,7 +440,7 @@ public:
bool isAcyclic() const noexcept;
//! export a graphviz view of the graph
void export_graphviz(utils::io::ostream& out, const char* name = nullptr) const noexcept;
void export_graphviz(utils::io::ostream& out, const char* name = nullptr);
/**
* Export a fgviewer::FrameGraphInfo for current graph.

View File

@@ -171,7 +171,7 @@ public:
bool isEdgeValid(Edge const* edge) const noexcept;
//! export a graphviz view of the graph
void export_graphviz(utils::io::ostream& out, const char* name = nullptr) const noexcept;
void export_graphviz(utils::io::ostream& out, const char* name = nullptr);
bool isAcyclic() const noexcept;

View File

@@ -78,23 +78,18 @@ public:
// The incoming passes should be sorted by the execution order.
void setPasses(std::vector<Pass> sortedPasses);
void setGraphvizData(utils::CString data);
const char* getViewName() const;
const std::vector<Pass>& getPasses() const;
const std::unordered_map<ResourceId, Resource>& getResources() const;
const char* getGraphvizData() const;
private:
utils::CString viewName;
// The order of the passes in the vector indicates the execution
// order of the passes.
std::vector<Pass> passes;
std::unordered_map<ResourceId, Resource> resources;
utils::CString graphvizData;
};
} // namespace filament::fgviewer

View File

@@ -31,7 +31,6 @@ FrameGraphInfo::~FrameGraphInfo() = default;
FrameGraphInfo::FrameGraphInfo(FrameGraphInfo&& rhs) noexcept = default;
bool FrameGraphInfo::operator==(const FrameGraphInfo& rhs) const {
// We skip checking graphviz here since checking passes and resources should be enough.
return viewName == rhs.viewName
&& passes == rhs.passes
&& resources == rhs.resources;
@@ -67,10 +66,6 @@ void FrameGraphInfo::setPasses(std::vector<Pass> sortedPasses) {
passes = std::move(sortedPasses);
}
void FrameGraphInfo::setGraphvizData(utils::CString data) {
graphvizData = std::move(data);
}
const char* FrameGraphInfo::getViewName() const {
return viewName.c_str_safe();
}
@@ -84,8 +79,4 @@ const std::unordered_map<ResourceId, FrameGraphInfo::Resource>&
return resources;
}
const char* FrameGraphInfo::getGraphvizData() const {
return graphvizData.c_str_safe();
}
} // namespace filament::fgviewer

View File

@@ -27,7 +27,6 @@
namespace filament::fgviewer {
namespace {
void writeJSONString(std::ostream& os, const char* str) {
os << '"';
const char* p = str;
@@ -47,7 +46,7 @@ void writeJSONString(std::ostream& os, const char* str) {
void writeViewName(std::ostream& os, const FrameGraphInfo& frameGraph) {
os << " \"viewName\": ";
writeJSONString(os, frameGraph.getViewName());
os << "\n";
os << ",\n";
}
void writeResourceIds(std::ostream& os, const std::vector<ResourceId>& resources) {
@@ -81,7 +80,7 @@ void writePasses(std::ostream& os, const FrameGraphInfo& frameGraph) {
if (i + 1 < passes.size()) os << ",";
os << "\n";
}
os << " ]\n";
os << " ],\n";
}
void writeResources(std::ostream& os, const FrameGraphInfo& frameGraph) {
@@ -115,15 +114,6 @@ void writeResources(std::ostream& os, const FrameGraphInfo& frameGraph) {
}
os << " }\n";
}
void writeGraphviz(std::ostream& os, const FrameGraphInfo& frameGraph) {
const char* graphvizString = frameGraph.getGraphvizData();
os << " \"graphviz\": ";
writeJSONString(os, graphvizString);
os << "\n";
}
} // anonymous
const char* JsonWriter::getJsonString() const {
@@ -138,12 +128,8 @@ bool JsonWriter::writeFrameGraphInfo(const FrameGraphInfo& frameGraph) {
std::ostringstream os;
writeViewName(os, frameGraph);
os << ",\n";
writePasses(os, frameGraph);
os << ",\n";
writeResources(os, frameGraph);
os << ",\n";
writeGraphviz(os, frameGraph);
mJsonString = utils::CString(os.str().c_str());

View File

@@ -15,8 +15,6 @@
*/
import {LitElement, html, css, unsafeCSS, nothing} from "https://unpkg.com/lit@2.8.0?module";
import { graphviz } from "https://cdn.skypack.dev/d3-graphviz@5.1.0";
import * as d3 from "https://cdn.skypack.dev/d3@7";
const kUntitledPlaceholder = "Untitled View";
@@ -35,11 +33,6 @@ const READ_WRITE_COLOR = '#ffeb99';
const DEFAULT_COLOR = '#ffffff';
const SUBRESOURCE_COLOR = '#d3d3d3';
// Constants for view mode toggle
const VIEW_TOGGLE_INACTIVE_COLOR = '#292929';
const VIEW_TOGGLE_UNSELECTED_COLOR = '#3f4cbe';
const VIEW_TOGGLE_SELECTED_COLOR = '#2c3892';
const RESOURCE_USAGE_TYPE_READ = 'read';
const RESOURCE_USAGE_TYPE_WRITE = 'write';
const RESOURCE_USAGE_TYPE_NO_ACCESS = 'no-access';
@@ -47,10 +40,6 @@ const RESOURCE_USAGE_TYPE_READ_WRITE = 'read-write';
const IS_SUBRESOURCE_KEY = 'is_subresource_of'
// View mode constants
const VIEW_MODE_TABLE = 'table';
const VIEW_MODE_GRAPHVIZ = 'graphviz';
class MenuSection extends LitElement {
static get properties() {
return {
@@ -161,35 +150,19 @@ class FrameGraphSidePanel extends LitElement {
font-weight: bolder;
color: ${FOREGROUND_COLOR};
}
.resource-title {
display: flex;
flex-direction: column;
margin-bottom: 5px;
font-size: ${REGULAR_FONT_SIZE}px;
color: ${UNSELECTED_COLOR};
}
.resource-content {
display: flex;
flex-direction: column;
font-size: ${REGULAR_FONT_SIZE}px;
color: ${UNSELECTED_COLOR};
}
.view-mode-selector {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 15px;
}
.view-toggle {
display: inline-block;
background-color: ${this.connected ? VIEW_TOGGLE_UNSELECTED_COLOR:VIEW_TOGGLE_INACTIVE_COLOR};
color: white;
padding: 5px 10px;
margin: 5px;
border-radius: 4px;
cursor: pointer;
user-select: none;
font-size: 12px;
text-align: center;
flex: 1;
}
.view-toggle.active {
background-color: ${this.connected ? VIEW_TOGGLE_SELECTED_COLOR:VIEW_TOGGLE_INACTIVE_COLOR};
font-weight: bold;
}
`;
}
@@ -198,7 +171,6 @@ class FrameGraphSidePanel extends LitElement {
connected: {type: Boolean, attribute: 'connected'},
selectedFrameGraph: {type: String, attribute: 'selected-framegraph'},
selectedResourceId: {type: Number, attribute: 'selected-resource'},
viewMode: {type: String, attribute: 'view-mode'},
database: {type: Object, state: true},
framegraphs: {type: Array, state: true},
@@ -210,7 +182,6 @@ class FrameGraphSidePanel extends LitElement {
this.connected = false;
this.framegraphs = [];
this.database = {};
this.viewMode = VIEW_MODE_TABLE;
}
updated(props) {
@@ -228,6 +199,7 @@ class FrameGraphSidePanel extends LitElement {
}
}
_handleFrameGraphClick(ev) {
this.dispatchEvent(new CustomEvent('select-framegraph', {
detail: ev,
@@ -236,85 +208,41 @@ class FrameGraphSidePanel extends LitElement {
}));
}
_handleViewModeClick(mode) {
this.dispatchEvent(new CustomEvent('change-view-mode', {
detail: mode,
bubbles: true,
composed: true
}));
}
_findCurrentResource() {
if (!this.selectedFrameGraph)
return null;
const resources = this.database[this.selectedFrameGraph]?.resources;
if (resources) {
const resource = resources[this.selectedResourceId];
if (resource) {
return resource;
}
}
return null;
const frameGraph = this.database[this.selectedFrameGraph];
return Object.values(frameGraph?.resources)
.find(resource => resource.id === this.selectedResourceId) || null;
}
render() {
const renderFrameGraphs = (title) => {
if (!this.framegraphs.length)
return nothing;
if (!this.framegraphs.length) return nothing;
return html`
<menu-section title="${title}">
${this.selectedFrameGraph ? html`
<div class="view-mode-selector">
<div class="view-toggle ${this.viewMode === VIEW_MODE_TABLE ? 'active' : ''}"
@click="${() => this._handleViewModeClick(VIEW_MODE_TABLE)}">
Table Mode
${this.framegraphs.map(({ fgid, name }) => html`
<div class="framegraph"
@click="${() => this._handleFrameGraphClick(fgid)}"
data-id="${fgid}">
${fgid === this.selectedFrameGraph ? '● ' : ''}${name}
</div>
<div class="view-toggle ${this.viewMode === VIEW_MODE_GRAPHVIZ ? 'active' : ''}"
@click="${() => this._handleViewModeClick(VIEW_MODE_GRAPHVIZ)}">
Graph Mode
</div>
</div>
` : nothing}
<div class="framegraphs">
${this.framegraphs.map(({fgid, name}) => {
const isSelected = this.selectedFrameGraph === fgid;
return html`
<div @click="${() => this._handleFrameGraphClick(fgid)}"
class="framegraph ${isSelected ? 'selected' : ''}">
${isSelected ? '● ' : ''}${name}
</div>`;
})}
</div>
`)}
</menu-section>
`;
};
const renderResourceDetails = (title) => {
const currentResource = this._findCurrentResource();
if (!currentResource)
return nothing;
if (!currentResource) return nothing;
return html`
<menu-section title="${title}">
<div class="resource-content">
<div><b>Name:</b> ${currentResource.name}</div>
</div>
<div class="resource-content">
<div><b>ID:</b> ${currentResource.id}</div>
</div>
${currentResource.properties.length > 0 ? html`
<div class="resource-content">
<div><b>Properties:</b></div>
<ul>
${currentResource.properties.map(prop => html`
<li>${prop.key}: ${prop.value}</li>
`)}
</ul>
</div>
` : ''}
<div class="resource-title">${currentResource.id}: ${currentResource.name}</div>
${currentResource.properties?.map(({ key, value }) => html`
<div class="resource-content">${key}: ${value}</div>
`)}
</menu-section>
`;
};
@@ -328,6 +256,7 @@ class FrameGraphSidePanel extends LitElement {
</div>
`;
}
}
customElements.define("framegraph-sidepanel", FrameGraphSidePanel);
@@ -551,8 +480,7 @@ class FrameGraphTable extends LitElement {
}
render() {
if (!this.frameGraphData?.passes || !this.frameGraphData?.resources)
return nothing;
if (!this.frameGraphData?.passes || !this.frameGraphData?.resources) return nothing;
const allPasses = this.frameGraphData.passes;
const resources = Object.values(this.frameGraphData.resources);
@@ -577,82 +505,6 @@ class FrameGraphTable extends LitElement {
customElements.define("framegraph-table", FrameGraphTable);
class GraphvizView extends LitElement {
static get styles() {
return css`
:host {
display: block;
flex-grow: 1;
width: 80%;
}
.graphviz-container {
margin-left: 300px;
height: 100vh;
width: calc(100% - 300px);
overflow: auto;
background-color: white;
}
#graph {
width: 100%;
height: 100%;
}
`;
}
static get properties() {
return {
graphvizData: {type: String, state: true},
};
}
constructor() {
super();
this.graphvizData = '';
}
updated(changedProps) {
if (changedProps.has('graphvizData')) {
this._renderGraphviz();
}
}
_renderGraphviz() {
if (!this.graphvizData)
return;
const container = this.renderRoot.querySelector('#graphviz-view');
if (!container)
return;
try {
const viz = d3.select(container)
.graphviz({ useWorker: false })
.zoom(true)
.fit(true);
viz.renderDot(this.graphvizData);
} catch (error) {
console.error('Failed to render graphviz:', error);
container.innerHTML = `<div class="error">Failed to render graphviz: ${error.message}</div>`;
}
}
render() {
if(!this.graphvizData)
return nothing;
return html`
<div class="graphviz-container">
<div id="graphviz-view"></div>
</div>
`;
}
}
customElements.define("graphviz-view", GraphvizView);
class FrameGraphViewer extends LitElement {
static get styles() {
return css`
@@ -671,10 +523,6 @@ class FrameGraphViewer extends LitElement {
get _framegraphTable() {
return this.renderRoot.querySelector('#table');
}
get _graphvizView() {
return this.renderRoot.querySelector('#graphviz');
}
async init() {
const isConnected = () => this.connected;
@@ -687,7 +535,6 @@ class FrameGraphViewer extends LitElement {
let fgInfo = await fetchFrameGraph(fgid);
this.database[fgInfo.fgid] = fgInfo;
this._framegraphTable.frameGraphData = fgInfo;
this._graphvizView.graphvizData = fgInfo.graphviz;
}
}
);
@@ -708,7 +555,6 @@ class FrameGraphViewer extends LitElement {
this.database = {};
this.selectedFrameGraph = null;
this.selectedResourceId = -1;
this.viewMode = VIEW_MODE_TABLE;
this.init();
this.addEventListener('select-framegraph',
@@ -722,12 +568,6 @@ class FrameGraphViewer extends LitElement {
this.selectedResourceId = ev.detail;
}
);
this.addEventListener('change-view-mode',
(ev) => {
this.viewMode = ev.detail;
}
);
}
static get properties() {
@@ -736,7 +576,6 @@ class FrameGraphViewer extends LitElement {
database: {type: Object, state: true},
selectedFrameGraph: {type: String, state: true},
selectedResourceId: {type: Number, state: true},
viewMode: {type: String, state: true},
}
}
@@ -744,7 +583,6 @@ class FrameGraphViewer extends LitElement {
if (props.has('selectedFrameGraph') || props.has('database')) {
const framegraph = this._getFrameGraph();
this._framegraphTable.frameGraphData = framegraph;
this._graphvizView.graphvizData = framegraph?.graphviz;
this._sidePanel.database = this.database;
}
}
@@ -755,21 +593,13 @@ class FrameGraphViewer extends LitElement {
<framegraph-sidepanel id="sidepanel"
?connected="${this.connected}"
selected-framegraph="${this.selectedFrameGraph}"
selected-resource="${this.selectedResourceId}"
view-mode="${this.viewMode}">
selected-resource="${this.selectedResourceId}">
</framegraph-sidepanel>
<framegraph-table id="table"
style="display: ${this.viewMode === VIEW_MODE_TABLE ? 'block' : 'none'};"
?connected="${this.connected}"
selected-framegraph="${this.selectedFrameGraph}"
selected-resource="${this.selectedResourceId}">
</framegraph-table>
<graphviz-view id="graphviz"
style="display: ${this.viewMode === VIEW_MODE_GRAPHVIZ ? 'block' : 'none'};"
framegraph-id="${this.selectedFrameGraph}">
</graphviz-view>
`;
}
}

View File

@@ -19,13 +19,13 @@ if [[ "$GITHUB_WORKFLOW" ]]; then
set -e
fi
OS_NAME=$(uname -s)
LLVM_VERSION=${GITHUB_LLVM_VERSION-16}
MESA_VERSION=${GITHUB_MESA_VERSION-24.2.1}
os_name=$(uname -s)
LLVM_VERSION=16
MESA_VERSION=${MESA_VERSION-24.2.1}
ORIG_DIR=$(pwd)
MESA_DIR=${MESA_DIR-${ORIG_DIR}/mesa}
if [[ "$OS_NAME" == "Linux" ]]; then
if [[ "$os_name" == "Linux" ]]; then
sudo apt install python3-venv
fi
@@ -42,7 +42,7 @@ done
deactivate
# Install system deps
if [[ "$OS_NAME" == "Linux" ]]; then
if [[ "$os_name" == "Linux" ]]; then
if [[ "$GITHUB_WORKFLOW" ]]; then
# We only want to do this if it is a CI machine.
sudo apt-get -y remove llvm-*
@@ -64,17 +64,18 @@ if [[ "$OS_NAME" == "Linux" ]]; then
sudo apt -y remove llvm-18 llvm-18-* llvm-19 llvm-19-*
set -e
CURRENT_CLANG_VERSION=$(clang --version | head -n 1 | awk '{ print $4 }' | awk 'BEGIN { FS="\\." } { print $1 }')
CLANG_VERSION=${CURRENT_CLANG_VERSION:-${LLVM_VERSION}}
sudo apt-get -y install clang-${CLANG_VERSION} \
libc++-${CLANG_VERSION}-dev \
libc++abi-${CLANG_VERSION}-dev \
GITHUB_CLANG_VERSION=${GITHUB_CLANG_VERSION:-${CURRENT_CLANG_VERSION}}
GITHUB_CLANG_VERSION=${GITHUB_CLANG_VERSION:-${LLVM_VERSION}}
sudo apt-get -y install clang-${GITHUB_CLANG_VERSION} \
libc++-${GITHUB_CLANG_VERSION}-dev \
libc++abi-${GITHUB_CLANG_VERSION}-dev \
llvm-${LLVM_VERSION} \
llvm-${LLVM_VERSION}-{dev,tools,runtime}
! command -v clang > /dev/null 2>&1 && \
sudo ln -s /usr/bin/clang-${CLANG_VERSION} /usr/bin/clang && \
sudo ln -s /usr/bin/clang++-${CLANG_VERSION} /usr/bin/clang++
sudo ln -s /usr/bin/clang-${GITHUB_CLANG_VERSION} /usr/bin/clang && \
sudo ln -s /usr/bin/clang++-${GITHUB_CLANG_VERSION} /usr/bin/clang++
fi # [[ "$GITHUB_WORKFLOW" ]]
elif [[ "$OS_NAME" == "Darwin" ]]; then
elif [[ "$os_name" == "Darwin" ]]; then
if [[ ! "$GITHUB_WORKFLOW" ]]; then
if [ ! command -v brew > /dev/null 2>&1 ]; then
echo "Error: need to install homebrew to continue"
@@ -82,7 +83,7 @@ elif [[ "$OS_NAME" == "Darwin" ]]; then
fi
fi
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=true brew install autoconf automake libx11 libxext libxrandr llvm@${LLVM_VERSION} ninja meson pkg-config libxshmfence
fi # [[ "$OS_NAME" == x ]]
fi # [[ "$os_name" == x ]]
LOCAL_LDFLAGS=${LDFLAGS}
LOCAL_CPPFLAGS=${CPPFLAGS}
@@ -121,7 +122,7 @@ mkdir -p out
source ${ORIG_DIR}/venv/bin/activate
if [[ "$OS_NAME" == "Darwin" ]]; then
if [[ "$os_name" == "Darwin" ]]; then
LOCAL_LDFLAGS="-L/opt/homebrew/opt/llvm@${LLVM_VERSION}/lib"
LOCAL_CPPFLAGS="-I/opt/homebrew/opt/llvm@${LLVM_VERSION}/include -I/opt/homebrew/include"
LOCAL_PATH=${PATH}:/opt/homebrew/opt/llvm@${LLVM_VERSION}/bin