Compare commits
10 Commits
ebridgewat
...
ebridgewat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e1499d1a1 | ||
|
|
586c82ef10 | ||
|
|
7ece76a679 | ||
|
|
9959b104f9 | ||
|
|
f024214621 | ||
|
|
8a3bf04eef | ||
|
|
44840a481e | ||
|
|
8070643ba5 | ||
|
|
0c33f9f2a3 | ||
|
|
db72bd024b |
7
.github/actions/dep-versions/action.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
name: 'Set up dependency versions'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Set up dependency versions
|
||||
shell: bash
|
||||
run: cat ./build/common/versions >> $GITHUB_ENV
|
||||
38
.github/actions/linux-prereq/action.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
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
|
||||
23
.github/actions/mac-prereq/action.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
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
|
||||
3
.github/workflows/android-continuous.yml
vendored
@@ -16,6 +16,9 @@ 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:
|
||||
|
||||
3
.github/workflows/ios-continuous.yml
vendored
@@ -14,6 +14,9 @@ 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
|
||||
|
||||
3
.github/workflows/linux-continuous.yml
vendored
@@ -14,6 +14,9 @@ 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
|
||||
|
||||
3
.github/workflows/mac-continuous.yml
vendored
@@ -14,6 +14,9 @@ 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
|
||||
|
||||
82
.github/workflows/presubmit.yml
vendored
@@ -9,22 +9,32 @@ on:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build-desktop:
|
||||
name: build-desktop
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-14-xlarge, ubuntu-22.04-16core]
|
||||
|
||||
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: |
|
||||
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
|
||||
cd build/$WORKFLOW_OS && printf "y" | ./build.sh presubmit
|
||||
cd build/mac && printf "y" | ./build.sh presubmit
|
||||
- name: Test material parser
|
||||
run: |
|
||||
out/cmake-release/filament/test/test_material_parser
|
||||
|
||||
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
|
||||
- name: Test material parser
|
||||
run: |
|
||||
out/cmake-release/filament/test/test_material_parser
|
||||
@@ -35,6 +45,8 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run build script
|
||||
run: |
|
||||
build\windows\build-github.bat presubmit
|
||||
@@ -48,6 +60,7 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
@@ -66,6 +79,7 @@ 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
|
||||
@@ -79,6 +93,9 @@ 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
|
||||
@@ -101,30 +118,20 @@ 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:
|
||||
python-version: '3.x'
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Cache Mesa and deps
|
||||
id: mesa-cache
|
||||
uses: actions/cache@v4 # Use a specific version
|
||||
with:
|
||||
path: |
|
||||
$HOME/Library/Caches/Homebrew
|
||||
mesa
|
||||
key: ${{ runner.os }}-mesa-deps-${{ vars.MESA_VERSION }}
|
||||
path: mesa
|
||||
key: ${{ runner.os }}-mesa-deps-2-${{ vars.MESA_VERSION }}
|
||||
- name: Get Mesa
|
||||
id: mesa-prereq
|
||||
env:
|
||||
MESA_VERSION: ${{ vars.MESA_VERSION }}
|
||||
run: |
|
||||
bash test/utils/get_mesa.sh
|
||||
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
|
||||
@@ -133,11 +140,13 @@ 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: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat filament
|
||||
run: ./build.sh -W debug test_filamat filament
|
||||
- name: Run test
|
||||
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
|
||||
|
||||
@@ -148,23 +157,16 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- 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
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Install clang-tidy and deps
|
||||
run: |
|
||||
pip install pyyaml
|
||||
brew install llvm
|
||||
sudo ln -s "$(brew --prefix llvm)/bin/clang-tidy" "/usr/local/bin/clang-tidy"
|
||||
brew install llvm@${GITHUB_LLVM_VERSION}
|
||||
sudo ln -s "$(brew --prefix llvm)@${GITHUB_LLVM_VERSION}/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
|
||||
|
||||
48
.github/workflows/release.yml
vendored
@@ -29,8 +29,42 @@ on:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
build-desktop:
|
||||
name: build-desktop
|
||||
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
|
||||
runs-on: ${{ matrix.os }}
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
|
||||
|
||||
@@ -49,15 +83,14 @@ 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: |
|
||||
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
|
||||
cd build/$WORKFLOW_OS && printf "y" | ./build.sh release
|
||||
cd build/mac && printf "y" | ./build.sh release
|
||||
cd ../..
|
||||
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;
|
||||
mv out/filament-release-darwin.tgz out/filament-${TAG}-mac.tgz
|
||||
- uses: actions/github-script@v6
|
||||
env:
|
||||
TAG: ${{ steps.git_ref.outputs.tag }}
|
||||
@@ -84,6 +117,7 @@ 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 }}
|
||||
@@ -121,6 +155,7 @@ jobs:
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- name: Run build script
|
||||
env:
|
||||
TAG: ${{ steps.git_ref.outputs.tag }}
|
||||
@@ -171,6 +206,7 @@ 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 }}
|
||||
|
||||
3
.github/workflows/web-continuous.yml
vendored
@@ -14,6 +14,9 @@ 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
|
||||
|
||||
@@ -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,30 +30,27 @@ 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 0
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Unless explicitly specified, NDK version will be set to match exactly the required one
|
||||
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/ndk.version)}
|
||||
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
|
||||
|
||||
# Install the required NDK version specifically (if not present)
|
||||
if [[ ! -d "${ANDROID_HOME}/ndk/$FILAMENT_NDK_VERSION" ]]; then
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
27.0.11718014
|
||||
@@ -2,5 +2,4 @@
|
||||
|
||||
if [[ "$GITHUB_WORKFLOW" ]]; then
|
||||
echo "Running workflow $GITHUB_WORKFLOW (event: $GITHUB_EVENT_NAME, action: $GITHUB_ACTION)"
|
||||
CONTINUOUS_INTEGRATION=true
|
||||
fi
|
||||
|
||||
18
build/common/get-ninja.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/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
|
||||
6
build/common/versions
Normal file
@@ -0,0 +1,6 @@
|
||||
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
|
||||
@@ -28,7 +28,6 @@ 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
|
||||
@@ -41,4 +40,3 @@ if [[ "${GENERATE_ARCHIVES}" ]]; then
|
||||
fi
|
||||
|
||||
./build.sh -i -p ios -c $BUILD_SIMULATOR $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/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"
|
||||
@@ -32,7 +32,6 @@ 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
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/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
|
||||
@@ -28,7 +28,6 @@ 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
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/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"
|
||||
@@ -31,10 +31,16 @@ 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}/android/ndk.version" FILAMENT_NDK_VERSION)
|
||||
string(REGEX MATCH "^\\d+" FILAMENT_NDK_VERSION ${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()
|
||||
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)
|
||||
|
||||
@@ -32,10 +32,16 @@ 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}/android/ndk.version" FILAMENT_NDK_VERSION)
|
||||
string(REGEX MATCH "^\\d+" FILAMENT_NDK_VERSION ${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()
|
||||
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)
|
||||
|
||||
@@ -31,10 +31,16 @@ 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}/android/ndk.version" FILAMENT_NDK_VERSION)
|
||||
string(REGEX MATCH "^\\d+" FILAMENT_NDK_VERSION ${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()
|
||||
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)
|
||||
|
||||
@@ -31,10 +31,16 @@ 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}/android/ndk.version" FILAMENT_NDK_VERSION)
|
||||
string(REGEX MATCH "^\\d+" FILAMENT_NDK_VERSION ${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()
|
||||
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)
|
||||
|
||||
@@ -1,16 +1,4 @@
|
||||
#!/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
|
||||
|
||||
@@ -259,6 +259,8 @@ 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
|
||||
|
||||
@@ -66,53 +66,62 @@ namespace {
|
||||
};
|
||||
wgpu::ShaderModule module = device.CreateShaderModule(&descriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(module != nullptr) << "Failed to create " << descriptor.label;
|
||||
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";
|
||||
|
||||
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:
|
||||
break;
|
||||
}
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(errorCount < 1)
|
||||
<< errorCount << " error(s) compiling " << descriptor.label << ":\n"
|
||||
<< errorStream.str();
|
||||
}
|
||||
FWGPU_LOGD << descriptor.label << " compiled successfully" << utils::io::endl;
|
||||
});
|
||||
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);
|
||||
return module;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "webgpu/WebGPUDriver.h"
|
||||
|
||||
#include "WebGPUPipelineCreation.h"
|
||||
#include "WebGPUSwapChain.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
@@ -24,18 +25,20 @@
|
||||
#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>
|
||||
@@ -371,9 +374,7 @@ Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
|
||||
return allocHandle<WGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept { return allocHandle<WGPUTexture>(); }
|
||||
|
||||
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
|
||||
return allocHandle<WGPUProgram>();
|
||||
@@ -392,7 +393,7 @@ Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
|
||||
@@ -420,7 +421,7 @@ Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
||||
@@ -432,15 +433,15 @@ Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcep
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImage2S() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPUTexture>();
|
||||
}
|
||||
|
||||
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||
@@ -494,30 +495,46 @@ void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byte
|
||||
|
||||
void WebGPUDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage usage) {}
|
||||
TextureUsage usage) {
|
||||
constructHandle<WGPUTexture>(th, target, levels, format, samples, w, h, depth, usage, mDevice);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
uint8_t baseLevel, uint8_t levelCount) {}
|
||||
uint8_t baseLevel, uint8_t levelCount) {
|
||||
auto source = handleCast<WGPUTexture>(srch);
|
||||
|
||||
constructHandle<WGPUTexture>(th, source, baseLevel, levelCount);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
|
||||
backend::TextureSwizzle a) {}
|
||||
backend::TextureSwizzle a) {
|
||||
PANIC_POSTCONDITION("Swizzle WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage) {}
|
||||
Platform::ExternalImageHandleRef externalImage) {
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* externalImage) {}
|
||||
void* externalImage) {
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* image, uint32_t plane) {}
|
||||
void* image, uint32_t plane) {
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::importTextureR(Handle<HwTexture> th, intptr_t id, SamplerType target,
|
||||
uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h,
|
||||
uint32_t depth, TextureUsage usage) {}
|
||||
uint32_t depth, TextureUsage usage) {
|
||||
PANIC_POSTCONDITION("Import WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
|
||||
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {
|
||||
@@ -594,7 +611,7 @@ FenceStatus WebGPUDriver::getFenceStatus(Handle<HwFence> fh) {
|
||||
// We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that
|
||||
// the GPU supports the given texture format with non-zero optimal tiling features.
|
||||
bool WebGPUDriver::isTextureFormatSupported(TextureFormat format) {
|
||||
return true;
|
||||
return WGPUTexture::fToWGPUTextureFormat(format) != wgpu::TextureFormat::Undefined;
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isTextureSwizzleSupported() {
|
||||
@@ -859,6 +876,44 @@ 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) {
|
||||
@@ -917,24 +972,22 @@ void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> dsh,
|
||||
|
||||
void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> dsh,
|
||||
backend::descriptor_binding_t binding, Handle<HwTexture> th, SamplerParams params) {
|
||||
/*
|
||||
auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
|
||||
auto texture = handleCast<WGPUTexture>(th);
|
||||
|
||||
// TODO very high odds badd assumptions are in here about handling HwTexture. Revisit with more
|
||||
// understanding. Right now assuming there is a wgpu::TextureView filled in
|
||||
if (!bindGroup->getIsLocked()) {
|
||||
// Dawn will cache duplicate samplers, so we don't strictly need to maintain a cache.
|
||||
// Making a cache might save us minor perf by reducing param translation
|
||||
auto sampler = makeSampler(params);
|
||||
// TODO making assumptions that size and offset mean the same thing here.
|
||||
wgpu::BindGroupEntry tEntry{ .binding = static_cast<uint32_t>(binding * 2),
|
||||
.textureView = texture->texView };
|
||||
.textureView = texture->getTexView() };
|
||||
bindGroup->addEntry(tEntry.binding, std::move(tEntry));
|
||||
|
||||
wgpu::BindGroupEntry sEntry{ .binding = static_cast<uint32_t>(binding * 2 + 1),
|
||||
.sampler = texture->sampler };
|
||||
.sampler = sampler };
|
||||
bindGroup->addEntry(sEntry.binding, std::move(sEntry));
|
||||
}
|
||||
//TODO Just the setup, this function stilll needs the rest of logic implemented
|
||||
*/
|
||||
}
|
||||
|
||||
void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh, backend::descriptor_set_t set,
|
||||
@@ -946,5 +999,121 @@ void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh, backend::descr
|
||||
|
||||
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
||||
}
|
||||
wgpu::Sampler WebGPUDriver::makeSampler(SamplerParams const& params) {
|
||||
wgpu::SamplerDescriptor desc;
|
||||
|
||||
desc.label = "TODO";
|
||||
desc.addressModeU = fWrapModeToWAddressMode(params.wrapS);
|
||||
desc.addressModeV = fWrapModeToWAddressMode(params.wrapR);
|
||||
desc.addressModeW = fWrapModeToWAddressMode(params.wrapT);
|
||||
switch (params.filterMag) {
|
||||
case SamplerMagFilter::NEAREST: {
|
||||
desc.magFilter = wgpu::FilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMagFilter::LINEAR: {
|
||||
desc.magFilter = wgpu::FilterMode::Linear;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (params.filterMin) {
|
||||
case SamplerMinFilter::NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
// Metal Driver uses an explicit not-mipmapped value webgpu lacks. Nearest should
|
||||
// suffice
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
// Metal Driver uses an explicit not-mipmapped value webgpu lacks. Nearest should
|
||||
// suffice
|
||||
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
|
||||
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (params.compareFunc) {
|
||||
case SamplerCompareFunc::LE: {
|
||||
desc.compare = wgpu::CompareFunction::LessEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::GE: {
|
||||
desc.compare = wgpu::CompareFunction::GreaterEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::L: {
|
||||
desc.compare = wgpu::CompareFunction::Less;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::G: {
|
||||
desc.compare = wgpu::CompareFunction::Greater;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::E: {
|
||||
desc.compare = wgpu::CompareFunction::Equal;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::NE: {
|
||||
desc.compare = wgpu::CompareFunction::NotEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::A: {
|
||||
desc.compare = wgpu::CompareFunction::Always;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::N: {
|
||||
desc.compare = wgpu::CompareFunction::Never;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
desc.maxAnisotropy = 1u << params.anisotropyLog2;
|
||||
|
||||
|
||||
// Unused: Filament's compareMode, WGPU lodMinClamp/lodMaxClamp
|
||||
|
||||
return mDevice.CreateSampler();
|
||||
}
|
||||
wgpu::AddressMode WebGPUDriver::fWrapModeToWAddressMode(const SamplerWrapMode& fWrapMode) {
|
||||
switch (fWrapMode) {
|
||||
case SamplerWrapMode::CLAMP_TO_EDGE: {
|
||||
return wgpu::AddressMode::ClampToEdge;
|
||||
break;
|
||||
}
|
||||
case SamplerWrapMode::REPEAT: {
|
||||
return wgpu::AddressMode::Repeat;
|
||||
break;
|
||||
}
|
||||
case SamplerWrapMode::MIRRORED_REPEAT: {
|
||||
return wgpu::AddressMode::MirrorRepeat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return wgpu::AddressMode::Undefined;
|
||||
}
|
||||
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -56,7 +56,8 @@ private:
|
||||
explicit WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept;
|
||||
[[nodiscard]] ShaderModel getShaderModel() const noexcept final;
|
||||
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
|
||||
|
||||
[[nodiscard]] wgpu::Sampler makeSampler(SamplerParams const& params);
|
||||
[[nodiscard]] static wgpu::AddressMode fWrapModeToWAddressMode(const filament::backend::SamplerWrapMode& fUsage);
|
||||
template<typename GPUBufferObject>
|
||||
void updateGPUBuffer(GPUBufferObject* gpuBufferObject, BufferDescriptor&& bufferDescriptor,
|
||||
uint32_t byteOffset) {
|
||||
|
||||
@@ -267,10 +267,14 @@ WebGPUDescriptorSetLayout::~WebGPUDescriptorSetLayout() {}
|
||||
|
||||
WebGPUDescriptorSet::WebGPUDescriptorSet(const wgpu::BindGroupLayout& layout, uint layoutSize)
|
||||
: mLayout(layout),
|
||||
entries(layoutSize, wgpu::BindGroupEntry{}) {
|
||||
entries(layoutSize, wgpu::BindGroupEntry{.buffer = nullptr, .sampler = nullptr, .textureView = nullptr}) {
|
||||
// Establish the size of entries based on the layout. This should be reliable and efficient.
|
||||
}
|
||||
WebGPUDescriptorSet::~WebGPUDescriptorSet() {}
|
||||
WebGPUDescriptorSet::~WebGPUDescriptorSet() {
|
||||
mBindGroup = nullptr;
|
||||
mLayout = nullptr;
|
||||
entries.clear();
|
||||
}
|
||||
|
||||
wgpu::BindGroup WebGPUDescriptorSet::lockAndReturn(const wgpu::Device& device) {
|
||||
if (mBindGroup) {
|
||||
@@ -292,6 +296,364 @@ void WebGPUDescriptorSet::addEntry(uint index, wgpu::BindGroupEntry&& entry) {
|
||||
}
|
||||
// TODO: Putting some level of trust that Filament is not going to reuse indexes or go past the
|
||||
// layout index for efficiency. Add guards if wrong.
|
||||
entries[index] = std::move(entry);
|
||||
entries.emplace_back(std::move(entry));
|
||||
}
|
||||
// From createTextureR
|
||||
WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
|
||||
wgpu::Device device) noexcept {
|
||||
|
||||
// First the texture aspect
|
||||
wgpu::TextureDescriptor desc;
|
||||
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
case SamplerType::SAMPLER_2D:
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
// Should be safe to assume external is 2d
|
||||
case SamplerType::SAMPLER_EXTERNAL: {
|
||||
desc.dimension = wgpu::TextureDimension::e2D;
|
||||
break;
|
||||
}
|
||||
case SamplerType::SAMPLER_3D: {
|
||||
desc.dimension = wgpu::TextureDimension::e3D;
|
||||
break;
|
||||
}
|
||||
}
|
||||
desc.size = { .width = width, .height = height, .depthOrArrayLayers = depth };
|
||||
desc.format = fToWGPUTextureFormat(format);
|
||||
assert_invariant(desc.format != wgpu::TextureFormat::Undefined);
|
||||
|
||||
// WGPU requires this to be true. Filament should comply
|
||||
assert(samples == 1 || samples || 4);
|
||||
|
||||
desc.sampleCount = samples;
|
||||
desc.usage = fToWGPUTextureUsage(usage);
|
||||
desc.mipLevelCount = levels;
|
||||
|
||||
// TODO Is this fine? Could do all-the-things, a naive mapping or get something from Filament
|
||||
desc.viewFormats = nullptr;
|
||||
|
||||
texture = device.CreateTexture(&desc);
|
||||
|
||||
// TODO should a default levelCount be something other than 0? Sample count?
|
||||
texView = makeTextureView(0, 1);
|
||||
}
|
||||
// From createTextureViewR
|
||||
WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept {
|
||||
texture = src->texture;
|
||||
texView = makeTextureView(baseLevel, levelCount);
|
||||
}
|
||||
|
||||
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(const TextureUsage& fUsage) {
|
||||
wgpu::TextureUsage retUsage = wgpu::TextureUsage::None;
|
||||
|
||||
// Basing this mapping off of VulkanTexture.cpp's getUsage func and suggestions from Gemini
|
||||
// TODO Validate assumptions, revisit if issues.
|
||||
if (any(TextureUsage::BLIT_SRC & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::CopySrc;
|
||||
}
|
||||
if (any((TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE) & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::CopyDst;
|
||||
}
|
||||
if (any(TextureUsage::SAMPLEABLE & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::TextureBinding;
|
||||
}
|
||||
// WGPU Render attachment covers either color or stencil situation dependant
|
||||
// NOTE: Depth attachment isn't used this way in Vulkan but logically maps to WGPU docs. If
|
||||
// issues, investigate here.
|
||||
if (any((TextureUsage::COLOR_ATTACHMENT | TextureUsage::STENCIL_ATTACHMENT |
|
||||
TextureUsage::DEPTH_ATTACHMENT) &
|
||||
fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::RenderAttachment;
|
||||
}
|
||||
|
||||
// This is from Vulkan logic- if there are any issues try disabling this first, allows perf
|
||||
// benefit though
|
||||
const bool useTransientAttachment =
|
||||
// Usage consists of attachment flags only.
|
||||
none(fUsage & ~TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Usage contains at least one attachment flag.
|
||||
any(fUsage & TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Depth resolve cannot use transient attachment because it uses a custom shader.
|
||||
// TODO: see VulkanDriver::isDepthStencilResolveSupported() to know when to remove this
|
||||
// restriction.
|
||||
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
|
||||
// and above to be able to support stencil resolve (along with depth).
|
||||
!(any(fUsage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
|
||||
if (useTransientAttachment) {
|
||||
retUsage |= wgpu::TextureUsage::TransientAttachment;
|
||||
}
|
||||
// NOTE: Unused wgpu flags:
|
||||
// StorageBinding
|
||||
// StorageAttachment
|
||||
|
||||
// NOTE: Unused Filament flags:
|
||||
// SUBPASS_INPUT VK goes to input attachment which we don't support right now
|
||||
// PROTECTED
|
||||
return retUsage;
|
||||
}
|
||||
wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(const TextureFormat& fUsage) {
|
||||
switch (fUsage) {
|
||||
case filament::backend::TextureFormat::R8:
|
||||
return wgpu::TextureFormat::R8Unorm;
|
||||
case filament::backend::TextureFormat::R8_SNORM:
|
||||
return wgpu::TextureFormat::R8Snorm;
|
||||
case filament::backend::TextureFormat::R8UI:
|
||||
return wgpu::TextureFormat::R8Uint;
|
||||
case filament::backend::TextureFormat::R8I:
|
||||
return wgpu::TextureFormat::R8Sint;
|
||||
case filament::backend::TextureFormat::STENCIL8:
|
||||
return wgpu::TextureFormat::Stencil8;
|
||||
case filament::backend::TextureFormat::R16F:
|
||||
return wgpu::TextureFormat::R16Float;
|
||||
case filament::backend::TextureFormat::R16UI:
|
||||
return wgpu::TextureFormat::R16Uint;
|
||||
case filament::backend::TextureFormat::R16I:
|
||||
return wgpu::TextureFormat::R16Sint;
|
||||
case filament::backend::TextureFormat::RG8:
|
||||
return wgpu::TextureFormat::RG8Unorm;
|
||||
case filament::backend::TextureFormat::RG8_SNORM:
|
||||
return wgpu::TextureFormat::RG8Snorm;
|
||||
case filament::backend::TextureFormat::RG8UI:
|
||||
return wgpu::TextureFormat::RG8Uint;
|
||||
case filament::backend::TextureFormat::RG8I:
|
||||
return wgpu::TextureFormat::RG8Sint;
|
||||
case filament::backend::TextureFormat::R32F:
|
||||
return wgpu::TextureFormat::R32Float;
|
||||
case filament::backend::TextureFormat::R32UI:
|
||||
return wgpu::TextureFormat::R32Uint;
|
||||
case filament::backend::TextureFormat::R32I:
|
||||
return wgpu::TextureFormat::R32Sint;
|
||||
case filament::backend::TextureFormat::RG16F:
|
||||
return wgpu::TextureFormat::RG16Float;
|
||||
case filament::backend::TextureFormat::RG16UI:
|
||||
return wgpu::TextureFormat::RG16Uint;
|
||||
case filament::backend::TextureFormat::RG16I:
|
||||
return wgpu::TextureFormat::RG16Sint;
|
||||
case filament::backend::TextureFormat::RGBA8:
|
||||
return wgpu::TextureFormat::RGBA8Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_A8:
|
||||
return wgpu::TextureFormat::RGBA8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA8_SNORM:
|
||||
return wgpu::TextureFormat::RGBA8Snorm;
|
||||
case filament::backend::TextureFormat::RGBA8UI:
|
||||
return wgpu::TextureFormat::RGBA8Uint;
|
||||
case filament::backend::TextureFormat::RGBA8I:
|
||||
return wgpu::TextureFormat::RGBA8Sint;
|
||||
case filament::backend::TextureFormat::DEPTH16:
|
||||
return wgpu::TextureFormat::Depth16Unorm;
|
||||
case filament::backend::TextureFormat::DEPTH24:
|
||||
return wgpu::TextureFormat::Depth24Plus;
|
||||
case filament::backend::TextureFormat::DEPTH32F:
|
||||
return wgpu::TextureFormat::Depth32Float;
|
||||
case filament::backend::TextureFormat::DEPTH24_STENCIL8:
|
||||
return wgpu::TextureFormat::Depth24PlusStencil8;
|
||||
case filament::backend::TextureFormat::DEPTH32F_STENCIL8:
|
||||
return wgpu::TextureFormat::Depth32FloatStencil8;
|
||||
case filament::backend::TextureFormat::RG32F:
|
||||
return wgpu::TextureFormat::RG32Float;
|
||||
case filament::backend::TextureFormat::RG32UI:
|
||||
return wgpu::TextureFormat::RG32Uint;
|
||||
case filament::backend::TextureFormat::RG32I:
|
||||
return wgpu::TextureFormat::RG32Sint;
|
||||
case filament::backend::TextureFormat::RGBA16F:
|
||||
return wgpu::TextureFormat::RGBA16Float;
|
||||
case filament::backend::TextureFormat::RGBA16UI:
|
||||
return wgpu::TextureFormat::RGBA16Uint;
|
||||
case filament::backend::TextureFormat::RGBA16I:
|
||||
return wgpu::TextureFormat::RGBA16Sint;
|
||||
case filament::backend::TextureFormat::RGBA32F:
|
||||
return wgpu::TextureFormat::RGBA32Float;
|
||||
case filament::backend::TextureFormat::RGBA32UI:
|
||||
return wgpu::TextureFormat::RGBA32Uint;
|
||||
case filament::backend::TextureFormat::RGBA32I:
|
||||
return wgpu::TextureFormat::RGBA32Sint;
|
||||
case filament::backend::TextureFormat::EAC_R11:
|
||||
return wgpu::TextureFormat::EACR11Unorm;
|
||||
case filament::backend::TextureFormat::EAC_R11_SIGNED:
|
||||
return wgpu::TextureFormat::EACR11Snorm;
|
||||
case filament::backend::TextureFormat::EAC_RG11:
|
||||
return wgpu::TextureFormat::EACRG11Unorm;
|
||||
case filament::backend::TextureFormat::EAC_RG11_SIGNED:
|
||||
return wgpu::TextureFormat::EACRG11Snorm;
|
||||
case filament::backend::TextureFormat::ETC2_RGB8:
|
||||
return wgpu::TextureFormat::ETC2RGB8Unorm;
|
||||
case filament::backend::TextureFormat::ETC2_SRGB8:
|
||||
return wgpu::TextureFormat::ETC2RGB8UnormSrgb;
|
||||
case filament::backend::TextureFormat::ETC2_RGB8_A1:
|
||||
return wgpu::TextureFormat::ETC2RGB8A1Unorm;
|
||||
case filament::backend::TextureFormat::ETC2_SRGB8_A1:
|
||||
return wgpu::TextureFormat::ETC2RGB8A1UnormSrgb;
|
||||
case filament::backend::TextureFormat::ETC2_EAC_RGBA8:
|
||||
return wgpu::TextureFormat::ETC2RGBA8Unorm;
|
||||
case filament::backend::TextureFormat::ETC2_EAC_SRGBA8:
|
||||
return wgpu::TextureFormat::ETC2RGBA8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_4x4:
|
||||
return wgpu::TextureFormat::ASTC4x4Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
|
||||
return wgpu::TextureFormat::ASTC4x4UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_5x4:
|
||||
return wgpu::TextureFormat::ASTC5x4Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_5x4:
|
||||
return wgpu::TextureFormat::ASTC5x4UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_5x5:
|
||||
return wgpu::TextureFormat::ASTC5x5Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_5x5:
|
||||
return wgpu::TextureFormat::ASTC5x5UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_6x5:
|
||||
return wgpu::TextureFormat::ASTC6x5Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_6x5:
|
||||
return wgpu::TextureFormat::ASTC6x5UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_6x6:
|
||||
return wgpu::TextureFormat::ASTC6x6Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_6x6:
|
||||
return wgpu::TextureFormat::ASTC6x6UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_8x5:
|
||||
return wgpu::TextureFormat::ASTC8x5Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x5:
|
||||
return wgpu::TextureFormat::ASTC8x5UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_8x6:
|
||||
return wgpu::TextureFormat::ASTC8x6Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x6:
|
||||
return wgpu::TextureFormat::ASTC8x6UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_8x8:
|
||||
return wgpu::TextureFormat::ASTC8x8Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x8:
|
||||
return wgpu::TextureFormat::ASTC8x8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_10x5:
|
||||
return wgpu::TextureFormat::ASTC10x5Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x5:
|
||||
return wgpu::TextureFormat::ASTC10x5UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_10x6:
|
||||
return wgpu::TextureFormat::ASTC10x6Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x6:
|
||||
return wgpu::TextureFormat::ASTC10x6UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_10x8:
|
||||
return wgpu::TextureFormat::ASTC10x8Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x8:
|
||||
return wgpu::TextureFormat::ASTC10x8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_10x10:
|
||||
return wgpu::TextureFormat::ASTC10x10Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x10:
|
||||
return wgpu::TextureFormat::ASTC10x10UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_12x10:
|
||||
return wgpu::TextureFormat::ASTC12x10Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_12x10:
|
||||
return wgpu::TextureFormat::ASTC12x10UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_12x12:
|
||||
return wgpu::TextureFormat::ASTC12x12Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_12x12:
|
||||
return wgpu::TextureFormat::ASTC12x12UnormSrgb;
|
||||
case filament::backend::TextureFormat::RED_RGTC1:
|
||||
return wgpu::TextureFormat::BC4RUnorm;
|
||||
case filament::backend::TextureFormat::SIGNED_RED_RGTC1:
|
||||
return wgpu::TextureFormat::BC4RSnorm;
|
||||
case filament::backend::TextureFormat::RED_GREEN_RGTC2:
|
||||
return wgpu::TextureFormat::BC5RGUnorm;
|
||||
case filament::backend::TextureFormat::SIGNED_RED_GREEN_RGTC2:
|
||||
return wgpu::TextureFormat::BC5RGSnorm;
|
||||
case filament::backend::TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
|
||||
return wgpu::TextureFormat::BC6HRGBUfloat;
|
||||
case filament::backend::TextureFormat::RGB_BPTC_SIGNED_FLOAT:
|
||||
return wgpu::TextureFormat::BC6HRGBFloat;
|
||||
case filament::backend::TextureFormat::RGBA_BPTC_UNORM:
|
||||
return wgpu::TextureFormat::BC7RGBAUnorm;
|
||||
case filament::backend::TextureFormat::SRGB_ALPHA_BPTC_UNORM:
|
||||
return wgpu::TextureFormat::BC7RGBAUnormSrgb;
|
||||
case filament::backend::TextureFormat::RGB565:
|
||||
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
|
||||
// and discard the alpha and lower precision.
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case filament::backend::TextureFormat::RGB9_E5:
|
||||
return wgpu::TextureFormat::RGB9E5Ufloat;
|
||||
case filament::backend::TextureFormat::RGB5_A1:
|
||||
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
|
||||
// and handle the packing/unpacking in shaders.
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case filament::backend::TextureFormat::RGBA4:
|
||||
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
|
||||
// and handle the packing/unpacking in shaders.
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case filament::backend::TextureFormat::RGB8:
|
||||
// No direct sRGB equivalent in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8:
|
||||
// No direct sRGB equivalent in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGB8_SNORM:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8Snorm;
|
||||
case filament::backend::TextureFormat::RGB8UI:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8Uint;
|
||||
case filament::backend::TextureFormat::RGB8I:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8Sint;
|
||||
case filament::backend::TextureFormat::R11F_G11F_B10F:
|
||||
return wgpu::TextureFormat::RG11B10Ufloat;
|
||||
case filament::backend::TextureFormat::UNUSED:
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case filament::backend::TextureFormat::RGB10_A2:
|
||||
return wgpu::TextureFormat::RGB10A2Unorm;
|
||||
case filament::backend::TextureFormat::RGB16F:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA16Float;
|
||||
case filament::backend::TextureFormat::RGB16UI:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA16Uint;
|
||||
case filament::backend::TextureFormat::RGB16I:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA16Sint;
|
||||
case filament::backend::TextureFormat::RGB32F:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA32Float;
|
||||
case filament::backend::TextureFormat::RGB32UI:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA32Uint;
|
||||
case filament::backend::TextureFormat::RGB32I:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA32Sint;
|
||||
case filament::backend::TextureFormat::DXT1_RGB:
|
||||
return wgpu::TextureFormat::BC1RGBAUnorm;
|
||||
case filament::backend::TextureFormat::DXT1_RGBA:
|
||||
return wgpu::TextureFormat::BC1RGBAUnorm;
|
||||
case filament::backend::TextureFormat::DXT3_RGBA:
|
||||
return wgpu::TextureFormat::BC2RGBAUnorm;
|
||||
case filament::backend::TextureFormat::DXT5_RGBA:
|
||||
return wgpu::TextureFormat::BC3RGBAUnorm;
|
||||
case filament::backend::TextureFormat::DXT1_SRGB:
|
||||
return wgpu::TextureFormat::BC1RGBAUnormSrgb;
|
||||
case filament::backend::TextureFormat::DXT1_SRGBA:
|
||||
return wgpu::TextureFormat::BC1RGBAUnormSrgb;
|
||||
case filament::backend::TextureFormat::DXT3_SRGBA:
|
||||
return wgpu::TextureFormat::BC2RGBAUnormSrgb;
|
||||
case filament::backend::TextureFormat::DXT5_SRGBA:
|
||||
return wgpu::TextureFormat::BC3RGBAUnormSrgb;
|
||||
}
|
||||
}
|
||||
|
||||
wgpu::TextureView WGPUTexture::makeTextureView(const uint8_t& baseLevel,
|
||||
const uint8_t& levelCount) {
|
||||
wgpu::TextureViewDescriptor desc;
|
||||
desc.baseMipLevel = baseLevel;
|
||||
desc.mipLevelCount = levelCount;
|
||||
|
||||
// baseArrayLayer is required, making a guess
|
||||
desc.baseArrayLayer = 0;
|
||||
// Have not found an analouge to aspect in other drivers, but ALL should be unrestrictive.
|
||||
// TODO Can we make this better?
|
||||
desc.aspect = wgpu::TextureAspect::All;
|
||||
|
||||
// The rest of the properties should be fine to leave as default, using the texture params.
|
||||
desc.label = "TODO";
|
||||
|
||||
desc.format = wgpu::TextureFormat::Undefined;
|
||||
desc.dimension = wgpu::TextureViewDimension::Undefined;
|
||||
desc.usage = wgpu::TextureUsage::None;
|
||||
|
||||
return texture.CreateView(&desc);
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -132,21 +132,28 @@ private:
|
||||
wgpu::BindGroup mBindGroup;
|
||||
};
|
||||
|
||||
// TODO: Currently WGPUTexture is not used by WebGPU for useful task.
|
||||
// Update the struct when used by WebGPU driver.
|
||||
struct WGPUTexture : public HwTexture {
|
||||
class WGPUTexture : public HwTexture {
|
||||
public:
|
||||
WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage) noexcept;
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
|
||||
wgpu::Device device) noexcept;
|
||||
|
||||
// constructors for creating texture views
|
||||
WGPUTexture(WGPUTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
|
||||
WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
|
||||
|
||||
const wgpu::Texture& getTexture() const { return texture; }
|
||||
const wgpu::TextureView& getTexView() const { return texView; }
|
||||
|
||||
// Public to allow checking for support of a texture format
|
||||
static wgpu::TextureFormat fToWGPUTextureFormat(const filament::backend::TextureFormat& fUsage);
|
||||
|
||||
private:
|
||||
wgpu::TextureView makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount);
|
||||
// CreateTextureR has info for a texture and sampler. Texture Views are needed for binding,
|
||||
// along with a sampler Current plan: Inherit the sampler and Texture to always exist (It is a
|
||||
// ref counted pointer) when making views. View is optional
|
||||
wgpu::Texture texture = nullptr;
|
||||
// TODO: Adding this but not yet setting it up. Filament "Textures" are combined image samplers,
|
||||
// rep both.
|
||||
wgpu::Sampler sampler = nullptr;
|
||||
//TODO: Not sure all the ways HwTexture is used. Overloading like this might be entirely wrong.
|
||||
wgpu::TextureView texView = nullptr;
|
||||
wgpu::TextureUsage fToWGPUTextureUsage(const filament::backend::TextureUsage& fUsage);
|
||||
};
|
||||
|
||||
struct WGPURenderPrimitive : public HwRenderPrimitive {
|
||||
@@ -170,7 +177,7 @@ struct WGPURenderTarget : public HwRenderTarget {
|
||||
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
|
||||
: level(level),
|
||||
layer(layer),
|
||||
texture(gpuTexture->texture),
|
||||
texture(gpuTexture->getTexture()),
|
||||
mWGPUTexture(gpuTexture) {}
|
||||
|
||||
uint8_t level = 0;
|
||||
|
||||
254
filament/backend/src/webgpu/WebGPUPipelineCreation.cpp
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* 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
|
||||
46
filament/backend/src/webgpu/WebGPUPipelineCreation.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -73,7 +73,8 @@ void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabiliti
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config) {
|
||||
void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
|
||||
wgpu::TextureFormat depthFormat) {
|
||||
std::stringstream formatStream{};
|
||||
formatStream << config.format;
|
||||
std::stringstream usageStream{};
|
||||
@@ -82,6 +83,8 @@ 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;
|
||||
@@ -98,10 +101,11 @@ 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
|
||||
|
||||
wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
|
||||
constexpr wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
|
||||
wgpu::TextureFormat const* availableFormats, bool useSRGBColorSpace) {
|
||||
const std::array expectedColorFormats =
|
||||
useSRGBColorSpace ?
|
||||
@@ -119,7 +123,21 @@ wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
|
||||
return *firstFoundColorFormat;
|
||||
}
|
||||
|
||||
wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
|
||||
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 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)
|
||||
@@ -133,7 +151,7 @@ wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
|
||||
return desiredPresentMode;
|
||||
}
|
||||
|
||||
wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
|
||||
constexpr wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
|
||||
wgpu::CompositeAlphaMode const* availableAlphaModes) {
|
||||
bool autoAvailable = false;
|
||||
bool inheritAvailable = false;
|
||||
@@ -209,7 +227,7 @@ void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
|
||||
namespace filament::backend {
|
||||
|
||||
WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
|
||||
wgpu::Adapter& adapter, wgpu::Device& device, uint64_t flags)
|
||||
wgpu::Adapter const& adapter, wgpu::Device const& device, uint64_t flags)
|
||||
: mSurface(surface) {
|
||||
wgpu::SurfaceCapabilities capabilities = {};
|
||||
if (!mSurface.GetCapabilities(adapter, &capabilities)) {
|
||||
@@ -220,7 +238,13 @@ 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);
|
||||
}
|
||||
|
||||
@@ -235,12 +259,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;
|
||||
// TODO we may need to ensure no surface texture is flight when we do this. some
|
||||
#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
|
||||
// synchronization may be necessary
|
||||
mSurface.Configure(&mConfig);
|
||||
}
|
||||
@@ -257,7 +281,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 = "texture_view",
|
||||
.label = "surface_texture_view",
|
||||
.format = surfaceTexture.texture.GetFormat(),
|
||||
.dimension = wgpu::TextureViewDimension::e2D,
|
||||
.baseMipLevel = 0,
|
||||
|
||||
@@ -29,10 +29,14 @@ namespace filament::backend {
|
||||
class WebGPUSwapChain final : public Platform::SwapChain, HwSwapChain {
|
||||
public:
|
||||
WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
|
||||
wgpu::Adapter& adapter, wgpu::Device& device, uint64_t flags);
|
||||
wgpu::Adapter const& adapter, wgpu::Device const& device, uint64_t flags);
|
||||
~WebGPUSwapChain();
|
||||
|
||||
wgpu::TextureView getCurrentSurfaceTextureView(wgpu::Extent2D const&);
|
||||
[[nodiscard]] wgpu::TextureFormat getColorFormat() const { return mConfig.format; }
|
||||
|
||||
[[nodiscard]] wgpu::TextureFormat getDepthFormat() const { return mDepthFormat; }
|
||||
|
||||
[[nodiscard]] wgpu::TextureView getCurrentSurfaceTextureView(wgpu::Extent2D const&);
|
||||
|
||||
void present();
|
||||
|
||||
@@ -41,6 +45,7 @@ private:
|
||||
|
||||
wgpu::Surface mSurface = {};
|
||||
wgpu::SurfaceConfiguration mConfig = {};
|
||||
wgpu::TextureFormat mDepthFormat = wgpu::TextureFormat::Undefined;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -98,10 +98,22 @@ wgpu::Adapter WebGPUPlatform::requestAdapter(wgpu::Surface const& surface) {
|
||||
}
|
||||
|
||||
wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
|
||||
// TODO consider passing required features and/or limits
|
||||
// TODO consider passing limits
|
||||
constexpr std::array desiredFeatures = { wgpu::FeatureName::DepthClipControl,
|
||||
wgpu::FeatureName::Depth32FloatStencil8, wgpu::FeatureName::CoreFeaturesAndLimits,
|
||||
wgpu::FeatureName::TransientAttachments };
|
||||
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));
|
||||
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) {
|
||||
|
||||
@@ -33,9 +33,10 @@
|
||||
#endif
|
||||
|
||||
ScreenshotParams::ScreenshotParams(int width, int height, std::string fileName,
|
||||
uint32_t expectedHash)
|
||||
uint32_t expectedHash, bool isSrgb)
|
||||
: mWidth(width),
|
||||
mHeight(height),
|
||||
mIsSrgb(isSrgb),
|
||||
mExpectedPixelHash(expectedHash),
|
||||
mFileName(std::move(fileName)) {}
|
||||
|
||||
@@ -47,6 +48,10 @@ int ScreenshotParams::height() const {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
bool ScreenshotParams::isSrgb() const {
|
||||
return mIsSrgb;
|
||||
}
|
||||
|
||||
uint32_t ScreenshotParams::expectedHash() const {
|
||||
return mExpectedPixelHash;
|
||||
}
|
||||
@@ -147,13 +152,25 @@ RenderTargetDump::RenderTargetDump(filament::backend::DriverApi& api,
|
||||
auto* internal = static_cast<RenderTargetDump::Internal*>(user);
|
||||
internal->bytesFilled = true;
|
||||
#ifndef FILAMENT_IOS
|
||||
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);
|
||||
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; });
|
||||
}
|
||||
std::string filePath = internal->params.actualFilePath();
|
||||
std::ofstream pngStream(filePath, std::ios::binary | std::ios::trunc);
|
||||
image::ImageEncoder::encode(pngStream, image::ImageEncoder::Format::PNG, image, "",
|
||||
// To avoid going from linear -> sRGB -> linear save the PNG as linear.
|
||||
image::ImageEncoder::encode(pngStream, image::ImageEncoder::Format::PNG_LINEAR, image, "",
|
||||
filePath);
|
||||
#endif
|
||||
};
|
||||
@@ -175,8 +192,12 @@ 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 utils::hash::murmur3((uint32_t*)mInternal->bytes.data(), mInternal->bytes.size() / 4, 0);
|
||||
return mInternal->hash();
|
||||
}
|
||||
|
||||
bool RenderTargetDump::bytesFilled() const {
|
||||
@@ -204,9 +225,15 @@ 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;
|
||||
<< "Failed to load expected test result: " << mFilePath << ".\n"
|
||||
<< "Did you forget to sync CMake after updating the expected image in the source "
|
||||
"directory?";
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -40,10 +40,12 @@ do { \
|
||||
*/
|
||||
class ScreenshotParams {
|
||||
public:
|
||||
ScreenshotParams(int width, int height, std::string fileName, uint32_t expectedPixelHash);
|
||||
ScreenshotParams(int width, int height, std::string fileName, uint32_t expectedPixelHash,
|
||||
bool isSrgb = false);
|
||||
|
||||
int width() const;
|
||||
int height() const;
|
||||
bool isSrgb() const;
|
||||
uint32_t expectedHash() const;
|
||||
|
||||
static std::string actualDirectoryPath();
|
||||
@@ -56,6 +58,7 @@ public:
|
||||
private:
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
bool mIsSrgb;
|
||||
uint32_t mExpectedPixelHash;
|
||||
std::string mFileName;
|
||||
};
|
||||
@@ -92,6 +95,8 @@ 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
|
||||
@@ -105,6 +110,8 @@ public:
|
||||
|
||||
uint32_t hash() const;
|
||||
|
||||
const std::vector<unsigned char>& bytes() const;
|
||||
|
||||
private:
|
||||
std::string mFilePath;
|
||||
std::vector<unsigned char> mBytes;
|
||||
|
||||
@@ -55,8 +55,10 @@ Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup
|
||||
prog.descriptorBindings(1, bindingsInfo);
|
||||
mProgram = cleanup.add(api.createProgram(std::move(prog)));
|
||||
|
||||
mDescriptorSetLayout = cleanup.add(
|
||||
api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
|
||||
if (!kLayouts.empty()) {
|
||||
mDescriptorSetLayout =
|
||||
cleanup.add(api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
|
||||
}
|
||||
}
|
||||
|
||||
filament::backend::DescriptorSetHandle Shader::createDescriptorSet(DriverApi& api) const {
|
||||
|
||||
@@ -96,6 +96,7 @@ public:
|
||||
protected:
|
||||
Cleanup& mCleanup;
|
||||
filament::backend::ProgramHandle mProgram;
|
||||
// This will be a null handle if there are no uniforms.
|
||||
filament::backend::DescriptorSetLayoutHandle mDescriptorSetLayout;
|
||||
};
|
||||
|
||||
|
||||
@@ -21,12 +21,14 @@
|
||||
#include "BackendTest.h"
|
||||
|
||||
// skipEnvironment must be a test::SkipEnvironment
|
||||
#define SKIP_IF(skipEnvironment) \
|
||||
do { \
|
||||
SkipEnvironment skip(skipEnvironment); \
|
||||
if (skip.matches()) { \
|
||||
GTEST_SKIP() << "Skipping test as the " << skip.describe(); \
|
||||
} \
|
||||
// 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; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
namespace test {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 982 B After Width: | Height: | Size: 915 B |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.6 KiB |
BIN
filament/backend/test/expected_images/pushConstants.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
62
filament/backend/test/move_actual_images_to_expected.py
Normal file
@@ -0,0 +1,62 @@
|
||||
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)
|
||||
@@ -25,7 +25,7 @@ using namespace filament::backend;
|
||||
namespace test {
|
||||
|
||||
TEST_F(BackendTest, FrameScheduledCallback) {
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
|
||||
SKIP_IF(Backend::OPENGL, "Frame callbacks are unsupported in OpenGL");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
@@ -84,7 +84,7 @@ TEST_F(BackendTest, FrameScheduledCallback) {
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, FrameCompletedCallback) {
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
|
||||
SKIP_IF(Backend::OPENGL, "Frame callbacks are unsupported in OpenGL");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "Shader.h"
|
||||
#include "Skip.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -98,6 +99,9 @@ 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);
|
||||
|
||||
@@ -239,7 +243,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", 0x70695aa1));
|
||||
ScreenshotParams(kTexWidth, kTexHeight, "FeedbackLoops", 4192780705));
|
||||
}
|
||||
|
||||
api.flush();
|
||||
|
||||
@@ -80,7 +80,7 @@ void main() {
|
||||
})";
|
||||
|
||||
TEST_F(BackendTest, PushConstants) {
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
|
||||
SKIP_IF(Backend::OPENGL, "Push constants not supported on OpenGL");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "Lifetimes.h"
|
||||
#include "Shader.h"
|
||||
#include "SharedShaders.h"
|
||||
#include "Skip.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -45,6 +46,7 @@ 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);
|
||||
|
||||
@@ -111,6 +113,7 @@ 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);
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "Lifetimes.h"
|
||||
#include "Shader.h"
|
||||
#include "SharedShaders.h"
|
||||
#include "Skip.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
using namespace filament;
|
||||
@@ -161,6 +162,7 @@ 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);
|
||||
|
||||
@@ -237,7 +239,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
api.endFrame(0);
|
||||
|
||||
EXPECT_IMAGE(renderTarget1, getExpectations(),
|
||||
ScreenshotParams(512, 512, "StencilBufferAutoResolve", 0x6CEFAC8F));
|
||||
ScreenshotParams(512, 512, "StencilBufferAutoResolve", 3353562179));
|
||||
|
||||
flushAndWait();
|
||||
getDriver().purge();
|
||||
|
||||
@@ -139,7 +139,7 @@ void DependencyGraph::clear() noexcept {
|
||||
mNodes.clear();
|
||||
}
|
||||
|
||||
void DependencyGraph::export_graphviz(utils::io::ostream& out, char const* name) {
|
||||
void DependencyGraph::export_graphviz(utils::io::ostream& out, char const* name) const noexcept {
|
||||
#ifndef NDEBUG
|
||||
const char* graphName = name ? name : "graph";
|
||||
out << "digraph \"" << graphName << "\" {\n";
|
||||
|
||||
@@ -476,7 +476,7 @@ bool FrameGraph::isAcyclic() const noexcept {
|
||||
return mGraph.isAcyclic();
|
||||
}
|
||||
|
||||
void FrameGraph::export_graphviz(utils::io::ostream& out, char const* name) {
|
||||
void FrameGraph::export_graphviz(utils::io::ostream& out, char const* name) const noexcept {
|
||||
mGraph.export_graphviz(out, name);
|
||||
}
|
||||
|
||||
@@ -573,6 +573,11 @@ 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();
|
||||
|
||||
@@ -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);
|
||||
void export_graphviz(utils::io::ostream& out, const char* name = nullptr) const noexcept;
|
||||
|
||||
/**
|
||||
* Export a fgviewer::FrameGraphInfo for current graph.
|
||||
|
||||
@@ -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);
|
||||
void export_graphviz(utils::io::ostream& out, const char* name = nullptr) const noexcept;
|
||||
|
||||
bool isAcyclic() const noexcept;
|
||||
|
||||
|
||||
@@ -78,18 +78,23 @@ 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
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ 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;
|
||||
@@ -66,6 +67,10 @@ 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();
|
||||
}
|
||||
@@ -79,4 +84,8 @@ const std::unordered_map<ResourceId, FrameGraphInfo::Resource>&
|
||||
return resources;
|
||||
}
|
||||
|
||||
const char* FrameGraphInfo::getGraphvizData() const {
|
||||
return graphvizData.c_str_safe();
|
||||
}
|
||||
|
||||
} // namespace filament::fgviewer
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
namespace filament::fgviewer {
|
||||
|
||||
namespace {
|
||||
|
||||
void writeJSONString(std::ostream& os, const char* str) {
|
||||
os << '"';
|
||||
const char* p = str;
|
||||
@@ -46,7 +47,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) {
|
||||
@@ -80,7 +81,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) {
|
||||
@@ -114,6 +115,15 @@ 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 {
|
||||
@@ -128,8 +138,12 @@ 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());
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
|
||||
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";
|
||||
|
||||
@@ -33,6 +35,11 @@ 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';
|
||||
@@ -40,6 +47,10 @@ 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 {
|
||||
@@ -150,19 +161,35 @@ 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;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -171,6 +198,7 @@ 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},
|
||||
@@ -182,6 +210,7 @@ class FrameGraphSidePanel extends LitElement {
|
||||
this.connected = false;
|
||||
this.framegraphs = [];
|
||||
this.database = {};
|
||||
this.viewMode = VIEW_MODE_TABLE;
|
||||
}
|
||||
|
||||
updated(props) {
|
||||
@@ -199,7 +228,6 @@ class FrameGraphSidePanel extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_handleFrameGraphClick(ev) {
|
||||
this.dispatchEvent(new CustomEvent('select-framegraph', {
|
||||
detail: ev,
|
||||
@@ -208,41 +236,85 @@ 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 frameGraph = this.database[this.selectedFrameGraph];
|
||||
return Object.values(frameGraph?.resources)
|
||||
.find(resource => resource.id === this.selectedResourceId) || null;
|
||||
|
||||
const resources = this.database[this.selectedFrameGraph]?.resources;
|
||||
if (resources) {
|
||||
const resource = resources[this.selectedResourceId];
|
||||
if (resource) {
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
const renderFrameGraphs = (title) => {
|
||||
if (!this.framegraphs.length) return nothing;
|
||||
if (!this.framegraphs.length)
|
||||
return nothing;
|
||||
|
||||
return html`
|
||||
<menu-section title="${title}">
|
||||
${this.framegraphs.map(({ fgid, name }) => html`
|
||||
<div class="framegraph"
|
||||
@click="${() => this._handleFrameGraphClick(fgid)}"
|
||||
data-id="${fgid}">
|
||||
${fgid === this.selectedFrameGraph ? '● ' : ''}${name}
|
||||
${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
|
||||
</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-title">${currentResource.id}: ${currentResource.name}</div>
|
||||
${currentResource.properties?.map(({ key, value }) => html`
|
||||
<div class="resource-content">${key}: ${value}</div>
|
||||
`)}
|
||||
<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>
|
||||
` : ''}
|
||||
</menu-section>
|
||||
`;
|
||||
};
|
||||
@@ -256,7 +328,6 @@ class FrameGraphSidePanel extends LitElement {
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
customElements.define("framegraph-sidepanel", FrameGraphSidePanel);
|
||||
@@ -480,7 +551,8 @@ 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);
|
||||
@@ -505,6 +577,82 @@ 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`
|
||||
@@ -523,6 +671,10 @@ class FrameGraphViewer extends LitElement {
|
||||
get _framegraphTable() {
|
||||
return this.renderRoot.querySelector('#table');
|
||||
}
|
||||
|
||||
get _graphvizView() {
|
||||
return this.renderRoot.querySelector('#graphviz');
|
||||
}
|
||||
|
||||
async init() {
|
||||
const isConnected = () => this.connected;
|
||||
@@ -535,6 +687,7 @@ class FrameGraphViewer extends LitElement {
|
||||
let fgInfo = await fetchFrameGraph(fgid);
|
||||
this.database[fgInfo.fgid] = fgInfo;
|
||||
this._framegraphTable.frameGraphData = fgInfo;
|
||||
this._graphvizView.graphvizData = fgInfo.graphviz;
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -555,6 +708,7 @@ class FrameGraphViewer extends LitElement {
|
||||
this.database = {};
|
||||
this.selectedFrameGraph = null;
|
||||
this.selectedResourceId = -1;
|
||||
this.viewMode = VIEW_MODE_TABLE;
|
||||
this.init();
|
||||
|
||||
this.addEventListener('select-framegraph',
|
||||
@@ -568,6 +722,12 @@ class FrameGraphViewer extends LitElement {
|
||||
this.selectedResourceId = ev.detail;
|
||||
}
|
||||
);
|
||||
|
||||
this.addEventListener('change-view-mode',
|
||||
(ev) => {
|
||||
this.viewMode = ev.detail;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
@@ -576,6 +736,7 @@ class FrameGraphViewer extends LitElement {
|
||||
database: {type: Object, state: true},
|
||||
selectedFrameGraph: {type: String, state: true},
|
||||
selectedResourceId: {type: Number, state: true},
|
||||
viewMode: {type: String, state: true},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,6 +744,7 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -593,13 +755,21 @@ class FrameGraphViewer extends LitElement {
|
||||
<framegraph-sidepanel id="sidepanel"
|
||||
?connected="${this.connected}"
|
||||
selected-framegraph="${this.selectedFrameGraph}"
|
||||
selected-resource="${this.selectedResourceId}">
|
||||
selected-resource="${this.selectedResourceId}"
|
||||
view-mode="${this.viewMode}">
|
||||
</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>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@ if [[ "$GITHUB_WORKFLOW" ]]; then
|
||||
set -e
|
||||
fi
|
||||
|
||||
os_name=$(uname -s)
|
||||
LLVM_VERSION=16
|
||||
MESA_VERSION=${MESA_VERSION-24.2.1}
|
||||
OS_NAME=$(uname -s)
|
||||
LLVM_VERSION=${GITHUB_LLVM_VERSION-16}
|
||||
MESA_VERSION=${GITHUB_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,18 +64,17 @@ 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 }')
|
||||
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 \
|
||||
CLANG_VERSION=${CURRENT_CLANG_VERSION:-${LLVM_VERSION}}
|
||||
sudo apt-get -y install clang-${CLANG_VERSION} \
|
||||
libc++-${CLANG_VERSION}-dev \
|
||||
libc++abi-${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-${GITHUB_CLANG_VERSION} /usr/bin/clang && \
|
||||
sudo ln -s /usr/bin/clang++-${GITHUB_CLANG_VERSION} /usr/bin/clang++
|
||||
sudo ln -s /usr/bin/clang-${CLANG_VERSION} /usr/bin/clang && \
|
||||
sudo ln -s /usr/bin/clang++-${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"
|
||||
@@ -83,7 +82,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}
|
||||
@@ -122,7 +121,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
|
||||
|
||||