Compare commits

..

1 Commits

Author SHA1 Message Date
bridgewaterrobbie
9a74936103 Update Gitignore to ignore generated files. 2025-04-22 14:23:31 -04:00
144 changed files with 1052 additions and 12603 deletions

View File

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

View File

@@ -1,38 +0,0 @@
name: 'Linux Preqrequisites'
runs:
using: "composite"
steps:
- uses: ./.github/actions/dep-versions
- name: Install Linux Prerequisites
shell: bash
run: |
set -xe
# See https://askubuntu.com/questions/272248/processing-triggers-for-man-db/1476024#1476024
echo "set man-db/auto-update false" | sudo debconf-communicate
sudo dpkg-reconfigure man-db
# Install ninja
source ./build/common/get-ninja.sh
# Install CMake
mkdir -p cmake
cd cmake
sudo wget https://github.com/Kitware/CMake/releases/download/v$GITHUB_CMAKE_VERSION/cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh
sudo chmod +x ./cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh
sudo ./cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh --skip-license > /dev/null
sudo update-alternatives --install /usr/bin/cmake cmake $(pwd)/bin/cmake 1000 --force
cd ..
sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install clang-$GITHUB_CLANG_VERSION libc++-$GITHUB_CLANG_VERSION-dev libc++abi-$GITHUB_CLANG_VERSION-dev
sudo apt-get install mesa-common-dev libxi-dev libxxf86vm-dev
# For dawn
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libx11-xcb-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100
set +xe

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,42 +29,8 @@ on:
types: [created]
jobs:
build-linux:
name: build-linux
runs-on: ubuntu-22.04-32core
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
steps:
- name: Decide Git ref
id: git_ref
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- uses: ./.github/actions/linux-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
cd build/linux && printf "y" | ./build.sh release
cd ../..
mv out/filament-release-linux.tgz out/filament-${TAG}-linux.tgz
- uses: actions/github-script@v6
env:
TAG: ${{ steps.git_ref.outputs.tag }}
with:
script: |
const upload = require('./build/common/upload-release-assets');
const { TAG } = process.env;
const globber = await glob.create('out/*.tgz');
await upload({ github, context }, await globber.glob(), TAG);
build-mac:
name: build-mac
build-desktop:
name: build-desktop
runs-on: ${{ matrix.os }}
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
@@ -83,14 +49,15 @@ jobs:
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- uses: ./.github/actions/mac-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
cd build/mac && printf "y" | ./build.sh release
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
cd build/$WORKFLOW_OS && printf "y" | ./build.sh release
cd ../..
mv out/filament-release-darwin.tgz out/filament-${TAG}-mac.tgz
if [ -f out/filament-release-darwin.tgz ]; then mv out/filament-release-darwin.tgz out/filament-${TAG}-mac.tgz; fi;
if [ -f out/filament-release-linux.tgz ]; then mv out/filament-release-linux.tgz out/filament-${TAG}-linux.tgz; fi;
- uses: actions/github-script@v6
env:
TAG: ${{ steps.git_ref.outputs.tag }}
@@ -117,7 +84,6 @@ jobs:
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- uses: ./.github/actions/linux-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
@@ -155,7 +121,6 @@ jobs:
with:
distribution: 'temurin'
java-version: '17'
- uses: ./.github/actions/linux-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
@@ -206,7 +171,6 @@ jobs:
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- uses: ./.github/actions/mac-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}

View File

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

15
.gitignore vendored
View File

@@ -18,3 +18,18 @@ test*.json
results
/compile_commands.json
/.cache
build/.cmake/
build/CMakeCache.txt
build/CMakeFiles/
build/Makefile
build/SPIRV-Tools*
build/cmake_install.cmake
build/compile_commands.json
build/filament/
build/include/
build/libs/
build/mac/ninja
build/samples/
build/shaders/
build/third_party/
build/tools/

View File

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

View File

@@ -0,0 +1 @@
27.0.11718014

View File

@@ -56,16 +56,10 @@ popd >/dev/null
rm -rf out/check-headers
mkdir -p out/check-headers
TMP_FILE=out/check-headers/temp.cpp
echo "Checking that public headers compile independently..."
for include in "${includes[@]}"; do
rm -f ${TMP_FILE}
echo "Checking ${include}"
if [[ "${include}" == "utils/Systrace.h" ]]; then
# A necessary define before we can include utils/Systrace.h
echo "#define SYSTRACE_TAG SYSTRACE_TAG_DISABLED" >> ${TMP_FILE}
fi
echo "#include <${include}>" >> ${TMP_FILE}
clang -std=c++17 -I "${FILAMENT_HEADERS}" ${TMP_FILE} -c -o /dev/null
echo "#include <${include}>" >> out/check-headers/temp.cpp
clang -std=c++17 -I "${FILAMENT_HEADERS}" out/check-headers/temp.cpp -c -o /dev/null
done
echo "Done!"

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

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

@@ -0,0 +1,38 @@
#!/bin/bash
# version of clang we want to use
export GITHUB_CLANG_VERSION=14
# version of CMake to use instead of the default one
export GITHUB_CMAKE_VERSION=3.19.5
# version of ninja to use
export GITHUB_NINJA_VERSION=1.10.2
# Steps for GitHub Workflows
if [[ "$GITHUB_WORKFLOW" ]]; then
# Install ninja
wget -q https://github.com/ninja-build/ninja/releases/download/v$GITHUB_NINJA_VERSION/ninja-linux.zip
unzip -q ninja-linux.zip
export PATH="$PWD:$PATH"
# Install CMake
mkdir -p cmake
cd cmake
sudo wget https://github.com/Kitware/CMake/releases/download/v$GITHUB_CMAKE_VERSION/cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh
sudo chmod +x ./cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh
sudo ./cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh --skip-license > /dev/null
sudo update-alternatives --install /usr/bin/cmake cmake $(pwd)/bin/cmake 1000 --force
cd ..
sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install clang-$GITHUB_CLANG_VERSION libc++-$GITHUB_CLANG_VERSION-dev libc++abi-$GITHUB_CLANG_VERSION-dev
sudo apt-get install mesa-common-dev libxi-dev libxxf86vm-dev
# For dawn
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libx11-xcb-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100
fi

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -194,8 +194,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanDriver.cpp
src/vulkan/VulkanDriver.h
src/vulkan/VulkanDriverFactory.h
src/vulkan/VulkanExternalImageManager.cpp
src/vulkan/VulkanExternalImageManager.h
src/vulkan/VulkanFboCache.cpp
src/vulkan/VulkanFboCache.h
src/vulkan/VulkanHandles.cpp
@@ -259,8 +257,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPUDriver.h
src/webgpu/WebGPUHandles.cpp
src/webgpu/WebGPUHandles.h
src/webgpu/WebGPUPipelineCreation.cpp
src/webgpu/WebGPUPipelineCreation.h
src/webgpu/WebGPUSwapChain.cpp
src/webgpu/WebGPUSwapChain.h
src/webgpu/WGPUProgram.cpp

View File

@@ -294,16 +294,6 @@ public:
VkQueue getProtectedGraphicsQueue() const noexcept;
struct ExternalImageMetadata {
/**
* The Filament texture format.
*/
TextureFormat filamentFormat;
/**
* The Filament texture usage.
*/
TextureUsage filamentUsage;
/**
* The width of the external image
*/
@@ -314,6 +304,11 @@ public:
*/
uint32_t height;
/**
* The layerCount of the external image
*/
uint32_t layerCount;
/**
* The layer count of the external image
*/
@@ -329,6 +324,11 @@ public:
*/
VkFormat format;
/**
* An external buffer can be protected. This tells you if it is.
*/
bool isProtected;
/**
* The type of external format (opaque int) if used.
*/
@@ -348,61 +348,20 @@ public:
* Heap information
*/
uint32_t memoryTypeBits;
/**
* Ycbcr conversion components
*/
VkComponentMapping ycbcrConversionComponents;
/**
* Ycbcr model
*/
VkSamplerYcbcrModelConversion ycbcrModel;
/**
* Ycbcr range
*/
VkSamplerYcbcrRange ycbcrRange;
/**
* Ycbcr x chroma offset
*/
VkChromaLocation xChromaOffset;
/**
* Ycbcr y chroma offset
*/
VkChromaLocation yChromaOffset;
};
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
using ImageData = std::pair<VkImage, VkDeviceMemory>;
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
// Note that the image metadata might change per-frame, hence we need a method for extracting
// it.
virtual ExternalImageMetadata extractExternalImageMetadata(ExternalImageHandleRef image) const {
return {};
}
virtual VkSampler createExternalSampler(SamplerYcbcrConversion chroma,
SamplerParams sampler, uint32_t internalFormat);
struct ImageData {
struct Bundle {
VkImage image = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
inline bool valid() const noexcept {
return image != VK_NULL_HANDLE;
}
};
// It's possible for the external image to also have a known VK format. We need to create an
// image for that in case we are not looking to use an external "sampler" with this image.
Bundle internal;
// If we get a externalFormat in the metadata, then we should create an image with
// VK_FORMAT_UNDEFINED
Bundle external;
};
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const {
return {};
}
virtual VkImageView createExternalImageView(SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle);
protected:
virtual ExtensionSet getSwapchainInstanceExtensions() const;
@@ -415,6 +374,20 @@ private:
// Platform dependent helper methods
static ExtensionSet getSwapchainInstanceExtensionsImpl();
static ExternalImageMetadata getExternalImageMetadataImpl(ExternalImageHandleRef externalImage,
VkDevice device);
static ImageData createExternalImageDataImpl(ExternalImageHandleRef externalImage,
VkDevice device, const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
static VkSampler createExternalSamplerImpl(VkDevice device,
SamplerYcbcrConversion chroma, SamplerParams sampler,
uint32_t internalFormat);
static VkImageView createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType,
VkComponentMapping swizzle);
// Platform dependent helper methods
static SurfaceBundle createVkSurfaceKHRImpl(void* nativeWindow, VkInstance instance,
uint64_t flags) noexcept;

View File

@@ -26,7 +26,7 @@ namespace filament::backend {
class VulkanPlatformAndroid : public VulkanPlatform {
public:
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
Platform::ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept;
struct UTILS_PUBLIC ExternalImageDescAndroid {
@@ -39,26 +39,31 @@ public:
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(
ExternalImageHandleRef externalImage) const noexcept;
virtual ExternalImageMetadata extractExternalImageMetadata(
ExternalImageHandleRef image) const override;
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override;
protected:
virtual ExtensionSet getSwapchainInstanceExtensions() const override;
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept override;
private:
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr;
bool sRGB = false;
unsigned int width; // Texture width
unsigned int height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
protected:
~ExternalImageVulkanAndroid() override;
};
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
using ImageData = VulkanPlatform::ImageData;
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
virtual ExtensionSet getSwapchainInstanceExtensions() const;
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept;
};
}// namespace filament::backend

View File

@@ -1408,8 +1408,8 @@ void MetalDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, Primit
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
auto vertexBuffer = handle_cast<MetalVertexBuffer>(vbh);
auto indexBuffer = handle_cast<MetalIndexBuffer>(ibh);
primitive->vertexBuffer = vertexBuffer;
primitive->indexBuffer = indexBuffer;
MetalVertexBufferInfo const* const vbi = handle_cast<MetalVertexBufferInfo>(vertexBuffer->vbih);
primitive->setBuffers(vbi, vertexBuffer, indexBuffer);
primitive->type = pt;
}

View File

@@ -194,8 +194,12 @@ struct MetalIndexBuffer : public HwIndexBuffer {
};
struct MetalRenderPrimitive : public HwRenderPrimitive {
MetalRenderPrimitive();
void setBuffers(MetalVertexBufferInfo const* const vbi,
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
// The pointers to MetalVertexBuffer and MetalIndexBuffer are "weak".
// The MetalVertexBuffer and MetalIndexBuffer must outlive the MetalRenderPrimitive.
MetalVertexBuffer* vertexBuffer = nullptr;
MetalIndexBuffer* indexBuffer = nullptr;
};
@@ -376,6 +380,7 @@ public:
math::uint2 getAttachmentSize() noexcept;
bool isDefaultRenderTarget() const { return defaultRenderTarget; }
uint8_t getSamples() const { return samples; }
Attachment getDrawColorAttachment(size_t index);

View File

@@ -536,6 +536,15 @@ MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uin
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
MetalRenderPrimitive::MetalRenderPrimitive() {
}
void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
this->vertexBuffer = vertexBuffer;
this->indexBuffer = indexBuffer;
}
MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept
: HwProgram(program.getName()), mContext(context) {
mToken = context.shaderCompiler->createProgram(program.getName(), std::move(program));

View File

@@ -16,15 +16,9 @@
#include "GLUtils.h"
#include "private/backend/Driver.h"
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <utils/trap.h>
#include <string_view>
#include <stddef.h>
#include "private/backend/Driver.h"
namespace filament::backend {
@@ -34,31 +28,38 @@ using namespace utils;
namespace GLUtils {
UTILS_NOINLINE
std::string_view getGLErrorString(GLenum error) noexcept {
const char* getGLError(GLenum error) noexcept {
const char* string = "unknown";
switch (error) {
case GL_NO_ERROR:
return "GL_NO_ERROR";
string = "GL_NO_ERROR";
break;
case GL_INVALID_ENUM:
return "GL_INVALID_ENUM";
string = "GL_INVALID_ENUM";
break;
case GL_INVALID_VALUE:
return "GL_INVALID_VALUE";
string = "GL_INVALID_VALUE";
break;
case GL_INVALID_OPERATION:
return "GL_INVALID_OPERATION";
string = "GL_INVALID_OPERATION";
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
return "GL_INVALID_FRAMEBUFFER_OPERATION";
string = "GL_INVALID_FRAMEBUFFER_OPERATION";
break;
case GL_OUT_OF_MEMORY:
return "GL_OUT_OF_MEMORY";
string = "GL_OUT_OF_MEMORY";
break;
default:
break;
}
return "unknown";
return string;
}
UTILS_NOINLINE
GLenum checkGLError(io::ostream& out, const char* function, size_t line) noexcept {
GLenum const error = glGetError();
if (UTILS_VERY_UNLIKELY(error != GL_NO_ERROR)) {
auto const string = getGLErrorString(error);
if (error != GL_NO_ERROR) {
const char* string = getGLError(error);
out << "OpenGL error " << io::hex << error << " (" << string << ") in \""
<< function << "\" at line " << io::dec << line << io::endl;
}
@@ -68,39 +69,46 @@ GLenum checkGLError(io::ostream& out, const char* function, size_t line) noexcep
UTILS_NOINLINE
void assertGLError(io::ostream& out, const char* function, size_t line) noexcept {
GLenum const err = checkGLError(out, function, line);
if (UTILS_VERY_UNLIKELY(err != GL_NO_ERROR)) {
if (err != GL_NO_ERROR) {
debug_trap();
}
}
UTILS_NOINLINE
std::string_view getFramebufferStatusString(GLenum status) noexcept {
const char* getFramebufferStatus(GLenum status) noexcept {
const char* string = "unknown";
switch (status) {
case GL_FRAMEBUFFER_COMPLETE:
return "GL_FRAMEBUFFER_COMPLETE";
string = "GL_FRAMEBUFFER_COMPLETE";
break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
string = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
string = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
return "GL_FRAMEBUFFER_UNSUPPORTED";
string = "GL_FRAMEBUFFER_UNSUPPORTED";
break;
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
case GL_FRAMEBUFFER_UNDEFINED:
return "GL_FRAMEBUFFER_UNDEFINED";
string = "GL_FRAMEBUFFER_UNDEFINED";
break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
string = "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
break;
#endif
default:
break;
}
return "unknown";
return string;
}
UTILS_NOINLINE
GLenum checkFramebufferStatus(io::ostream& out, GLenum target, const char* function, size_t line) noexcept {
GLenum const status = glCheckFramebufferStatus(target);
if (UTILS_VERY_UNLIKELY(status != GL_FRAMEBUFFER_COMPLETE)) {
auto const string = getFramebufferStatusString(status);
if (status != GL_FRAMEBUFFER_COMPLETE) {
const char* string = getFramebufferStatus(status);
out << "OpenGL framebuffer error " << io::hex << status << " (" << string << ") in \""
<< function << "\" at line " << io::dec << line << io::endl;
}
@@ -110,7 +118,7 @@ GLenum checkFramebufferStatus(io::ostream& out, GLenum target, const char* funct
UTILS_NOINLINE
void assertFramebufferStatus(io::ostream& out, GLenum target, const char* function, size_t line) noexcept {
GLenum const status = checkFramebufferStatus(out, target, function, line);
if (UTILS_VERY_UNLIKELY(status != GL_FRAMEBUFFER_COMPLETE)) {
if (status != GL_FRAMEBUFFER_COMPLETE) {
debug_trap();
}
}

View File

@@ -17,26 +17,29 @@
#ifndef TNT_FILAMENT_BACKEND_OPENGL_GLUTILS_H
#define TNT_FILAMENT_BACKEND_OPENGL_GLUTILS_H
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/Log.h>
#include <backend/DriverEnums.h>
#include <string_view>
#include <unordered_set>
#include <stddef.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "gl_headers.h"
namespace filament::backend::GLUtils {
namespace filament::backend {
namespace GLUtils {
std::string_view getGLErrorString(GLenum error) noexcept;
const char* getGLError(GLenum error) noexcept;
GLenum checkGLError(utils::io::ostream& out, const char* function, size_t line) noexcept;
void assertGLError(utils::io::ostream& out, const char* function, size_t line) noexcept;
std::string_view getFramebufferStatusString(GLenum err) noexcept;
const char* getFramebufferStatus(GLenum err) noexcept;
GLenum checkFramebufferStatus(utils::io::ostream& out, GLenum target, const char* function, size_t line) noexcept;
void assertFramebufferStatus(utils::io::ostream& out, GLenum target, const char* function, size_t line) noexcept;
@@ -50,7 +53,7 @@ void assertFramebufferStatus(utils::io::ostream& out, GLenum target, const char*
# define CHECK_GL_FRAMEBUFFER_STATUS(out, target) { GLUtils::checkFramebufferStatus(out, target, __func__, __LINE__); }
#endif
constexpr GLuint getComponentCount(ElementType const type) noexcept {
constexpr GLuint getComponentCount(ElementType type) noexcept {
using ElementType = ElementType;
switch (type) {
case ElementType::BYTE:
@@ -84,29 +87,27 @@ constexpr GLuint getComponentCount(ElementType const type) noexcept {
case ElementType::USHORT4:
return 4;
}
// should never happen
return 1;
}
// ------------------------------------------------------------------------------------------------
// Our enums to GLenum conversions
// ------------------------------------------------------------------------------------------------
constexpr GLbitfield getAttachmentBitfield(TargetBufferFlags const flags) noexcept {
constexpr GLbitfield getAttachmentBitfield(TargetBufferFlags flags) noexcept {
GLbitfield mask = 0;
if (any(flags & TargetBufferFlags::COLOR_ALL)) {
mask |= GLbitfield(GL_COLOR_BUFFER_BIT);
mask |= (GLbitfield)GL_COLOR_BUFFER_BIT;
}
if (any(flags & TargetBufferFlags::DEPTH)) {
mask |= GLbitfield(GL_DEPTH_BUFFER_BIT);
mask |= (GLbitfield)GL_DEPTH_BUFFER_BIT;
}
if (any(flags & TargetBufferFlags::STENCIL)) {
mask |= GLbitfield(GL_STENCIL_BUFFER_BIT);
mask |= (GLbitfield)GL_STENCIL_BUFFER_BIT;
}
return mask;
}
constexpr GLenum getBufferUsage(BufferUsage const usage) noexcept {
constexpr GLenum getBufferUsage(BufferUsage usage) noexcept {
switch (usage) {
case BufferUsage::STATIC:
return GL_STATIC_DRAW;
@@ -115,7 +116,7 @@ constexpr GLenum getBufferUsage(BufferUsage const usage) noexcept {
}
}
constexpr GLenum getBufferBindingType(BufferObjectBinding const bindingType) noexcept {
constexpr GLenum getBufferBindingType(BufferObjectBinding bindingType) noexcept {
switch (bindingType) {
case BufferObjectBinding::VERTEX:
return GL_ARRAY_BUFFER;
@@ -134,15 +135,13 @@ constexpr GLenum getBufferBindingType(BufferObjectBinding const bindingType) noe
return 0x90D2; // just to return something
#endif
}
// should never happen
return GL_ARRAY_BUFFER;
}
constexpr GLboolean getNormalization(bool const normalized) noexcept {
constexpr GLboolean getNormalization(bool normalized) noexcept {
return GLboolean(normalized ? GL_TRUE : GL_FALSE);
}
constexpr GLenum getComponentType(ElementType const type) noexcept {
constexpr GLenum getComponentType(ElementType type) noexcept {
using ElementType = ElementType;
switch (type) {
case ElementType::BYTE:
@@ -185,11 +184,9 @@ constexpr GLenum getComponentType(ElementType const type) noexcept {
return GL_HALF_FLOAT_OES;
#endif
}
// should never happen
return GL_INT;
}
constexpr GLenum getTextureTargetNotExternal(SamplerType const target) noexcept {
constexpr GLenum getTextureTargetNotExternal(SamplerType target) noexcept {
switch (target) {
case SamplerType::SAMPLER_2D:
return GL_TEXTURE_2D;
@@ -205,16 +202,14 @@ constexpr GLenum getTextureTargetNotExternal(SamplerType const target) noexcept
// we should never be here
return GL_TEXTURE_2D;
}
// should never happen
return GL_TEXTURE_2D;
}
constexpr GLenum getCubemapTarget(uint16_t const layer) noexcept {
constexpr GLenum getCubemapTarget(uint16_t layer) noexcept {
assert_invariant(layer <= 5);
return GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer;
}
constexpr GLenum getWrapMode(SamplerWrapMode const mode) noexcept {
constexpr GLenum getWrapMode(SamplerWrapMode mode) noexcept {
using SamplerWrapMode = SamplerWrapMode;
switch (mode) {
case SamplerWrapMode::REPEAT:
@@ -224,8 +219,6 @@ constexpr GLenum getWrapMode(SamplerWrapMode const mode) noexcept {
case SamplerWrapMode::MIRRORED_REPEAT:
return GL_MIRRORED_REPEAT;
}
// should never happen
return GL_CLAMP_TO_EDGE;
}
constexpr GLenum getTextureFilter(SamplerMinFilter filter) noexcept {
@@ -241,8 +234,6 @@ constexpr GLenum getTextureFilter(SamplerMinFilter filter) noexcept {
return GL_NEAREST_MIPMAP_NEAREST
- GLenum(SamplerMinFilter::NEAREST_MIPMAP_NEAREST) + GLenum(filter);
}
// should never happen
return GL_NEAREST;
}
constexpr GLenum getTextureFilter(SamplerMagFilter filter) noexcept {
@@ -250,7 +241,7 @@ constexpr GLenum getTextureFilter(SamplerMagFilter filter) noexcept {
}
constexpr GLenum getBlendEquationMode(BlendEquation const mode) noexcept {
constexpr GLenum getBlendEquationMode(BlendEquation mode) noexcept {
using BlendEquation = BlendEquation;
switch (mode) {
case BlendEquation::ADD: return GL_FUNC_ADD;
@@ -259,11 +250,9 @@ constexpr GLenum getBlendEquationMode(BlendEquation const mode) noexcept {
case BlendEquation::MIN: return GL_MIN;
case BlendEquation::MAX: return GL_MAX;
}
// should never happen
return GL_FUNC_ADD;
}
constexpr GLenum getBlendFunctionMode(BlendFunction const mode) noexcept {
constexpr GLenum getBlendFunctionMode(BlendFunction mode) noexcept {
using BlendFunction = BlendFunction;
switch (mode) {
case BlendFunction::ZERO: return GL_ZERO;
@@ -278,11 +267,9 @@ constexpr GLenum getBlendFunctionMode(BlendFunction const mode) noexcept {
case BlendFunction::ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA;
case BlendFunction::SRC_ALPHA_SATURATE: return GL_SRC_ALPHA_SATURATE;
}
// should never happen
return GL_ONE;
}
constexpr GLenum getCompareFunc(SamplerCompareFunc const func) noexcept {
constexpr GLenum getCompareFunc(SamplerCompareFunc func) noexcept {
switch (func) {
case SamplerCompareFunc::LE: return GL_LEQUAL;
case SamplerCompareFunc::GE: return GL_GEQUAL;
@@ -293,30 +280,28 @@ constexpr GLenum getCompareFunc(SamplerCompareFunc const func) noexcept {
case SamplerCompareFunc::A: return GL_ALWAYS;
case SamplerCompareFunc::N: return GL_NEVER;
}
// should never happen
return GL_LEQUAL;
}
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
constexpr GLenum getTextureCompareMode(SamplerCompareMode const mode) noexcept {
constexpr GLenum getTextureCompareMode(SamplerCompareMode mode) noexcept {
return mode == SamplerCompareMode::NONE ?
GL_NONE : GL_COMPARE_REF_TO_TEXTURE;
}
constexpr GLenum getTextureCompareFunc(SamplerCompareFunc const func) noexcept {
constexpr GLenum getTextureCompareFunc(SamplerCompareFunc func) noexcept {
return getCompareFunc(func);
}
#endif
constexpr GLenum getDepthFunc(SamplerCompareFunc const func) noexcept {
constexpr GLenum getDepthFunc(SamplerCompareFunc func) noexcept {
return getCompareFunc(func);
}
constexpr GLenum getStencilFunc(SamplerCompareFunc const func) noexcept {
constexpr GLenum getStencilFunc(SamplerCompareFunc func) noexcept {
return getCompareFunc(func);
}
constexpr GLenum getStencilOp(StencilOperation const op) noexcept {
constexpr GLenum getStencilOp(StencilOperation op) noexcept {
switch (op) {
case StencilOperation::KEEP: return GL_KEEP;
case StencilOperation::ZERO: return GL_ZERO;
@@ -327,11 +312,9 @@ constexpr GLenum getStencilOp(StencilOperation const op) noexcept {
case StencilOperation::DECR_WRAP: return GL_DECR_WRAP;
case StencilOperation::INVERT: return GL_INVERT;
}
// should never happen
return GL_KEEP;
}
constexpr GLenum getFormat(PixelDataFormat const format) noexcept {
constexpr GLenum getFormat(PixelDataFormat format) noexcept {
using PixelDataFormat = PixelDataFormat;
switch (format) {
case PixelDataFormat::RGB: return GL_RGB;
@@ -353,11 +336,9 @@ constexpr GLenum getFormat(PixelDataFormat const format) noexcept {
default: return GL_NONE;
#endif
}
// should never happen
return GL_RGBA;
}
constexpr GLenum getType(PixelDataType const type) noexcept {
constexpr GLenum getType(PixelDataType type) noexcept {
using PixelDataType = PixelDataType;
switch (type) {
case PixelDataType::UBYTE: return GL_UNSIGNED_BYTE;
@@ -379,12 +360,10 @@ constexpr GLenum getType(PixelDataType const type) noexcept {
default: return GL_NONE;
#endif
}
// should never happen
return GL_UNSIGNED_INT;
}
#if !defined(__EMSCRIPTEN__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
constexpr GLenum getSwizzleChannel(TextureSwizzle const c) noexcept {
constexpr GLenum getSwizzleChannel(TextureSwizzle c) noexcept {
using TextureSwizzle = TextureSwizzle;
switch (c) {
case TextureSwizzle::SUBSTITUTE_ZERO: return GL_ZERO;
@@ -394,12 +373,10 @@ constexpr GLenum getSwizzleChannel(TextureSwizzle const c) noexcept {
case TextureSwizzle::CHANNEL_2: return GL_BLUE;
case TextureSwizzle::CHANNEL_3: return GL_ALPHA;
}
// should never happen
return GL_RED;
}
#endif
constexpr GLenum getCullingMode(CullingMode const mode) noexcept {
constexpr GLenum getCullingMode(CullingMode mode) noexcept {
switch (mode) {
case CullingMode::NONE:
// should never happen
@@ -411,13 +388,11 @@ constexpr GLenum getCullingMode(CullingMode const mode) noexcept {
case CullingMode::FRONT_AND_BACK:
return GL_FRONT_AND_BACK;
}
// should never happen
return GL_FRONT_AND_BACK;
}
// ES2 supported internal formats for texturing and how they map to a format/type
constexpr std::pair<GLenum, GLenum> textureFormatToFormatAndType(
TextureFormat const format) noexcept {
TextureFormat format) noexcept {
switch (format) {
case TextureFormat::R8: return { 0x1909 /*GL_LUMINANCE*/, GL_UNSIGNED_BYTE };
case TextureFormat::RGB8: return { GL_RGB, GL_UNSIGNED_BYTE };
@@ -438,7 +413,7 @@ constexpr std::pair<GLenum, GLenum> textureFormatToFormatAndType(
// clang loses it on this one, and generates a huge jump table when
// inlined. So we don't mark it as inline (only constexpr) which solves the problem,
// strangely, when not inlined, clang simply generates an array lookup.
constexpr /* inline */ GLenum getInternalFormat(TextureFormat const format) noexcept {
constexpr /* inline */ GLenum getInternalFormat(TextureFormat format) noexcept {
switch (format) {
/* Formats supported by our ES2 implementations */
@@ -686,7 +661,7 @@ public:
unordered_string_set split(const char* extensions) noexcept;
} // namespace filament::backend::GLUtils
} // namespace GLUtils
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_OPENGL_GLUTILS_H

View File

@@ -2098,7 +2098,6 @@ void OpenGLDriver::setAcquiredImage(Handle<HwStream> sh, void* hwbuffer, const m
glstream->user_thread.pending = mPlatform.transformAcquiredImage({
hwbuffer, cb, userData, handler });
glstream->user_thread.transform = transform;
if (glstream->user_thread.pending.image != nullptr) {
// If there's no pending image, do nothing. Note that GL_OES_EGL_image does not let you pass

View File

@@ -17,10 +17,8 @@
#define COREVIDEO_SILENCE_GL_DEPRECATION
#include "CocoaExternalImage.h"
#include "../GLUtils.h"
#include <utils/Panic.h>
#include <utils/Log.h>
#include "../GLUtils.h"
namespace filament::backend {

View File

@@ -28,7 +28,6 @@
#include <utils/compiler.h>
#include <utils/Panic.h>
#include <utils/debug.h>
#include <utils/Log.h>
namespace filament::backend {

View File

@@ -295,8 +295,6 @@ VulkanCommandBuffer& CommandBufferPool::getRecording() {
}
void CommandBufferPool::gc() {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("CommandBufferPool::gc");
ActiveBuffers reclaimed;
mSubmitted.forEachSetBit([this,&reclaimed] (size_t index) {
auto& buffer = mBuffers[index];
@@ -306,7 +304,6 @@ void CommandBufferPool::gc() {
}
});
mSubmitted &= ~reclaimed;
FVK_SYSTRACE_END();
}
void CommandBufferPool::update() {
@@ -336,9 +333,7 @@ void CommandBufferPool::wait() {
mSubmitted.forEachSetBit([this, &count, &fences] (size_t index) {
fences[count++] = mBuffers[index]->getVkFence();
});
if (count) {
vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX);
}
vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX);
update();
}

View File

@@ -31,8 +31,8 @@ namespace filament::backend {
namespace {
using DescriptorCount = VulkanDescriptorSetLayout::Count;
using DescriptorSetLayoutArray = VulkanDescriptorSetCache::DescriptorSetLayoutArray;
using DescriptorCount = VulkanDescriptorSetCache::DescriptorCount;
// We create a pool for each layout as defined by the number of descriptors of each type. For
// example, a layout of
@@ -203,10 +203,11 @@ public:
DescriptorInfinitePool(VkDevice device)
: mDevice(device) {}
VkDescriptorSet obtainSet(DescriptorCount const& count, VkDescriptorSetLayout vklayout) {
VkDescriptorSet obtainSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto const vklayout = layout->getVkLayout();
DescriptorPool* sameTypePool = nullptr;
for (auto& pool: mPools) {
if (!pool->canAllocate(count)) {
if (!pool->canAllocate(layout->count)) {
continue;
}
if (auto set = pool->obtainSet(vklayout); set != VK_NULL_HANDLE) {
@@ -224,7 +225,8 @@ public:
}
// We need to increase the set of pools by one.
mPools.push_back(std::make_unique<DescriptorPool>(mDevice, count, capacity));
mPools.push_back(std::make_unique<DescriptorPool>(mDevice,
DescriptorCount::fromLayoutBitmask(layout->bitmask), capacity));
auto& pool = mPools.back();
auto ret = pool->obtainSet(vklayout);
assert_invariant(ret != VK_NULL_HANDLE && "failed to obtain a set?");
@@ -274,36 +276,39 @@ void VulkanDescriptorSetCache::unbind(uint8_t setIndex) {
}
void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
VkPipelineLayout pipelineLayout, fvkutils::DescriptorSetMask const& useExternalSamplers,
fvkutils::DescriptorSetMask const& setMask) {
VkPipelineLayout pipelineLayout, fvkutils::DescriptorSetMask const& setMask) {
// setMask indicates the set of descriptor sets the driver wants to bind, curMask is the
// actual set of sets that *needs* to be bound.
fvkutils::DescriptorSetMask curMask = setMask;
auto const& updateSets = mStashedSets;
curMask.forEachSetBit([&](size_t index) {
if (!updateSets[index]) {
curMask.unset(index);
}
});
auto& updateSets = mStashedSets;
bool const pipelineLayoutIsSame = mLastBoundInfo.pipelineLayout == pipelineLayout;
if (mLastBoundInfo.pipelineLayout == pipelineLayout) {
if (pipelineLayoutIsSame) {
auto& lastBoundSets = mLastBoundInfo.boundSets;
curMask.forEachSetBit([&](size_t index) {
if (updateSets[index] == lastBoundSets[index] && !useExternalSamplers[index]) {
setMask.forEachSetBit([&](size_t index) {
if (!updateSets[index] || updateSets[index] == lastBoundSets[index]) {
curMask.unset(index);
}
});
if (curMask.none() &&
mLastBoundInfo.setMask == setMask && mLastBoundInfo.boundSets == updateSets) {
return;
}
} else {
setMask.forEachSetBit([&](size_t index) {
if (!updateSets[index]) {
curMask.unset(index);
}
});
}
curMask.forEachSetBit([&](size_t index) {
curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) {
// This code actually binds the descriptor sets.
auto set = updateSets[index];
VkCommandBuffer const cmdbuffer = commands->buffer();
VkDescriptorSet vkset = useExternalSamplers[index] ? set->getExternalSamplerVkSet() :
set->getVkSet();
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
1, &vkset, set->uniqueDynamicUboCount, set->getOffsets()->data());
1, &set->getVkSet(), set->uniqueDynamicUboCount, set->getOffsets()->data());
commands->acquire(set);
});
@@ -329,7 +334,7 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
if (set->dynamicUboMask.test(binding)) {
type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
}
VkWriteDescriptorSet descriptorWrite = {
VkWriteDescriptorSet const descriptorWrite = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = set->getVkSet(),
.dstBinding = binding,
@@ -338,17 +343,12 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
.pBufferInfo = &info,
};
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
if (auto externalSamplerSet = set->getExternalSamplerVkSet();
externalSamplerSet != VK_NULL_HANDLE) {
descriptorWrite.dstSet = externalSamplerSet;
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
}
set->acquire(bufferObject);
}
void VulkanDescriptorSetCache::updateSamplerImpl(VkDescriptorSet vkset, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept {
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
VkSampler sampler) noexcept {
VkImageSubresourceRange range = texture->getPrimaryViewRange();
VkImageViewType const expectedType = texture->getViewType();
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
@@ -364,29 +364,16 @@ void VulkanDescriptorSetCache::updateSamplerImpl(VkDescriptorSet vkset, uint8_t
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()),
};
VkWriteDescriptorSet descriptorWrite = {
VkWriteDescriptorSet const descriptorWrite = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = nullptr,
.dstSet = vkset,
.dstSet = set->getVkSet(),
.dstBinding = binding,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &info,
};
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
}
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
VkSampler sampler) noexcept {
updateSamplerImpl(set->getVkSet(), binding, texture, sampler);
set->acquire(texture);
}
void VulkanDescriptorSetCache::updateSamplerForExternalSamplerSet(
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture) noexcept {
updateSamplerImpl(set->getExternalSamplerVkSet(), binding, texture, VK_NULL_HANDLE);
set->acquire(texture);
}
@@ -396,32 +383,32 @@ void VulkanDescriptorSetCache::updateInputAttachment(
// TOOD: fill this in.
}
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet(
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto const vkSet = mDescriptorPool->obtainSet(layout->count, layout->getVkLayout());
auto const vkSet = getVkSet(layout);
auto const& count = layout->count;
auto const vklayout = layout->getVkLayout();
auto set = fvkmemory::resource_ptr<VulkanDescriptorSet>::make(
mResourceManager, handle, layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
[vkSet, count, vklayout, this](
VulkanDescriptorSet*) { this->manualRecycle(count, vklayout, vkSet); },
vkSet);
auto set = fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle,
layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
[vkSet, count, vklayout, this](VulkanDescriptorSet*) {
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
// down).
if (mDescriptorPool) {
mDescriptorPool->recycle(count, vklayout, vkSet);
}
});
set->setVkSet(vkSet);
return set;
}
VkDescriptorSet VulkanDescriptorSetCache::getVkSet(DescriptorCount const& count,
VkDescriptorSetLayout vklayout) {
return mDescriptorPool->obtainSet(count, vklayout);
VkDescriptorSet VulkanDescriptorSetCache::getVkSet(
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
return mDescriptorPool->obtainSet(layout);
}
void VulkanDescriptorSetCache::manualRecycle(VulkanDescriptorSetLayout::Count const& count,
void VulkanDescriptorSetCache::manualRecyle(VulkanDescriptorSetLayout::Count const& count,
VkDescriptorSetLayout vklayout, VkDescriptorSet vkSet) {
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
// down).
if (mDescriptorPool) {
mDescriptorPool->recycle(count, vklayout, vkSet);
}
mDescriptorPool->recycle(count, vklayout, vkSet);
}
void VulkanDescriptorSetCache::gc() { mStashedSets = {}; }

View File

@@ -43,7 +43,6 @@ public:
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
using DescriptorSetArray =
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
using DescriptorCount = VulkanDescriptorSetLayout::Count;
VulkanDescriptorSetCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetCache();
@@ -57,10 +56,6 @@ public:
void updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
void updateSamplerForExternalSamplerSet(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture) noexcept;
void updateInputAttachment(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
VulkanAttachment const& attachment) noexcept;
@@ -70,17 +65,17 @@ public:
void unbind(uint8_t setIndex);
void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout,
fvkutils::DescriptorSetMask const& useExternalSamplerMask,
fvkutils::DescriptorSetMask const& setMask);
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
// This method is meant to be used with external samplers
VkDescriptorSet getVkSet(DescriptorCount const& count, VkDescriptorSetLayout vklayout);
// This method is only meant to be used with external samplers (or internally within this
// class).
VkDescriptorSet getVkSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
// This method is meant to be used with external samplers
void manualRecycle(VulkanDescriptorSetLayout::Count const& count, VkDescriptorSetLayout vklayout,
// This method is only meant to be used with external samplers.
void manualRecyle(VulkanDescriptorSetLayout::Count const& count, VkDescriptorSetLayout vklayout,
VkDescriptorSet vkSet);
DescriptorSetArray const& getBoundSets() const { return mStashedSets; }
@@ -88,9 +83,6 @@ public:
void gc();
private:
void updateSamplerImpl(VkDescriptorSet set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
class DescriptorInfinitePool;
VkDevice mDevice;

View File

@@ -127,7 +127,6 @@ void VulkanDescriptorSetLayoutCache::terminate() noexcept {
VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
VulkanDescriptorSetLayout::Bitmask const& bitmasks,
fvkutils::SamplerBitmask externalSamplers,
utils::FixedCapacityVector<VkSampler> immutableSamplers) {
LayoutKey key = {
.bitmask = bitmasks,
@@ -142,7 +141,7 @@ VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
bitmasks.dynamicUbo);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmasks.ubo);
count += appendSamplerBindings(&toBind[count], bitmasks.sampler, externalSamplers,
count += appendSamplerBindings(&toBind[count], bitmasks.sampler, bitmasks.externalSampler,
immutableSamplers);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
bitmasks.inputAttachment);
@@ -161,9 +160,9 @@ VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> VulkanDescriptorSetLayoutCache::createLayout(
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info) {
BitmaskGroup maskGroup = VulkanDescriptorSetLayout::Bitmask::fromLayoutDescription(info);
auto layout = fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::make(mResourceManager, handle,
std::move(info), getVkLayout(maskGroup, maskGroup.externalSampler));
info);
layout->setVkLayout(getVkLayout(layout->bitmask));
return layout;
}

View File

@@ -46,7 +46,6 @@ public:
// This method is meant to be used with external samplers
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks,
fvkutils::SamplerBitmask externalSamplers,
utils::FixedCapacityVector<VkSampler> immutableSamplers = {});
private:

View File

@@ -30,6 +30,7 @@
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Conversion.h"
#include "vulkan/utils/Definitions.h"
#include "vulkan/vulkan_core.h"
#include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatform.h>
@@ -197,9 +198,11 @@ Dispatcher VulkanDriver::getDispatcher() const noexcept {
}
VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig)
Platform::DriverConfig const& driverConfig) noexcept
: mPlatform(platform),
mResourceManager(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck,
mResourceManager(
driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck,
driverConfig.disableHeapHandleTags),
mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(),
mPlatform->getDevice())),
@@ -218,8 +221,6 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
mQueryManager(mPlatform->getDevice()),
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
&mDescriptorSetLayoutCache),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
mStereoscopicType(driverConfig.stereoscopicType) {
@@ -250,7 +251,7 @@ VulkanDriver::~VulkanDriver() noexcept = default;
UTILS_NOINLINE
Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig) {
Platform::DriverConfig const& driverConfig) noexcept {
#if 0
// this is useful for development, but too verbose even for debug builds
// For reference on a 64-bits machine in Release mode:
@@ -312,7 +313,7 @@ void VulkanDriver::terminate() {
mCurrentSwapChain = {};
mDefaultRenderTarget = {};
mPipelineState = {};
mBoundPipeline = {};
mQueryManager.terminate();
@@ -324,10 +325,6 @@ void VulkanDriver::terminate() {
mCommands.terminate();
// Must come before samplerCache, ycbcrConversionCache, descriptorSetCache,
// descriptorSetLayoutCache
mExternalImageManager.terminate();
mStagePool.terminate();
mPipelineCache.terminate();
mFramebufferCache.terminate();
@@ -384,10 +381,6 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
int64_t refreshIntervalNs, uint32_t frameId) {
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
// Do nothing.
if (mAppState.hasExternalSamplers()) {
mExternalImageManager.onBeginFrame();
}
}
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
@@ -428,17 +421,12 @@ void VulkanDriver::updateDescriptorSetTexture(
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
if (mExternalImageManager.isExternallySampledTexture(texture)) {
mExternalImageManager.bindExternallySampledTexture(set, binding, texture, params);
mAppState.hasBoundExternalImages = true;
} else {
VulkanSamplerCache::Params cacheParams = {
.sampler = params,
};
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
mExternalImageManager.clearTextureBinding(set, binding);
}
// TODO: YcbcrConversion?
VulkanSamplerCache::Params cacheParams = {
.sampler = params,
};
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
}
void VulkanDriver::flush(int) {
@@ -458,7 +446,6 @@ void VulkanDriver::finish(int dummy) {
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
FVK_SYSTRACE_SCOPE();
auto vb = resource_ptr<VulkanVertexBuffer>::cast(&mResourceManager, vbh);
auto ib = resource_ptr<VulkanIndexBuffer>::cast(&mResourceManager, ibh);
auto ptr = resource_ptr<VulkanRenderPrimitive>::make(&mResourceManager, rph, pt, vb, ib);
@@ -469,14 +456,12 @@ void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
if (!rph) {
return;
}
FVK_SYSTRACE_SCOPE();
auto ptr = resource_ptr<VulkanRenderPrimitive>::cast(&mResourceManager, rph);
ptr.dec();
}
void VulkanDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {
FVK_SYSTRACE_SCOPE();
auto vbi = resource_ptr<VulkanVertexBufferInfo>::make(&mResourceManager, vbih, bufferCount,
attributeCount, attributes);
vbi.inc();
@@ -486,14 +471,12 @@ void VulkanDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
if (!vbih) {
return;
}
FVK_SYSTRACE_SCOPE();
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager, vbih);
vbi.dec();
}
void VulkanDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih) {
FVK_SYSTRACE_SCOPE();
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager, vbih);
auto vb = resource_ptr<VulkanVertexBuffer>::make(&mResourceManager, vbh, mContext, mStagePool,
vertexCount, vbi);
@@ -504,14 +487,12 @@ void VulkanDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
if (!vbh) {
return;
}
FVK_SYSTRACE_SCOPE();
auto vb = resource_ptr<VulkanVertexBuffer>::cast(&mResourceManager, vbh);
vb.dec();
}
void VulkanDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {
FVK_SYSTRACE_SCOPE();
auto elementSize = (uint8_t) getElementTypeSize(elementType);
auto ib = resource_ptr<VulkanIndexBuffer>::make(&mResourceManager, ibh, mAllocator, mStagePool,
elementSize, indexCount);
@@ -522,14 +503,12 @@ void VulkanDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
if (!ibh) {
return;
}
FVK_SYSTRACE_SCOPE();
auto ib = resource_ptr<VulkanIndexBuffer>::cast(&mResourceManager, ibh);
ib.dec();
}
void VulkanDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
FVK_SYSTRACE_SCOPE();
auto bo = resource_ptr<VulkanBufferObject>::make(&mResourceManager, boh, mAllocator, mStagePool,
byteCount, bindingType);
bo.inc();
@@ -539,7 +518,6 @@ void VulkanDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
if (!boh) {
return;
}
FVK_SYSTRACE_SCOPE();
auto bo = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
bo.dec();
}
@@ -585,48 +563,41 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage) {
FVK_SYSTRACE_SCOPE();
auto metadata = mPlatform->extractExternalImageMetadata(externalImage);
auto const& metadata = mPlatform->getExternalImageMetadata(externalImage);
if (metadata.isProtected) {
usage |= backend::TextureUsage::PROTECTED;
}
VkImageUsageFlags vkUsage = metadata.usage;
if (any(usage & TextureUsage::BLIT_SRC)) {
vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
assert_invariant(width == metadata.width);
assert_invariant(height == metadata.height);
assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
// We do not check the format since AHB could return both a known format and an external format.
// In which case, we choose one or the other, but this choice is not known to the client.
// Therefore the following lines are commented out.
// assert_invariant(format == metadata.filamentFormat);
// assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
VkMemoryPropertyFlags const requiredMemoryFlags = any(usage & TextureUsage::UPLOADABLE)
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
uint32_t const memoryTypeIndex =
mContext.selectMemoryType(metadata.memoryTypeBits, requiredMemoryFlags);
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex != VK_MAX_MEMORY_TYPES)
<< "failed to find a valid memory type for external image memory.";
auto imgData = mPlatform->createVkImageFromExternal(externalImage);
VkImage vkimg;
VkDeviceMemory deviceMemory;
std::tie(vkimg, deviceMemory) =
mPlatform->createExternalImageData(externalImage, metadata, memoryTypeIndex, vkUsage);
assert_invariant(imgData.internal.valid() || imgData.external.valid());
VkFormat vkformat = metadata.format;
VkImage vkimage = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
if (imgData.internal.valid()) {
metadata.externalFormat = 0;
vkimage = imgData.internal.image;
memory = imgData.internal.memory;
} else { // imgData.external.valid()
vkformat = VK_FORMAT_UNDEFINED;
vkimage = imgData.external.image;
memory = imgData.external.memory;
}
VkSamplerYcbcrConversion const conversion =
mExternalImageManager.getVkSamplerYcbcrConversion(metadata);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mContext,
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, vkimage, memory,
vkformat, conversion, metadata.samples, metadata.width, metadata.height,
metadata.layers, usage, mStagePool);
auto& commands = mCommands.get();
// Unlike uploaded textures or swapchains, we need to explicit transition this
// texture into the read layout.
texture->transitionLayout(&commands, texture->getPrimaryViewRange(), VulkanLayout::READ_ONLY);
if (imgData.external.valid()) {
mExternalImageManager.addExternallySampledTexture(texture, externalImage);
}
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, vkimg, deviceMemory,
metadata.format, VK_NULL_HANDLE, metadata.samples, metadata.width, metadata.height,
metadata.layerCount, usage, mStagePool);
texture.inc();
}
@@ -659,8 +630,6 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
}
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
texture.dec();
mExternalImageManager.removeExternallySampledTexture(texture);
}
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
@@ -777,7 +746,6 @@ void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
}
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
FVK_SYSTRACE_SCOPE();
// Running gc() to guard against an edge case where the old swapchains need to have been
// destroyed before the new swapchain can be created. Otherwise, we would fail
// vkCreateSwapchainKHR with VK_ERROR_NATIVE_WINDOW_IN_USE_KHR.
@@ -819,7 +787,6 @@ void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
backend::DescriptorSetLayout&& info) {
FVK_SYSTRACE_SCOPE();
auto layout = mDescriptorSetLayoutCache.createLayout(dslh, std::move(info));
layout.inc();
}
@@ -831,10 +798,6 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
auto set = mDescriptorSetCache.createSet(dsh, layout);
set.inc();
if (layout->hasExternalSamplers()) {
mAppState.hasExternalSamplerLayouts = true;
}
}
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
@@ -959,10 +922,6 @@ void VulkanDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> dslh
void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
set.dec();
if (mAppState.hasExternalSamplers() && set->getExternalSamplerVkSet() != VK_NULL_HANDLE) {
mExternalImageManager.removeDescriptorSet(set);
}
}
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream) {
@@ -1523,6 +1482,7 @@ void VulkanDriver::endRenderPass(int) {
// pipeline barrier between framebuffer writes and shader reads.
rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer);
mRenderPassFboInfo = {};
mCurrentRenderPass.renderTarget = {};
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
@@ -1582,10 +1542,10 @@ void VulkanDriver::commit(Handle<HwSwapChain> sch) {
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
assert_invariant(mPipelineState.program && "Expect a program when writing to push constants");
assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants");
assert_invariant(mCurrentRenderPass.commandBuffer && "Should be called within a renderpass");
mPipelineState.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(),
mPipelineState.pipelineLayout, stage, index, value);
mBoundPipeline.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(),
mBoundPipeline.pipelineLayout, stage, index, value);
}
void VulkanDriver::insertEventMarker(char const* string) {
@@ -1764,69 +1724,16 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
}
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
// This resets all of the pipeline states; the most relevant (needing reset) is .bindInDraw.
mPipelineState = {};
auto& setLayouts = pipelineState.pipelineLayout.setLayout;
DescriptorSetLayoutHandleList layoutHandles;
uint8_t layoutCount = 0;
std::transform(setLayouts.begin(), setLayouts.end(), layoutHandles.begin(),
[&](auto const& handle) -> resource_ptr<VulkanDescriptorSetLayout> {
if (!handle) {
return {};
}
layoutCount++;
return resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, handle);
});
constexpr uint8_t descriptorSetMaskTable[4] = {0x1, 0x3, 0x7, 0xF};
fvkutils::DescriptorSetMask const descriptorSetMask =
fvkutils::DescriptorSetMask(descriptorSetMaskTable[layoutCount]);
if (mAppState.hasExternalSamplers()) {
auto const haveExternalSamplers = [&](auto layoutHandle) {
if (!layoutHandle) {
return false;
}
return layoutHandle->hasExternalSamplers();
};
if (std::any_of(layoutHandles.begin(), layoutHandles.end(), haveExternalSamplers)) {
BindInDrawBundle bundle = {
.pipelineState = pipelineState,
.dsLayoutHandles = layoutHandles,
.descriptorSetMask = descriptorSetMask,
};
mPipelineState.bindInDraw = { true, bundle };
return;
}
}
// The normal, non-external sampler path
using VkDescriptorSetLayoutArray = VulkanPipelineLayoutCache::DescriptorSetLayoutArray;
VkDescriptorSetLayoutArray vkLayouts;
std::transform(layoutHandles.begin(), layoutHandles.end(), vkLayouts.begin(),
[](auto const& layout) -> VkDescriptorSetLayout {
if (!layout) {
return VK_NULL_HANDLE;
}
return layout->getVkLayout();
});
auto program = resource_ptr<VulkanProgram>::cast(&mResourceManager, pipelineState.program);
auto pipelineLayout = mPipelineLayoutCache.getLayout(vkLayouts, program);
bindPipelineImpl(pipelineState, pipelineLayout, descriptorSetMask);
}
void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState,
VkPipelineLayout pipelineLayout, fvkutils::DescriptorSetMask descriptorSetMask) {
FVK_SYSTRACE_SCOPE();
auto commands = mCurrentRenderPass.commandBuffer;
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager,
pipelineState.vertexBufferInfo);
Handle<HwProgram> programHandle = pipelineState.program;
RasterState const& rasterState = pipelineState.rasterState;
PolygonOffset const& depthOffset = pipelineState.polygonOffset;
auto program = resource_ptr<VulkanProgram>::cast(&mResourceManager, pipelineState.program);
auto program = resource_ptr<VulkanProgram>::cast(&mResourceManager, programHandle);
commands->acquire(program);
// Update the VK raster state.
@@ -1868,11 +1775,28 @@ void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState,
mPipelineCache.bindPrimitiveTopology(topology);
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, vbi->getAttributeCount());
// Note that we cannot reinit mPipeline because the .bindInDraw metadata that needs to carry
// over even on bind.
mPipelineState.program = program;
mPipelineState.pipelineLayout = pipelineLayout;
mPipelineState.descriptorSetMask = descriptorSetMask;
auto& setLayouts = pipelineState.pipelineLayout.setLayout;
VulkanDescriptorSetLayout::DescriptorSetLayoutArray layoutList;
uint8_t layoutCount = 0;
std::transform(setLayouts.begin(), setLayouts.end(), layoutList.begin(),
[&](Handle<HwDescriptorSetLayout> handle) -> VkDescriptorSetLayout {
if (!handle) {
return VK_NULL_HANDLE;
}
auto layout =
resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, handle);
layoutCount++;
return layout->getVkLayout();
});
auto pipelineLayout = mPipelineLayoutCache.getLayout(layoutList, program);
constexpr uint8_t descriptorSetMaskTable[4] = {0x1, 0x3, 0x7, 0xF};
mBoundPipeline = {
.program = program,
.pipelineLayout = pipelineLayout,
.descriptorSetMask = fvkutils::DescriptorSetMask(descriptorSetMaskTable[layoutCount]),
};
mPipelineCache.bindLayout(pipelineLayout);
mPipelineCache.bindPipeline(mCurrentRenderPass.commandBuffer);
@@ -1917,41 +1841,15 @@ void VulkanDriver::bindDescriptorSet(
void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
FVK_SYSTRACE_SCOPE();
VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer();
auto const& [doBindInDraw, bundle] = mPipelineState.bindInDraw;
fvkutils::DescriptorSetMask setsWithExternalSamplers = {};
if (doBindInDraw) {
auto& layoutHandles = bundle.dsLayoutHandles;
setsWithExternalSamplers = mExternalImageManager.prepareBindSets(layoutHandles,
mDescriptorSetCache.getBoundSets());
VulkanDescriptorSetLayout::DescriptorSetLayoutArray vklayouts;
for (size_t i = 0; i < layoutHandles.size(); i++) {
if (!layoutHandles[i]) {
vklayouts[i] = VK_NULL_HANDLE;
continue;
}
if (setsWithExternalSamplers[i]) {
vklayouts[i] = layoutHandles[i]->getExternalSamplerVkLayout();
} else {
vklayouts[i] = layoutHandles[i]->getVkLayout();
}
}
auto program =
resource_ptr<VulkanProgram>::cast(&mResourceManager, bundle.pipelineState.program);
VkPipelineLayout const pipelineLayout = mPipelineLayoutCache.getLayout(vklayouts, program);
if (pipelineLayout != mPipelineState.pipelineLayout) {
bindPipelineImpl(bundle.pipelineState, pipelineLayout, bundle.descriptorSetMask);
}
mPipelineState.bindInDraw.first = false;
}
mDescriptorSetCache.commit(mCurrentRenderPass.commandBuffer, mPipelineState.pipelineLayout,
setsWithExternalSamplers, mPipelineState.descriptorSetMask);
mDescriptorSetCache.commit(mCurrentRenderPass.commandBuffer,
mBoundPipeline.pipelineLayout,
mBoundPipeline.descriptorSetMask);
// Finally, make the actual draw call. TODO: support subranges
uint32_t const firstIndex = indexOffset;
constexpr int32_t vertexOffset = 0;
constexpr uint32_t firstInstId = 0;
const uint32_t firstIndex = indexOffset;
const int32_t vertexOffset = 0;
const uint32_t firstInstId = 0;
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
}

View File

@@ -30,7 +30,6 @@
#include "VulkanYcbcrConversionCache.h"
#include "vulkan/VulkanDescriptorSetCache.h"
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
#include "vulkan/VulkanExternalImageManager.h"
#include "vulkan/VulkanPipelineLayoutCache.h"
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/memory/ResourcePointer.h"
@@ -55,7 +54,7 @@ constexpr uint8_t MAX_RENDERTARGET_ATTACHMENT_TEXTURES =
class VulkanDriver final : public DriverBase {
public:
static Driver* create(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig);
Platform::DriverConfig const& driverConfig) noexcept;
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
// Encapsulates the VK_EXT_debug_utils extension. In particular, we use
@@ -90,8 +89,8 @@ private:
void debugCommandBegin(CommandStream* cmds, bool synchronous,
const char* methodName) noexcept override;
VulkanDriver(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig);
inline VulkanDriver(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig) noexcept;
~VulkanDriver() noexcept override;
@@ -120,8 +119,6 @@ private:
private:
void collectGarbage();
void bindPipelineImpl(PipelineState const& pipelineState, VkPipelineLayout pipelineLayout,
fvkutils::DescriptorSetMask descriptorSetMask);
VulkanPlatform* mPlatform = nullptr;
fvkmemory::ResourceManager mResourceManager;
@@ -146,39 +143,21 @@ private:
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
VulkanDescriptorSetCache mDescriptorSetCache;
VulkanQueryManager mQueryManager;
VulkanExternalImageManager mExternalImageManager;
// This is necessary for us to write to push constants after binding a pipeline.
using DescriptorSetLayoutHandleList = std::array<resource_ptr<VulkanDescriptorSetLayout>,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
struct BindInDrawBundle {
PipelineState pipelineState = {};
DescriptorSetLayoutHandleList dsLayoutHandles = {};
fvkutils::DescriptorSetMask descriptorSetMask = {};
resource_ptr<VulkanProgram> program = {};
};
struct {
// For push constant
resource_ptr<VulkanProgram> program = {};
// For push commiting dynamic ubos in draw()
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
fvkutils::DescriptorSetMask descriptorSetMask = {};
std::pair<bool, BindInDrawBundle> bindInDraw = {false, {}};
} mPipelineState = {};
resource_ptr<VulkanProgram> program;
VkPipelineLayout pipelineLayout;
fvkutils::DescriptorSetMask descriptorSetMask;
} mBoundPipeline = {};
// We need to store information about a render pass to enable better barriers at the end of a
// renderpass.
struct {
// This tracks whether the app has seen external samplers bound to a the descriptor set.
// This will force bindPipeline to take a slow path.
bool hasExternalSamplerLayouts = false;
bool hasBoundExternalImages = false;
bool hasExternalSamplers() const noexcept {
return hasExternalSamplerLayouts && hasBoundExternalImages;
}
} mAppState;
using AttachmentArray =
fvkutils::StaticVector<VulkanAttachment, MAX_RENDERTARGET_ATTACHMENT_TEXTURES>;
AttachmentArray attachments;
} mRenderPassFboInfo = {};
bool const mIsSRGBSwapChainSupported;
backend::StereoscopicType const mStereoscopicType;

View File

@@ -1,294 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VulkanExternalImageManager.h"
#include "VulkanDescriptorSetCache.h"
#include "VulkanDescriptorSetLayoutCache.h"
#include "VulkanSamplerCache.h"
#include "VulkanYcbcrConversionCache.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Conversion.h"
#include <backend/platforms/VulkanPlatform.h>
#include <algorithm>
namespace filament::backend {
namespace {
using Bitmask = fvkutils::UniformBufferBitmask;
static_assert(sizeof(Bitmask) * 8 == fvkutils::MAX_DESCRIPTOR_SET_BITMASK_BITS);
template<typename T>
void erasep(std::vector<T>& v, std::function<bool(T const&)> f) {
auto newEnd = std::remove_if(v.begin(), v.end(), f);
v.erase(newEnd, v.end());
}
using ImageData = VulkanExternalImageManager::VulkanExternalImageManager::ImageData;
ImageData& findImage(std::vector<ImageData>& images,
fvkmemory::resource_ptr<VulkanTexture> texture) {
auto itr = std::find_if(images.begin(), images.end(), [&](ImageData const& data) {
return data.image == texture;
});
assert_invariant(itr != images.end());
return *itr;
}
void copySet(VkDevice device, VkDescriptorSet srcSet, VkDescriptorSet dstSet, Bitmask bindings) {
// TODO: fix the size for better memory management
std::vector<VkCopyDescriptorSet> copies;
bindings.forEachSetBit([&](size_t index) {
copies.push_back({
.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET,
.srcSet = srcSet,
.srcBinding = (uint32_t) index,
.dstSet = dstSet,
.dstBinding = (uint32_t) index,
.descriptorCount = 1,
});
});
vkUpdateDescriptorSets(device, 0, nullptr, copies.size(), copies.data());
}
Bitmask foldBitsInHalf(Bitmask bitset) {
Bitmask outBitset;
bitset.forEachSetBit([&](size_t index) {
constexpr size_t BITMASK_LOWER_BITS_LEN = sizeof(outBitset) * 4;
outBitset.set(index % BITMASK_LOWER_BITS_LEN);
});
return outBitset;
}
}// namespace
VulkanExternalImageManager::VulkanExternalImageManager(VulkanPlatform* platform,
VulkanSamplerCache* samplerCache, VulkanYcbcrConversionCache* ycbcrConversionCache,
VulkanDescriptorSetCache* setCache, VulkanDescriptorSetLayoutCache* layoutCache)
: mPlatform(platform),
mSamplerCache(samplerCache),
mYcbcrConversionCache(ycbcrConversionCache),
mDescriptorSetCache(setCache),
mDescriptorSetLayoutCache(layoutCache) {
}
VulkanExternalImageManager::~VulkanExternalImageManager() = default;
void VulkanExternalImageManager::terminate() {
mSetBindings.clear();
mImages.clear();
}
void VulkanExternalImageManager::onBeginFrame() {
std::for_each(mImages.begin(), mImages.end(), [](ImageData& image) {
image.hasBeenValidated = false;
});
std::for_each(mSetBindings.begin(), mSetBindings.end(), [](SetBindingInfo& info) {
info.bound = false;
});
}
fvkutils::DescriptorSetMask VulkanExternalImageManager::prepareBindSets(LayoutArray const& layouts,
SetArray const& sets) {
fvkutils::DescriptorSetMask shouldUseExternalSampler{};
for (uint8_t i = 0; i < sets.size(); i++) {
auto set = sets[i];
auto layout = layouts[i];
if (!set || !layout) {
continue;
}
if (hasExternalSampler(set)) {
updateSetAndLayout(set, layout);
shouldUseExternalSampler.set(i);
}
}
return shouldUseExternalSampler;
}
bool VulkanExternalImageManager::hasExternalSampler(
fvkmemory::resource_ptr<VulkanDescriptorSet> set) {
auto itr = std::find_if(mSetBindings.begin(), mSetBindings.end(),
[&](SetBindingInfo const& info) { return info.set == set; });
return itr != mSetBindings.end();
}
void VulkanExternalImageManager::updateSetAndLayout(
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
utils::FixedCapacityVector<
std::tuple<uint8_t, VkSampler, fvkmemory::resource_ptr<VulkanTexture>>>
samplerAndBindings;
samplerAndBindings.reserve(MAX_SAMPLER_COUNT);
fvkutils::SamplerBitmask actualExternalSamplers;
for (auto& bindingInfo : mSetBindings) {
if (bindingInfo.set != set || bindingInfo.bound) {
continue;
}
auto& imageData = findImage(mImages, bindingInfo.image);
updateImage(&imageData);
auto samplerParams = bindingInfo.samplerParams;
// according to spec, these must match chromaFilter
// https://registry.khronos.org/vulkan/specs/latest/man/html/VkSamplerCreateInfo.html#VUID-VkSamplerCreateInfo-minFilter-01645
samplerParams.filterMag = SamplerMagFilter::NEAREST;
samplerParams.filterMin = SamplerMinFilter::NEAREST;
auto sampler = mSamplerCache->getSampler({
.sampler = samplerParams,
.conversion = imageData.conversion,
});
actualExternalSamplers.set(bindingInfo.binding);
samplerAndBindings.push_back({ bindingInfo.binding, sampler, bindingInfo.image });
bindingInfo.bound = true;
}
if (samplerAndBindings.empty()) {
return;
}
// Sort by binding number
std::sort(samplerAndBindings.begin(), samplerAndBindings.end(), [](auto const& a, auto const& b) {
return std::get<0>(a) < std::get<0>(b);
});
utils::FixedCapacityVector<VkSampler> outSamplers;
outSamplers.reserve(MAX_SAMPLER_COUNT);
std::for_each(samplerAndBindings.begin(), samplerAndBindings.end(),
[&](auto const& b) { outSamplers.push_back(std::get<1>(b)); });
VkDescriptorSetLayout const oldLayout = layout->getExternalSamplerVkLayout();
VkDescriptorSetLayout const newLayout = mDescriptorSetLayoutCache->getVkLayout(layout->bitmask,
actualExternalSamplers, outSamplers);
// Need to copy the set
VkDescriptorSet const oldSet = set->getExternalSamplerVkSet();
if (oldLayout != newLayout || oldSet == VK_NULL_HANDLE) {
// Build a new descriptor set from the new layout
VkDescriptorSet const newSet = mDescriptorSetCache->getVkSet(layout->count, newLayout);
auto const ubo = layout->bitmask.ubo | layout->bitmask.dynamicUbo;
auto const samplers = layout->bitmask.sampler & (~actualExternalSamplers);
// Each bitmask denotes a binding index, and separated into two stages - vertex and buffer
// We fold the two stages into just the lower half of the bits to denote a combined set of
// bindings.
Bitmask const copyBindings = foldBitsInHalf(ubo | samplers);
VkDescriptorSet const srcSet = oldSet != VK_NULL_HANDLE ? oldSet : set->getVkSet();
copySet(mPlatform->getDevice(), srcSet, newSet, copyBindings);
set->setExternalSamplerVkSet(newSet, [&](VulkanDescriptorSet*) {
mDescriptorSetCache->manualRecycle(layout->count, newLayout, newSet);
});
if (oldLayout != newLayout) {
layout->setExternalSamplerVkLayout(newLayout);
}
}
// Update the external samplers in the set
for (auto& [binding, sampler, image]: samplerAndBindings) {
mDescriptorSetCache->updateSamplerForExternalSamplerSet(set, binding, image);
}
}
VkSamplerYcbcrConversion VulkanExternalImageManager::getVkSamplerYcbcrConversion(
VulkanPlatform::ExternalImageMetadata const& metadata) {
// This external image does not require external sampler (YUV conversion).
if (metadata.externalFormat == 0 && !fvkutils::isVKYcbcrConversionFormat(metadata.format)) {
return VK_NULL_HANDLE;
}
VulkanYcbcrConversionCache::Params ycbcrParams = {
.conversion = {
.ycbcrModel = fvkutils::getYcbcrModelConversionFilament(metadata.ycbcrModel),
.r = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.r, 0),
.g = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.g, 1),
.b = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.b, 2),
.a = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.a, 3),
.ycbcrRange = fvkutils::getYcbcrRangeFilament(metadata.ycbcrRange),
.xChromaOffset = fvkutils::getChromaLocationFilament(metadata.xChromaOffset),
.yChromaOffset = fvkutils::getChromaLocationFilament(metadata.yChromaOffset),
// Unclear where to get the chromaFilter, we just assume it's nearest.
.chromaFilter = SamplerMagFilter::NEAREST,
},
.format = metadata.format,
.externalFormat = metadata.externalFormat,
};
return mYcbcrConversionCache->getConversion(ycbcrParams);
}
void VulkanExternalImageManager::updateImage(ImageData* image) {
if (image->hasBeenValidated) {
return;
}
image->hasBeenValidated = true;
auto metadata = mPlatform->extractExternalImageMetadata(image->platformHandle);
auto vkYcbcr = getVkSamplerYcbcrConversion(metadata);
if (vkYcbcr == image->conversion) {
return;
}
image->image->setYcbcrConversion(vkYcbcr);
image->conversion = vkYcbcr;
return;
}
void VulkanExternalImageManager::removeDescriptorSet(
fvkmemory::resource_ptr<VulkanDescriptorSet> inSet) {
erasep<SetBindingInfo>(mSetBindings,
[&](auto const& bindingInfo) { return (bindingInfo.set == inSet); });
}
void VulkanExternalImageManager::bindExternallySampledTexture(
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t bindingPoint,
fvkmemory::resource_ptr<VulkanTexture> image, SamplerParams samplerParams) {
// Should we do duplicate validation here?
auto& imageData = findImage(mImages, image);
mSetBindings.push_back({ bindingPoint, imageData.image, set, samplerParams });
}
void VulkanExternalImageManager::addExternallySampledTexture(
fvkmemory::resource_ptr<VulkanTexture> image,
Platform::ExternalImageHandleRef platformHandleRef) {
mImages.push_back({ image, platformHandleRef, false });
}
void VulkanExternalImageManager::removeExternallySampledTexture(
fvkmemory::resource_ptr<VulkanTexture> image) {
erasep<SetBindingInfo>(mSetBindings,
[&](auto const& bindingInfo) { return (bindingInfo.image == image); });
erasep<ImageData>(mImages, [&](auto const& imageData) {
return imageData.image == image;
});
}
bool VulkanExternalImageManager::isExternallySampledTexture(
fvkmemory::resource_ptr<VulkanTexture> image) const {
return std::find_if(mImages.begin(), mImages.end(), [&](auto const& imageData) {
return imageData.image == image;
}) != mImages.end();
}
void VulkanExternalImageManager::clearTextureBinding(
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t bindingPoint) {
erasep<SetBindingInfo>(mSetBindings, [&](auto const& bindingInfo) {
return (bindingInfo.set == set && bindingInfo.binding == bindingPoint);
});
}
} // namesapce filament::backend

View File

@@ -1,121 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
#include "VulkanHandles.h"
#include <backend/DriverEnums.h>
#include <array>
#include <vector>
namespace filament::backend {
class VulkanYcbcrConversionCache;
class VulkanSamplerCache;
class VulkanDescriptorSetLayoutCache;
class VulkanDescriptorSetCache;
// Manages the logic of external images and their quirks wrt Vulikan.
class VulkanExternalImageManager {
public:
VulkanExternalImageManager(
VulkanPlatform* platform,
VulkanSamplerCache* samplerCache,
VulkanYcbcrConversionCache* ycbcrConversionCache,
VulkanDescriptorSetCache* setCache,
VulkanDescriptorSetLayoutCache* layoutCache);
~VulkanExternalImageManager();
void terminate();
void onBeginFrame();
using SetArray = std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
using LayoutArray = std::array<fvkmemory::resource_ptr<VulkanDescriptorSetLayout>,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
using VkLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
// Returns bitmask to indicate whether or not to use the external sampler version of each
// descriptor set.
fvkutils::DescriptorSetMask prepareBindSets(LayoutArray const& layouts, SetArray const& sets);
void removeDescriptorSet(fvkmemory::resource_ptr<VulkanDescriptorSet> set);
void bindExternallySampledTexture(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t bindingPoint, fvkmemory::resource_ptr<VulkanTexture> image,
SamplerParams samplerParams);
void clearTextureBinding(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t bindingPoint);
void addExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> external,
Platform::ExternalImageHandleRef platformHandleRef);
void removeExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image);
bool isExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image) const;
VkSamplerYcbcrConversion getVkSamplerYcbcrConversion(
VulkanPlatform::ExternalImageMetadata const& metadata);
struct ImageData {
fvkmemory::resource_ptr<VulkanTexture> image;
Platform::ExternalImageHandle platformHandle;
bool hasBeenValidated = false; // indicates whether the image has been validated *this frame*
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
};
private:
bool hasExternalSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set);
void updateSetAndLayout(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
void updateImage(ImageData* imageData);
VulkanPlatform* mPlatform;
VulkanSamplerCache* mSamplerCache;
VulkanYcbcrConversionCache* mYcbcrConversionCache;
VulkanDescriptorSetCache* mDescriptorSetCache;
VulkanDescriptorSetLayoutCache* mDescriptorSetLayoutCache;
using SetAndLayout = std::pair<fvkmemory::resource_ptr<VulkanDescriptorSet>,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>>;
struct SetBindingInfo {
uint8_t binding = 0;
fvkmemory::resource_ptr<VulkanTexture> image;
fvkmemory::resource_ptr<VulkanDescriptorSet> set;
SamplerParams samplerParams;
bool bound = false;
};
// Use vectors instead of hash maps because we only expect small number of entries.
std::vector<SetBindingInfo> mSetBindings;
std::vector<ImageData> mImages;
};
} // filament::backend
#endif // TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H

View File

@@ -330,20 +330,12 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
mRenderPassCache[config] = {renderPass, mCurrentTime};
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
FVK_LOGD << "Created render pass " << renderPass << " with ";
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; ++i) {
FVK_LOGD << (int) config.colorFormat[i] << " ";
}
FVK_LOGD << ", "
<< "depth = " << config.depthFormat << ", "
<< "initialDepthLayout = " << (int) config.initialDepthLayout << ", "
<< "samples = " << int(config.samples) << ", "
<< "needsResolveMask = " << int(config.needsResolveMask) << ", "
<< "usesLazilyAllocatedMemory = " << int(config.usesLazilyAllocatedMemory) << ", "
<< "viewCount = " << int(config.viewCount) << ", "
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount
<< utils::io::endl;
#endif
FVK_LOGD << "Created render pass " << renderPass << " with "
<< "samples = " << int(config.samples) << ", "
<< "depth = " << (hasDepth ? 1 : 0) << ", "
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount
<< utils::io::endl;
#endif
return renderPass;
}

View File

@@ -149,18 +149,11 @@ void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr<VulkanBufferObject> ob
mResources.push_back(obj);
}
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout&& layout,
VkDescriptorSetLayout vkLayout)
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout const& layout)
: bitmask(fromBackendLayout(layout)),
count(Count::fromLayoutBitmask(bitmask)),
mVkLayout(vkLayout) {}
count(Count::fromLayoutBitmask(bitmask)) {}
VulkanDescriptorSetLayout::Bitmask VulkanDescriptorSetLayout::Bitmask::fromLayoutDescription(
DescriptorSetLayout const& layout) {
return fromBackendLayout(layout);
}
PushConstantDescription::PushConstantDescription(backend::Program const& program) {
PushConstantDescription::PushConstantDescription(backend::Program const& program) noexcept {
mRangeCount = 0;
for (auto stage : { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) {
auto const& constants = program.getPushConstants(stage);

View File

@@ -71,7 +71,7 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
fvkutils::SamplerBitmask sampler; // 8 bytes
fvkutils::InputAttachmentBitmask inputAttachment; // 8 bytes
// This is a subset of the sampler field.
// This is a subset of the bitmask.sampler field.
fvkutils::SamplerBitmask externalSampler; // 8 bytes
bool operator==(Bitmask const& right) const {
@@ -79,8 +79,6 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
inputAttachment == right.inputAttachment &&
externalSampler == right.externalSampler;
}
static Bitmask fromLayoutDescription(DescriptorSetLayout const& layout);
};
static_assert(sizeof(Bitmask) == 40);
@@ -122,20 +120,16 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
}
};
VulkanDescriptorSetLayout(DescriptorSetLayout&& layout, VkDescriptorSetLayout vkLayout);
VulkanDescriptorSetLayout(DescriptorSetLayout const& layout);
// Note that we don't destroy the vklayout. This is done by the layout cache.
~VulkanDescriptorSetLayout() = default;
VkDescriptorSetLayout getVkLayout() const noexcept { return mVkLayout; }
VkDescriptorSetLayout const& getVkLayout() const noexcept { return mVkLayout; }
VkDescriptorSetLayout getExternalSamplerVkLayout() const noexcept {
return mExternalSamplerVkLayout;
}
void setExternalSamplerVkLayout(VkDescriptorSetLayout vklayout) noexcept {
mExternalSamplerVkLayout = vklayout;
}
// It is possible to have the layout switch out due to AHardwarebuffer (external image) format
// changes.
void setVkLayout(VkDescriptorSetLayout vklayout) noexcept { mVkLayout = vklayout; }
bool hasExternalSamplers() const noexcept { return bitmask.externalSampler.count() > 0; }
@@ -143,11 +137,7 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
Count const count;
private:
// This is the layout without any immutable samplers.
VkDescriptorSetLayout const mVkLayout = VK_NULL_HANDLE;
// This is the layout with immutable samplers, and can be updated.
VkDescriptorSetLayout mExternalSamplerVkLayout = VK_NULL_HANDLE;
VkDescriptorSetLayout mVkLayout = VK_NULL_HANDLE;
};
struct VulkanDescriptorSet : public HwDescriptorSet, fvkmemory::Resource {
@@ -159,37 +149,23 @@ public:
VulkanDescriptorSet(
fvkutils::UniformBufferBitmask const& dynamicUboMask,
uint8_t uniqueDynamicUboCount,
OnRecycle&& onRecycleFn, VkDescriptorSet vkSet)
OnRecycle&& onRecycleFn)
: dynamicUboMask(dynamicUboMask),
uniqueDynamicUboCount(uniqueDynamicUboCount),
mVkSet(vkSet),
mOnRecycleFn(std::move(onRecycleFn)) {}
// NOLINTNEXTLINE(bugprone-exception-escape)
~VulkanDescriptorSet() {
if (mOnRecycleFn) {
mOnRecycleFn(this);
}
if (mOnRecycleExternalSamplerFn) {
mOnRecycleExternalSamplerFn(this);
}
}
VkDescriptorSet getVkSet() const noexcept {
VkDescriptorSet const& getVkSet() const noexcept {
return mVkSet;
}
VkDescriptorSet getExternalSamplerVkSet() const noexcept {
return mExternalSamplerVkSet;
}
void setExternalSamplerVkSet(VkDescriptorSet vkset, OnRecycle onRecycle) {
mExternalSamplerVkSet = vkset;
if (mOnRecycleExternalSamplerFn) {
mOnRecycleExternalSamplerFn(this);
}
mOnRecycleExternalSamplerFn = onRecycle;
}
// Note that the only case where you'd set it more than once is with external images/samplers.
void setVkSet(VkDescriptorSet vkset) noexcept { mVkSet = vkset; }
void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept {
mOffsets = std::move(offsets);
@@ -206,20 +182,17 @@ public:
uint8_t const uniqueDynamicUboCount;
private:
VkDescriptorSet const mVkSet;
VkDescriptorSet mExternalSamplerVkSet = VK_NULL_HANDLE;
VkDescriptorSet mVkSet = VK_NULL_HANDLE;
backend::DescriptorSetOffsetArray mOffsets;
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
OnRecycle mOnRecycleFn;
OnRecycle mOnRecycleExternalSamplerFn;
};
using PushConstantNameArray = utils::FixedCapacityVector<char const*>;
using PushConstantNameByStage = std::array<PushConstantNameArray, Program::SHADER_TYPE_COUNT>;
struct PushConstantDescription {
explicit PushConstantDescription(backend::Program const& program);
explicit PushConstantDescription(backend::Program const& program) noexcept;
VkPushConstantRange const* getVkRanges() const noexcept { return mRanges; }
uint32_t getVkRangeCount() const noexcept { return mRangeCount; }

View File

@@ -66,12 +66,8 @@ void VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) {
// If an error occurred, allow higher levels to handle it gracefully.
assert_invariant(cacheEntry != nullptr && "Failed to create/find pipeline");
static PipelineEqual equal;
if (!equal(mBoundPipeline, mPipelineRequirements)) {
mBoundPipeline = mPipelineRequirements;
vkCmdBindPipeline(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, cacheEntry->handle);
}
mBoundPipeline = mPipelineRequirements;
vkCmdBindPipeline(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, cacheEntry->handle);
}
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept {

View File

@@ -335,7 +335,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
mTaskHandler->post(std::move(waitFenceFunc), std::move(cleanPbdFunc));
}
void VulkanReadPixels::runUntilComplete() {
void VulkanReadPixels::runUntilComplete() noexcept {
if (!mTaskHandler) {
return;
}

View File

@@ -79,7 +79,7 @@ public:
OnReadCompleteFunction const& readCompleteFunc);
// This method will block until all of the in-flight requests are complete.
void runUntilComplete();
void runUntilComplete() noexcept;
private:
VkDevice mDevice = VK_NULL_HANDLE;

View File

@@ -28,7 +28,7 @@ namespace filament::backend {
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
: mDevice(device) {}
VkSampler VulkanSamplerCache::getSampler(Params params) {
VkSampler VulkanSamplerCache::getSampler(Params params) noexcept {
auto iter = mCache.find(params);
if (UTILS_LIKELY(iter != mCache.end())) {
return iter->second;

View File

@@ -38,7 +38,7 @@ public:
static_assert(sizeof(Params) == 16);
explicit VulkanSamplerCache(VkDevice device);
VkSampler getSampler(Params params);
VkSampler getSampler(Params params) noexcept;
void terminate() noexcept;
private:
VkDevice mDevice;

View File

@@ -24,7 +24,7 @@
#include <utils/Panic.h>
static constexpr uint32_t TIME_BEFORE_EVICTION = 3;
static constexpr uint32_t TIME_BEFORE_EVICTION = FVK_MAX_COMMAND_BUFFERS;
namespace filament::backend {
@@ -39,7 +39,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
auto stage = iter->second;
mFreeStages.erase(iter);
stage->lastAccessed = mCurrentFrame;
mUsedStages.push_back(stage);
mUsedStages.insert(stage);
return stage;
}
// We were not able to find a sufficiently large stage, so create a new one.
@@ -51,7 +51,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
});
// Create the VkBuffer.
mUsedStages.push_back(stage);
mUsedStages.insert(stage);
VkBufferCreateInfo bufferInfo {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = numBytes,
@@ -77,7 +77,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
if (image->format == vkformat && image->width == width && image->height == height) {
mFreeImages.erase(image);
image->lastAccessed = mCurrentFrame;
mUsedImages.push_back(image);
mUsedImages.insert(image);
return image;
}
}
@@ -89,7 +89,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
.lastAccessed = mCurrentFrame,
});
mUsedImages.push_back(image);
mUsedImages.insert(image);
const VkImageCreateInfo imageInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
@@ -161,7 +161,7 @@ void VulkanStagePool::gc() noexcept {
stage->lastAccessed = mCurrentFrame;
mFreeStages.insert(std::make_pair(stage->capacity, stage));
} else {
mUsedStages.push_back(stage);
mUsedStages.insert(stage);
}
}
@@ -185,7 +185,7 @@ void VulkanStagePool::gc() noexcept {
image->lastAccessed = mCurrentFrame;
mFreeImages.insert(image);
} else {
mUsedImages.push_back(image);
mUsedImages.insert(image);
}
}
FVK_SYSTRACE_END();

View File

@@ -22,7 +22,6 @@
#include <map>
#include <unordered_set>
#include <vector>
namespace filament::backend {
@@ -74,10 +73,10 @@ private:
std::multimap<uint32_t, VulkanStage const*> mFreeStages;
// Simple unordered set for stashing a list of in-use stages that can be reclaimed later.
std::vector<VulkanStage const*> mUsedStages;
std::unordered_set<VulkanStage const*> mUsedStages;
std::unordered_set<VulkanStageImage const*> mFreeImages;
std::vector<VulkanStageImage const*> mUsedImages;
std::unordered_set<VulkanStageImage const*> mUsedImages;
// Store the current "time" (really just a frame count) and LRU eviction parameters.
uint64_t mCurrentFrame = 0;

View File

@@ -223,7 +223,8 @@ VkImageUsageFlags getUsage(VulkanContext const& context, uint8_t samples,
VulkanTextureState::VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands,
VmaAllocator allocator, VkDevice device, VkImage image, VkDeviceMemory deviceMemory,
VkFormat format, VkImageViewType viewType, uint8_t levels, uint8_t layerCount,
VkSamplerYcbcrConversion ycbcrConversion, VkImageUsageFlags usage, bool isProtected)
VkSamplerYcbcrConversion ycbcrConversion, bool isExternalFormat, VkImageUsageFlags usage,
bool isProtected)
: mStagePool(stagePool),
mCommands(commands),
mAllocator(allocator),
@@ -233,17 +234,17 @@ VulkanTextureState::VulkanTextureState(VulkanStagePool& stagePool, VulkanCommand
mVkFormat(format),
mViewType(viewType),
mFullViewRange{ fvkutils::getImageAspect(format), 0, levels, 0, layerCount },
mYcbcr{ ycbcrConversion },
mYcbcr{ ycbcrConversion, isExternalFormat },
mDefaultLayout(getDefaultLayoutImpl(usage)),
mUsage(usage),
mIsProtected(isProtected) {}
VulkanTextureState::~VulkanTextureState() {
clearCachedImageViews();
if (mTextureImageMemory != VK_NULL_HANDLE) {
vkDestroyImage(mDevice, mTextureImage, VKALLOC);
vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC);
}
clearCachedImageViews();
}
void VulkanTextureState::clearCachedImageViews() noexcept {
@@ -271,7 +272,7 @@ VkImageView VulkanTextureState::getImageView(VkImageSubresourceRange range, VkIm
.flags = 0,
.image = mTextureImage,
.viewType = viewType,
.format = mYcbcr.conversion != VK_NULL_HANDLE ? VK_FORMAT_UNDEFINED : mVkFormat,
.format = mYcbcr.isExternalFormat ? VK_FORMAT_UNDEFINED : mVkFormat,
.components = swizzle,
.subresourceRange = range,
};
@@ -293,6 +294,7 @@ VulkanTexture::VulkanTexture(VulkanContext const& context, VkDevice device, VmaA
commands, allocator, device, image, memory, format,
fvkutils::getViewType(SamplerType::SAMPLER_2D),
/*mipLevels=*/1, getLayerCountFromDepth(depth), conversion,
/*isExternalFormat=*/false,
getUsage(context, samples, VK_NULL_HANDLE, format, tusage),
any(usage & TextureUsage::PROTECTED))) {
mPrimaryViewRange = mState->mFullViewRange;
@@ -422,7 +424,8 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
mState = fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, stagePool,
commands, allocator, device, textureImage, textureImageMemory, vkFormat,
fvkutils::getViewType(target), levels, getLayerCount(target, depth),
VK_NULL_HANDLE /* ycbcrConversion */, imageInfo.usage, isProtected);
VK_NULL_HANDLE /* ycbcrConversion */, false /*isExternalFormat*/, imageInfo.usage,
isProtected);
// Spec out the "primary" VkImageView that shaders use to sample from the image.
mPrimaryViewRange = mState->mFullViewRange;
@@ -761,13 +764,15 @@ void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout
}
}
void VulkanTexture::setYcbcrConversion(VkSamplerYcbcrConversion conversion) {
void VulkanTexture::setYcbcrConversion(VkSamplerYcbcrConversion conversion, bool isExternalFormat) {
// Note that this comparison is valid because we only ever create VkSamplerYcbcrConversion from
// a cache. So for each set of parameters, there is exactly one conversion (similar to
// samplers).
VulkanTextureState::Ycbcr ycbcr = {
.conversion = conversion,
.isExternalFormat = isExternalFormat,
};
if (mState->mYcbcr != ycbcr) {
mState->mYcbcr = ycbcr;
mState->clearCachedImageViews();

View File

@@ -40,7 +40,8 @@ struct VulkanTextureState : public fvkmemory::Resource {
VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands, VmaAllocator allocator,
VkDevice device, VkImage image, VkDeviceMemory deviceMemory, VkFormat format,
VkImageViewType viewType, uint8_t levels, uint8_t layerCount,
VkSamplerYcbcrConversion ycbcrConversion, VkImageUsageFlags usage, bool isProtected);
VkSamplerYcbcrConversion ycbcrConversion, bool isExternalFormat,
VkImageUsageFlags usage, bool isProtected);
~VulkanTextureState();
@@ -85,9 +86,10 @@ private:
// conversion matrix per-frame.
struct Ycbcr {
VkSamplerYcbcrConversion conversion;
bool isExternalFormat;
bool operator==(Ycbcr const& other) const {
return conversion == other.conversion;
return conversion == other.conversion && isExternalFormat == other.isExternalFormat;
}
bool operator!=(Ycbcr const& other) const {
@@ -207,7 +209,7 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
// This is used in the case of external images and external samplers. AHB might update the
// conversion per-frame. This implies that we need to invalidate the view cache when that
// happens.
void setYcbcrConversion(VkSamplerYcbcrConversion conversion);
void setYcbcrConversion(VkSamplerYcbcrConversion conversion, bool isExternal);
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
void print() const;

View File

@@ -33,7 +33,7 @@ VulkanYcbcrConversionCache::VulkanYcbcrConversionCache(VkDevice device)
: mDevice(device) {}
VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
VulkanYcbcrConversionCache::Params params) {
VulkanYcbcrConversionCache::Params params) noexcept {
auto iter = mCache.find(params);
if (UTILS_LIKELY(iter != mCache.end())) {
return iter->second;
@@ -43,7 +43,7 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
TextureSwizzle const swizzleArray[] = { chroma.r, chroma.g, chroma.b, chroma.a };
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.format = params.format,
.format = fvkutils::getVkFormat(params.format),
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
.components = fvkutils::getSwizzleMap(swizzleArray),

View File

@@ -30,14 +30,15 @@ namespace filament::backend {
class VulkanYcbcrConversionCache {
public:
struct Params {
SamplerYcbcrConversion conversion = {}; // 4
VkFormat format; // 4
uint64_t externalFormat = 0; // 8
SamplerYcbcrConversion conversion = {};
TextureFormat format = {};
uint16_t padding = 0;
uint64_t externalFormat = 0;
};
static_assert(sizeof(Params) == 16);
explicit VulkanYcbcrConversionCache(VkDevice device);
VkSamplerYcbcrConversion getConversion(Params params);
VkSamplerYcbcrConversion getConversion(Params params) noexcept;
void terminate() noexcept;
private:
@@ -47,8 +48,7 @@ private:
bool operator()(Params lhs, Params rhs) const noexcept {
SamplerYcbcrConversion::EqualTo equal;
return equal(lhs.conversion, rhs.conversion) &&
lhs.externalFormat == rhs.externalFormat &&
lhs.format == rhs.format;
lhs.externalFormat == rhs.externalFormat;
}
};
using ConversionHashFn = utils::hash::MurmurHashFn<Params>;

View File

@@ -31,8 +31,6 @@ ResourceManager::ResourceManager(size_t arenaSize, bool disableUseAfterFreeCheck
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck, disablePoolHandleTags) {}
void ResourceManager::gc() noexcept {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("ResourceManager::gc");
auto destroyAll = [this](GcList& list) {
for (auto const& [type, id]: list) {
destroyWithType(type, id);
@@ -51,7 +49,6 @@ void ResourceManager::gc() noexcept {
GcList gcs;
std::swap(gcs, mGcList);
destroyAll(gcs);
FVK_SYSTRACE_END();
}
void ResourceManager::terminate() noexcept {

View File

@@ -132,10 +132,6 @@ public:
return id() == other.id() && type() == other.type();
}
inline bool operator!=(resource_ptr<D> const& other) const {
return !((*this) == other);
}
inline explicit operator bool() const {
return bool(mRef);
}

View File

@@ -212,8 +212,6 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
// This is needed for external images. See VulkanPlatformAndroid
VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME,
#endif
// MoltenVk is the only non-conformant implementation we're interested in.
#if defined(__APPLE__)
@@ -995,6 +993,30 @@ VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept {
return mImpl->mProtectedGraphicsQueue;
}
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadata(
ExternalImageHandleRef externalImage) {
return getExternalImageMetadataImpl(externalImage, mImpl->mDevice);
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageData(
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata, memoryTypeIndex,
usage);
}
VkSampler VulkanPlatform::createExternalSampler(SamplerYcbcrConversion chroma,
SamplerParams sampler, uint32_t internalFormat) {
return createExternalSamplerImpl(mImpl->mDevice, chroma, sampler, internalFormat);
}
VkImageView VulkanPlatform::createExternalImageView(SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle) {
return createExternalImageViewImpl(mImpl->mDevice, chroma, internalFormat, image, range,
viewType, swizzle);
}
ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() const {
return getSwapchainInstanceExtensionsImpl();
}

View File

@@ -15,14 +15,14 @@
*/
#include <backend/platforms/VulkanPlatformAndroid.h>
#include "vulkan/VulkanConstants.h"
#include "vulkan/VulkanContext.h"
#include <backend/DriverEnums.h>
#include <private/backend/BackendUtilsAndroid.h>
#include "vulkan/VulkanConstants.h"
#include <utils/Panic.h>
#include "vulkan/utils/Image.h"
#include "vulkan/utils/Conversion.h"
#include <bluevk/BlueVK.h>
@@ -39,14 +39,6 @@ namespace {
VkFormat transformVkFormat(VkFormat format, bool sRGB) {
if (!sRGB) {
switch (format) {
case VK_FORMAT_R8G8B8A8_SRGB:
return VK_FORMAT_R8G8B8A8_UNORM;
case VK_FORMAT_R8G8B8_SRGB:
return VK_FORMAT_R8G8B8_UNORM;
default:
break;
}
return format;
}
@@ -65,7 +57,7 @@ VkFormat transformVkFormat(VkFormat format, bool sRGB) {
}
bool isProtectedFromUsage(uint64_t usage) {
return usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
return (usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) ? true : false;
}
std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer_Desc& desc,
@@ -128,9 +120,7 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
usage = 0;
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
// We shouldn't be using external samplers as input attachments
// usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
}
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
if (isDepthFormat) {
@@ -146,37 +136,87 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
return { format, usage };
}
std::pair<TextureFormat, TextureUsage> getFilamentFormatAndUsage(const AHardwareBuffer_Desc& desc,
bool sRGB) {
auto const format = mapToFilamentFormat(desc.format, sRGB);
return {
format,
mapToFilamentUsage(desc.usage, format),
VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevice device,
VulkanPlatform::ExternalImageMetadata const& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage) {
VulkanPlatform::ImageData data;
// if external format we need to specifiy it in the allocation
const bool useExternalFormat = metadata.format == VK_FORMAT_UNDEFINED;
const VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.pNext = nullptr,
// pass down the format (external means we don't have it VK defined)
.externalFormat = metadata.externalFormat,
};
const VkExternalMemoryImageCreateInfo externalCreateInfo = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
.pNext = useExternalFormat ? &externalFormat : nullptr,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
};
VkImageCreateInfo imageInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
imageInfo.pNext = &externalCreateInfo;
imageInfo.format = metadata.format;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent = {
metadata.width,
metadata.height,
1u,
};
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = metadata.layers;
imageInfo.samples = metadata.samples;
imageInfo.usage = usage;
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &data.first);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
// Allocate the memory
VkImportAndroidHardwareBufferInfoANDROID androidHardwareBufferInfo = {
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
.pNext = nullptr,
.buffer = buffer,
};
VkMemoryDedicatedAllocateInfo memoryDedicatedAllocateInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
.pNext = &androidHardwareBufferInfo,
.image = data.first,
.buffer = VK_NULL_HANDLE,
};
VkMemoryAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &memoryDedicatedAllocateInfo,
.allocationSize = metadata.allocationSize,
.memoryTypeIndex = memoryTypeIndex,
};
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &data.second);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
return data;
}
}// namespace
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() {
if (__builtin_available(android 26, *)) {
if (aHardwareBuffer) {
AHardwareBuffer_release(aHardwareBuffer);
}
}
}
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default;
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
AHardwareBuffer const* buffer, bool sRGB) noexcept {
if (__builtin_available(android 26, *)) {
auto bufferImpl = const_cast<AHardwareBuffer*>(buffer);
AHardwareBuffer_acquire(bufferImpl);
AHardwareBuffer_Desc hardwareBufferDescription = {};
AHardwareBuffer_describe(buffer, &hardwareBufferDescription);
auto* const p = new (std::nothrow) ExternalImageVulkanAndroid;
p->aHardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
p->sRGB = sRGB;
p->height = hardwareBufferDescription.height;
p->width = hardwareBufferDescription.width;
TextureFormat textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
p->format = textureFormat;
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
return Platform::ExternalImageHandle{ p };
}
@@ -185,20 +225,23 @@ Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
VulkanPlatformAndroid::ExternalImageDescAndroid VulkanPlatformAndroid::getExternalImageDesc(
ExternalImageHandleRef externalImage) const noexcept {
auto metadata = extractExternalImageMetadata(externalImage);
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
return {
.width = metadata.width,
.height = metadata.height,
.format = metadata.filamentFormat,
.usage = metadata.filamentUsage,
.width = fvkExternalImage->width,
.height = fvkExternalImage->height,
.format = fvkExternalImage->format,
.usage = fvkExternalImage->usage,
};
}
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::extractExternalImageMetadata(
ExternalImageHandleRef image) const {
auto const* fvkExternalImage = static_cast<ExternalImageVulkanAndroid const*>(image.get());
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMetadata(
ExternalImageHandleRef externalImage) {
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
ExternalImageMetadata metadata = {};
ExternalImageMetadata metadata;
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
if (__builtin_available(android 26, *)) {
AHardwareBuffer_Desc bufferDesc;
@@ -206,29 +249,16 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::extractExternalImag
metadata.width = bufferDesc.width;
metadata.height = bufferDesc.height;
metadata.layers = bufferDesc.layers;
metadata.isProtected = isProtectedFromUsage(bufferDesc.usage);
std::tie(metadata.format, metadata.usage) =
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
std::tie(metadata.filamentFormat, metadata.filamentUsage) =
getFilamentFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
if (isProtectedFromUsage(bufferDesc.usage)) {
metadata.filamentUsage |= TextureUsage::PROTECTED;
}
// TODO: The following seems unnecessary. we should be able to discern directly from the
// bufferDesc.
if (any(metadata.filamentUsage & TextureUsage::BLIT_SRC)) {
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(metadata.filamentUsage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
}
metadata.samples = VK_SAMPLE_COUNT_1_BIT;
VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = {
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID,
.pNext = nullptr,
};
VkAndroidHardwareBufferPropertiesANDROID properties = {
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
@@ -238,155 +268,138 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::extractExternalImag
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkGetAndroidHardwareBufferProperties failed with error="
<< static_cast<int32_t>(result);
VkFormat bufferPropertiesFormat = transformVkFormat(formatInfo.format, fvkExternalImage->sRGB);
FILAMENT_CHECK_POSTCONDITION(metadata.format == bufferPropertiesFormat)
<< "mismatched image format( " << metadata.format << ") and queried format("
<< bufferPropertiesFormat << ") for external image (AHB)";
bool const requiresConversion =
metadata.format == VK_FORMAT_UNDEFINED ||
fvkutils::isVKYcbcrConversionFormat(metadata.format);
if (requiresConversion) {
metadata.format = VK_FORMAT_UNDEFINED;
metadata.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
metadata.externalFormat = formatInfo.externalFormat;
} else {
metadata.externalFormat = 0;
}
metadata.externalFormat = formatInfo.externalFormat;
metadata.allocationSize = properties.allocationSize;
metadata.memoryTypeBits = properties.memoryTypeBits;
metadata.ycbcrConversionComponents = formatInfo.samplerYcbcrConversionComponents;
metadata.ycbcrModel = formatInfo.suggestedYcbcrModel;
metadata.ycbcrRange = formatInfo.suggestedYcbcrRange;
metadata.xChromaOffset = formatInfo.suggestedXChromaOffset;
metadata.yChromaOffset = formatInfo.suggestedYChromaOffset;
return metadata;
}
VulkanPlatform::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
ExternalImageHandleRef externalImage) const {
auto metadata = extractExternalImageMetadata(externalImage);
VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createExternalImageData(
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, getDevice(), metadata,
memoryTypeIndex, usage);
VkResult result = vkBindImageMemory(getDevice(), data.first, data.second, 0);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
return data;
}
VkDevice const device = getDevice();
VkPhysicalDevice const physicalDevice = getPhysicalDevice();
auto buildImage = [&](ExternalImageMetadata const& metadata) {
bool const isExternal = metadata.externalFormat != 0;
VkExternalFormatANDROID const externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.pNext = nullptr,
.externalFormat = metadata.externalFormat,
};
VkExternalMemoryImageCreateInfo externalCreateInfo = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
.pNext = isExternal ? &externalFormat : nullptr,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
};
VkFormat formats[2] = {};
VkImageFormatListCreateInfo imageFormatListInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO,
.pNext = nullptr,
.viewFormatCount = 2,
.pViewFormats = formats,
};
if (fvkExternalImage->sRGB) {
formats[0] = metadata.format;
formats[1] = transformVkFormat(metadata.format, /*sRGB=*/false);
imageFormatListInfo.pNext = externalCreateInfo.pNext;
externalCreateInfo.pNext = &imageFormatListInfo;
}
VkImageCreateInfo const imageInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = &externalCreateInfo,
.flags = fvkExternalImage->sRGB ? VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT : 0u,
.imageType = VK_IMAGE_TYPE_2D,
// For non external images, use the same format as the AHB, which isn't in SRGB
// Fix VUID-VkMemoryAllocateInfo-pNext-02387
.format = transformVkFormat(metadata.format, /*sRGB=*/false),
.extent = {
metadata.width,
metadata.height,
1u,
},
.mipLevels = 1,
.arrayLayers = metadata.layers,
.samples = metadata.samples,
.usage = metadata.usage,
};
VkImage image;
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &image);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
return image;
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle){
VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.externalFormat = internalFormat,
};
auto allocMem = [&](VkImage image, ExternalImageMetadata const& metadata) {
bool const isExternal = metadata.externalFormat != 0;
// Allocate the memory
VkImportAndroidHardwareBufferInfoANDROID const androidHardwareBufferInfo = {
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
.buffer = buffer,
};
VkMemoryDedicatedAllocateInfo const memoryDedicatedAllocateInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
.pNext = &androidHardwareBufferInfo,
.image = image,
.buffer = VK_NULL_HANDLE,
};
VkPhysicalDeviceMemoryProperties memoryProperties;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
VkMemoryPropertyFlags requiredMemoryFlags =
!isExternal && any(metadata.filamentUsage & TextureUsage::UPLOADABLE)
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.pNext = &externalFormat,
.format = VK_FORMAT_UNDEFINED,
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
.components = fvkutils::getSwizzleMap(swizzleArray),
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
nullptr, &conversion);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create Ycbcr Conversion."
<< " error=" << static_cast<int32_t>(result);
if (any(metadata.filamentUsage & TextureUsage::PROTECTED)) {
requiredMemoryFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
}
uint32_t const memoryTypeIndex = VulkanContext::selectMemoryType(memoryProperties,
metadata.memoryTypeBits, requiredMemoryFlags);
VkMemoryAllocateInfo const allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &memoryDedicatedAllocateInfo,
.allocationSize = metadata.allocationSize,
.memoryTypeIndex = memoryTypeIndex,
};
VkDeviceMemory memory;
VkResult result = vkAllocateMemory(device, &allocInfo, VKALLOC, &memory);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
result = vkBindImageMemory(getDevice(), image, memory, 0);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
return memory;
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.conversion = conversion,
};
VulkanPlatform::ImageData::Bundle internal = {}, external = {};
auto img = buildImage(metadata);
auto mem = allocMem(img, metadata);
// Note that we're always choosing a non-externally sampled format if it exists.
if (metadata.externalFormat == 0) {
internal = { img, mem };
} else {
external = { img, mem };
}
return {
.internal = internal,
.external = external,
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = &samplerYcbcrConversionInfo,
.flags = 0,
.image = image,
.viewType = viewType,
.format = VK_FORMAT_UNDEFINED,
.components = swizzle,
.subresourceRange = range,
};
VkImageView imageView;
result = vkCreateImageView(device, &viewInfo, VKALLOC, &imageView);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create VkImageView."
<< " error=" << static_cast<int32_t>(result);
return imageView;
}
VkSampler VulkanPlatform::createExternalSamplerImpl(
VkDevice device, SamplerYcbcrConversion chroma, SamplerParams params,
uint32_t internalFormat) {
VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.externalFormat = internalFormat,
};
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.pNext = &externalFormat,
.format = VK_FORMAT_UNDEFINED,
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
.components = fvkutils::getSwizzleMap(swizzleArray),
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
nullptr, &conversion);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create Ycbcr Conversion."
<< " error=" << static_cast<int32_t>(result);
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.pNext = nullptr,
.conversion = conversion,
};
VkSamplerCreateInfo samplerInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = &samplerYcbcrConversionInfo,
.magFilter = fvkutils::getFilter(params.filterMag),
.minFilter = fvkutils::getFilter(params.filterMin),
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
.addressModeU = fvkutils::getWrapMode(params.wrapS),
.addressModeV = fvkutils::getWrapMode(params.wrapT),
.addressModeW = fvkutils::getWrapMode(params.wrapR),
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
.compareOp = fvkutils::getCompareOp(params.compareFunc),
.minLod = 0.0f,
.maxLod = fvkutils::getMaxLod(params.filterMin),
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
};
VkSampler sampler;
result = vkCreateSampler(device, &samplerInfo, VKALLOC, &sampler);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create sampler."
<< " error=" << static_cast<int32_t>(result);
return sampler;
}
VulkanPlatform::ExtensionSet VulkanPlatformAndroid::getSwapchainInstanceExtensions() const {
@@ -400,7 +413,7 @@ VulkanPlatform::SurfaceBundle VulkanPlatformAndroid::createVkSurfaceKHR(void* na
VkSurfaceKHR surface;
VkExtent2D extent;
VkAndroidSurfaceCreateInfoKHR const createInfo = {
VkAndroidSurfaceCreateInfoKHR const createInfo{
.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
.window = (ANativeWindow*) nativeWindow,
};
@@ -414,9 +427,20 @@ VulkanPlatform::SurfaceBundle VulkanPlatformAndroid::createVkSurfaceKHR(void* na
// Deprecated platform dependent helper methods
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() { return {}; }
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
return ExternalImageMetadata{};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return ImageData{};
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
return SurfaceBundle{};
}
} // namespace filament::backend
}// namespace filament::backend

View File

@@ -43,6 +43,30 @@ VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl(
return ret;
}
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
return {};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return {};
}
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
SamplerYcbcrConversion chroma,
SamplerParams sampler,
uint32_t internalFormat) {
return VK_NULL_HANDLE;
}
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
return VK_NULL_HANDLE;
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
VkSurfaceKHR surface;

View File

@@ -84,6 +84,30 @@ using namespace bluevk;
namespace filament::backend {
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
return {};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return {};
}
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
SamplerYcbcrConversion chroma,
SamplerParams sampler,
uint32_t internalFormat) {
return VK_NULL_HANDLE;
}
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
return VK_NULL_HANDLE;
}
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
VulkanPlatform::ExtensionSet const ret = {
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)

View File

@@ -51,8 +51,6 @@ VkFormat getVkFormat(ElementType type, bool normalized, bool integer) {
return VK_FORMAT_UNDEFINED;
}
}
// Non-normalized case
switch (type) {
// Single Component Types
case ElementType::BYTE: return integer ? VK_FORMAT_R8_SINT : VK_FORMAT_R8_SSCALED;

View File

@@ -184,46 +184,6 @@ bool isVkStencilFormat(VkFormat format) {
return (getImageAspect(format) & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;
}
bool isVKYcbcrConversionFormat(VkFormat format) {
switch (format) {
case VK_FORMAT_G8B8G8R8_422_UNORM:
case VK_FORMAT_B8G8R8G8_422_UNORM:
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
case VK_FORMAT_G16B16G16R16_422_UNORM:
case VK_FORMAT_B16G16R16G16_422_UNORM:
case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
case VK_FORMAT_G8_B8R8_2PLANE_444_UNORM:
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16:
case VK_FORMAT_G16_B16R16_2PLANE_444_UNORM:
return true;
default:
return false;
}
}
static uint32_t mostSignificantBit(uint32_t x) { return 1ul << (31ul - utils::clz(x)); }
uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {

View File

@@ -98,8 +98,6 @@ bool isVkDepthFormat(VkFormat format);
bool isVkStencilFormat(VkFormat format);
bool isVKYcbcrConversionFormat(VkFormat format);
VkImageAspectFlags getImageAspect(VkFormat format);
uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask);

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -16,8 +16,8 @@
#include "webgpu/WebGPUDriver.h"
#include "WebGPUPipelineCreation.h"
#include "WebGPUSwapChain.h"
#include "webgpu/WebGPUConstants.h"
#include <backend/platforms/WebGPUPlatform.h>
#include "CommandStreamDispatcher.h"
@@ -25,20 +25,18 @@
#include "private/backend/Dispatcher.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/TargetBufferInfo.h>
#include <math/mat3.h>
#include <utils/CString.h>
#include <utils/Panic.h>
#include <utils/ostream.h>
#include <dawn/webgpu_cpp_print.h>
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <sstream>
#include <string_view>
#include <utility>
@@ -186,6 +184,8 @@ void printAdapterDetails(wgpu::Adapter const& adapter) {
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printDeviceDetails(wgpu::Device const& device) {
wgpu::SupportedFeatures supportedFeatures{};
@@ -237,7 +237,6 @@ WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfi
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printDeviceDetails(mDevice);
#endif
mQueue = mDevice.GetQueue();
}
WebGPUDriver::~WebGPUDriver() noexcept = default;
@@ -320,9 +319,6 @@ void WebGPUDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
}
void WebGPUDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
if (boh) {
destructHandle<WGPUBufferObject>(boh);
}
}
void WebGPUDriver::destroyTexture(Handle<HwTexture> th) {
@@ -357,9 +353,6 @@ void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh)
}
void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
if (tqh) {
destructHandle<WebGPUDescriptorSet>(tqh);
}
}
Handle<HwSwapChain> WebGPUDriver::createSwapChainS() noexcept {
@@ -374,7 +367,9 @@ Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
return allocHandle<WGPUTexture>();
}
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept { return allocHandle<WGPUTexture>(); }
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
return allocHandle<WGPUProgram>();
@@ -393,7 +388,7 @@ Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
}
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
return allocHandle<WGPUTexture>();
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
@@ -409,7 +404,7 @@ Handle<HwVertexBuffer> WebGPUDriver::createVertexBufferS() noexcept {
}
Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
return allocHandle<WebGPUDescriptorSet>();
return Handle<HwDescriptorSet>((Handle<HwDescriptorSet>::HandleId) mNextFakeHandle++);
}
Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
@@ -421,7 +416,7 @@ Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
}
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
return allocHandle<WGPUTexture>();
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
@@ -433,15 +428,15 @@ Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcep
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
return allocHandle<WGPUTexture>();
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImage2S() noexcept {
return allocHandle<WGPUTexture>();
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
return allocHandle<WGPUTexture>();
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
@@ -449,6 +444,7 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
assert_invariant(!mSwapChain);
wgpu::Surface surface = mPlatform.createSurface(nativeWindow, flags);
mQueue = mDevice.GetQueue();
wgpu::Extent2D surfaceSize = mPlatform.getSurfaceExtent(mNativeWindow);
mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter,
mDevice, flags);
@@ -471,82 +467,46 @@ void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
uint32_t height, uint64_t flags) {}
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {
constructHandle<WGPUVertexBufferInfo>(vbih, bufferCount, attributeCount, attributes);
}
uint8_t attributeCount, AttributeArray attributes) {}
void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih) {
auto* vertexBufferInfo = handleCast<WGPUVertexBufferInfo>(vbih);
constructHandle<WGPUVertexBuffer>(vbh, mDevice, vertexCount, vertexBufferInfo->bufferCount,
vbih);
}
Handle<HwVertexBufferInfo> vbih) {}
void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {
auto elementSize = static_cast<uint8_t>(getElementTypeSize(elementType));
constructHandle<WGPUIndexBuffer>(ibh, mDevice, elementSize, indexCount);
}
uint32_t indexCount, BufferUsage usage) {}
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
constructHandle<WGPUBufferObject>(boh, mDevice, bindingType, byteCount);
}
BufferObjectBinding bindingType, BufferUsage usage) {}
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) {
constructHandle<WGPUTexture>(th, target, levels, format, samples, w, h, depth, usage, mDevice);
}
TextureUsage usage) {}
void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
uint8_t baseLevel, uint8_t levelCount) {
auto source = handleCast<WGPUTexture>(srch);
constructHandle<WGPUTexture>(th, source, baseLevel, levelCount);
}
uint8_t baseLevel, uint8_t levelCount) {}
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
backend::TextureSwizzle a) {
PANIC_POSTCONDITION("Swizzle WebGPU Texture is not supported");
}
backend::TextureSwizzle a) {}
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage) {
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
Platform::ExternalImageHandleRef externalImage) {}
void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* externalImage) {
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
void* externalImage) {}
void WebGPUDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane) {
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
void* image, uint32_t plane) {}
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) {
PANIC_POSTCONDITION("Import WebGPU Texture is not supported");
}
uint32_t depth, TextureUsage usage) {}
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {
assert_invariant(mDevice);
auto* renderPrimitive = constructHandle<WGPURenderPrimitive>(rph);
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
auto* indexBuffer = handleCast<WGPUIndexBuffer>(ibh);
renderPrimitive->vertexBuffer = vertexBuffer;
renderPrimitive->indexBuffer = indexBuffer;
renderPrimitive->type = pt;
}
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {}
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
constructHandle<WGPUProgram>(ph, mDevice, program);
@@ -572,10 +532,7 @@ void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh
}
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
Handle<HwDescriptorSetLayout> dslh) {
auto layout = handleCast<WebGPUDescriptorSetLayout>(dslh);
constructHandle<WebGPUDescriptorSet>(dsh, layout->getLayout(), layout->getLayoutSize());
}
Handle<HwDescriptorSetLayout> dslh) {}
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream) {
return {};
@@ -611,11 +568,11 @@ 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 WGPUTexture::fToWGPUTextureFormat(format) != wgpu::TextureFormat::Undefined;
return true;
}
bool WebGPUDriver::isTextureSwizzleSupported() {
return false;
return true;
}
bool WebGPUDriver::isTextureFormatMipmappable(TextureFormat format) {
@@ -704,21 +661,20 @@ size_t WebGPUDriver::getMaxArrayTextureLayers() {
void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
uint32_t byteOffset) {
updateGPUBuffer(handleCast<WGPUIndexBuffer>(ibh), std::move(p), byteOffset);
scheduleDestroy(std::move(p));
}
void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> ibh, BufferDescriptor&& p,
uint32_t byteOffset) {
updateGPUBuffer(handleCast<WGPUBufferObject>(ibh), std::move(p), byteOffset);
scheduleDestroy(std::move(p));
}
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> ibh,
BufferDescriptor&& p, uint32_t byteOffset) {
updateGPUBuffer(handleCast<WGPUBufferObject>(ibh), std::move(p), byteOffset);
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> ibh, BufferDescriptor&& p,
uint32_t byteOffset) {
scheduleDestroy(std::move(p));
}
void WebGPUDriver::resetBufferObject(Handle<HwBufferObject> boh) {
// Is there something that needs to be done here? Vulkan has left it unimplemented.
}
void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
@@ -726,8 +682,7 @@ void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t in
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
auto* bufferObject = handleCast<WGPUBufferObject>(boh);
assert_invariant(index < vertexBuffer->buffers.size());
assert_invariant(bufferObject->buffer.GetUsage() & wgpu::BufferUsage::Vertex);
vertexBuffer->buffers[index] = bufferObject->buffer;
vertexBuffer->setBuffer(bufferObject, index);
}
void WebGPUDriver::update3DImage(Handle<HwTexture> th,
@@ -765,7 +720,6 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
};
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
assert_invariant(mCommandEncoder);
// TODO: Remove this code once WebGPU pipeline is implemented
static float red = 1.0f;
if (red - 0.01 > 0) {
@@ -876,60 +830,9 @@ 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) {
auto* renderPrimitive = handleCast<WGPURenderPrimitive>(rph);
// This *must* match the WGPUVertexBufferInfo that was bound in bindPipeline(). But we want
// to allow to call this before bindPipeline(), so the validation can only happen in draw()
auto vbi = handleCast<WGPUVertexBufferInfo>(renderPrimitive->vertexBuffer->vbih);
assert_invariant(
vbi->getVertexBufferLayoutSize() == renderPrimitive->vertexBuffer->buffers.size());
for (uint32_t i = 0; i < vbi->getVertexBufferLayoutSize(); i++) {
mRenderPassEncoder.SetVertexBuffer(i, renderPrimitive->vertexBuffer->buffers[i]);
}
mRenderPassEncoder.SetIndexBuffer(renderPrimitive->indexBuffer->buffer,
renderPrimitive->indexBuffer->indexFormat);
}
void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
@@ -955,48 +858,25 @@ void WebGPUDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
void WebGPUDriver::resetState(int) {
}
void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> dsh,
backend::descriptor_binding_t binding, Handle<HwBufferObject> boh, uint32_t offset,
void WebGPUDriver::updateDescriptorSetBuffer(
Handle<HwDescriptorSet> dsh,
backend::descriptor_binding_t binding,
Handle<HwBufferObject> boh,
uint32_t offset,
uint32_t size) {
auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
auto buffer = handleCast<WGPUBufferObject>(boh);
if (!bindGroup->getIsLocked()) {
// TODO making assumptions that size and offset mean the same thing here.
wgpu::BindGroupEntry entry{ .binding = static_cast<uint32_t>(binding * 2),
.buffer = buffer->buffer,
.offset = offset,
.size = size };
bindGroup->addEntry(entry.binding, std::move(entry));
}
}
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()) {
// 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 };
bindGroup->addEntry(tEntry.binding, std::move(tEntry));
wgpu::BindGroupEntry sEntry{ .binding = static_cast<uint32_t>(binding * 2 + 1),
.sampler = texture->sampler };
bindGroup->addEntry(sEntry.binding, std::move(sEntry));
}
//TODO Just the setup, this function stilll needs the rest of logic implemented
*/
void WebGPUDriver::updateDescriptorSetTexture(
Handle<HwDescriptorSet> dsh,
backend::descriptor_binding_t binding,
Handle<HwTexture> th,
SamplerParams params) {
}
void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh, backend::descriptor_set_t set,
void WebGPUDriver::bindDescriptorSet(
Handle<HwDescriptorSet> dsh,
backend::descriptor_set_t set,
backend::DescriptorSetOffsetArray&& offsets) {
auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
// TODO: presume we need this, use it. Probably Encoder::SetBindGroup
auto wbg = bindGroup->lockAndReturn(mDevice);
}
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {

View File

@@ -18,7 +18,6 @@
#define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#include "WebGPUHandles.h"
#include "webgpu/WebGPUConstants.h"
#include <backend/platforms/WebGPUPlatform.h>
#include "DriverBase.h"
@@ -35,7 +34,7 @@
#include <memory>
#ifndef FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB
#define FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB 8
# define FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB 8
#endif
namespace filament::backend {
@@ -57,27 +56,6 @@ private:
[[nodiscard]] ShaderModel getShaderModel() const noexcept final;
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
template<typename GPUBufferObject>
void updateGPUBuffer(GPUBufferObject* gpuBufferObject, BufferDescriptor&& bufferDescriptor,
uint32_t byteOffset) {
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer)
<< "copyIntoBuffer called with a null buffer";
FILAMENT_CHECK_PRECONDITION(
bufferDescriptor.size + byteOffset <= gpuBufferObject->buffer.GetSize())
<< "Attempting to copy " << bufferDescriptor.size << " bytes into a buffer of size "
<< gpuBufferObject->buffer.GetSize() << " at offset " << byteOffset;
// TODO: All buffer objects are created with CopyDst usage.
// This may have some performance implications. That should be investigated later.
assert_invariant(gpuBufferObject->buffer.GetUsage() & wgpu::BufferUsage::CopyDst);
// WriteBuffer is an async call. But cpu buffer data is already written to the staging
// buffer on return from the WriteBuffer.
mQueue.WriteBuffer(gpuBufferObject->buffer, byteOffset, bufferDescriptor.buffer,
bufferDescriptor.size);
scheduleDestroy(std::move(bufferDescriptor));
}
// the platform (e.g. OS) specific aspects of the WebGPU backend are strictly only
// handled in the WebGPUPlatform
WebGPUPlatform& mPlatform;
@@ -121,8 +99,8 @@ private:
return mHandleAllocator.allocate<D>();
}
template<typename D, typename B, typename... ARGS>
D* constructHandle(Handle<B>& handle, ARGS&&... args) noexcept {
template<typename D, typename B, typename ... ARGS>
D* constructHandle(Handle<B>& handle, ARGS&& ... args) noexcept {
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
}
@@ -134,7 +112,7 @@ private:
template<typename D, typename B>
void destructHandle(Handle<B>& handle) noexcept {
auto* p = mHandleAllocator.handle_cast<D*>(handle);
mHandleAllocator.deallocate(handle, p);
return mHandleAllocator.deallocate(handle, p);
}
};

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2025 The Android Open Source Project
* 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.
@@ -19,159 +19,46 @@
#include <utility>
namespace {
constexpr wgpu::BufferUsage getBufferObjectUsage(
filament::backend::BufferObjectBinding bindingType) noexcept {
switch (bindingType) {
case filament::backend::BufferObjectBinding::VERTEX:
return wgpu::BufferUsage::Vertex;
case filament::backend::BufferObjectBinding::UNIFORM:
return wgpu::BufferUsage::Uniform;
case filament::backend::BufferObjectBinding::SHADER_STORAGE:
return wgpu::BufferUsage::Storage;
}
}
wgpu::Buffer createBuffer(wgpu::Device const& device, wgpu::BufferUsage usage, uint32_t size,
char const* label) {
wgpu::BufferDescriptor descriptor{ .label = label,
.usage = usage,
.size = size,
wgpu::Buffer createIndexBuffer(wgpu::Device const& device, uint8_t elementSize, uint32_t indexCount) {
wgpu::BufferDescriptor descriptor{ .label = "index_buffer",
.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Index,
.size = elementSize * indexCount,
.mappedAtCreation = false };
return device.CreateBuffer(&descriptor);
}
wgpu::VertexFormat getVertexFormat(filament::backend::ElementType type, bool normalized, bool integer) {
using ElementType = filament::backend::ElementType;
using VertexFormat = wgpu::VertexFormat;
if (normalized) {
switch (type) {
// Single Component Types
case ElementType::BYTE: return VertexFormat::Snorm8;
case ElementType::UBYTE: return VertexFormat::Unorm8;
case ElementType::SHORT: return VertexFormat::Snorm16;
case ElementType::USHORT: return VertexFormat::Unorm16;
// Two Component Types
case ElementType::BYTE2: return VertexFormat::Snorm8x2;
case ElementType::UBYTE2: return VertexFormat::Unorm8x2;
case ElementType::SHORT2: return VertexFormat::Snorm16x2;
case ElementType::USHORT2: return VertexFormat::Unorm16x2;
// Three Component Types
// There is no vertex format type for 3 byte data in webgpu. Use
// 4 byte signed normalized type and ignore the last byte.
// TODO: This is to be verified.
case ElementType::BYTE3: return VertexFormat::Snorm8x4; // NOT MINSPEC
case ElementType::UBYTE3: return VertexFormat::Unorm8x4; // NOT MINSPEC
case ElementType::SHORT3: return VertexFormat::Snorm16x4; // NOT MINSPEC
case ElementType::USHORT3: return VertexFormat::Unorm16x4; // NOT MINSPEC
// Four Component Types
case ElementType::BYTE4: return VertexFormat::Snorm8x4;
case ElementType::UBYTE4: return VertexFormat::Unorm8x4;
case ElementType::SHORT4: return VertexFormat::Snorm16x4;
case ElementType::USHORT4: return VertexFormat::Unorm8x4;
default:
FILAMENT_CHECK_POSTCONDITION(false) << "Normalized format does not exist.";
return VertexFormat::Float32x3;
}
}
switch (type) {
// Single Component Types
// There is no direct alternative for SSCALED in webgpu. Convert them to Float32 directly.
// This will result in increased memory on the cpu side.
// TODO: Is Float16 acceptable instead with some potential accuracy errors?
case ElementType::BYTE: return integer ? VertexFormat::Sint8 : VertexFormat::Float32;
case ElementType::UBYTE: return integer ? VertexFormat::Uint8 : VertexFormat::Float32;
case ElementType::SHORT: return integer ? VertexFormat::Sint16 : VertexFormat::Float32;
case ElementType::USHORT: return integer ? VertexFormat::Uint16 : VertexFormat::Float32;
case ElementType::HALF: return VertexFormat::Float16;
case ElementType::INT: return VertexFormat::Sint32;
case ElementType::UINT: return VertexFormat::Uint32;
case ElementType::FLOAT: return VertexFormat::Float32;
// Two Component Types
case ElementType::BYTE2: return integer ? VertexFormat::Sint8x2 : VertexFormat::Float32x2;
case ElementType::UBYTE2: return integer ? VertexFormat::Uint8x2 : VertexFormat::Float32x2;
case ElementType::SHORT2: return integer ? VertexFormat::Sint16x2 : VertexFormat::Float32x2;
case ElementType::USHORT2: return integer ? VertexFormat::Uint16x2 : VertexFormat::Float32x2;
case ElementType::HALF2: return VertexFormat::Float16x2;
case ElementType::FLOAT2: return VertexFormat::Float32x2;
// Three Component Types
case ElementType::BYTE3: return VertexFormat::Sint8x4; // NOT MINSPEC
case ElementType::UBYTE3: return VertexFormat::Uint8x4; // NOT MINSPEC
case ElementType::SHORT3: return VertexFormat::Sint16x4; // NOT MINSPEC
case ElementType::USHORT3: return VertexFormat::Uint16x4; // NOT MINSPEC
case ElementType::HALF3: return VertexFormat::Float16x4; // NOT MINSPEC
case ElementType::FLOAT3: return VertexFormat::Float32x3;
// Four Component Types
case ElementType::BYTE4: return integer ? VertexFormat::Sint8x4 : VertexFormat::Float32x4;
case ElementType::UBYTE4: return integer ? VertexFormat::Uint8x4 : VertexFormat::Float32x4;
case ElementType::SHORT4: return integer ? VertexFormat::Sint16x4 : VertexFormat::Float32x4;
case ElementType::USHORT4: return integer ? VertexFormat::Uint16x4 : VertexFormat::Float32x4;
case ElementType::HALF4: return VertexFormat::Float16x4;
case ElementType::FLOAT4: return VertexFormat::Float32x4;
}
}
}// namespace
} // namespace
namespace filament::backend {
WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
mVertexBufferLayout(bufferCount),
mAttributes(bufferCount) {
assert_invariant(attributeCount > 0);
assert_invariant(bufferCount > 0);
for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) {
Attribute const& attrib = attributes[attribIndex];
// Ignore the attributes which are not bind to vertex buffers.
if (attrib.buffer == Attribute::BUFFER_UNUSED) {
continue;
}
assert_invariant(attrib.buffer < bufferCount);
bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET;
bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED;
wgpu::VertexFormat vertexFormat = getVertexFormat(attrib.type, isNormalized, isInteger);
// Attributes are sequential per buffer
mAttributes[attrib.buffer].push_back({
.format = vertexFormat,
.offset = attrib.offset,
.shaderLocation = static_cast<uint32_t>(mAttributes[attrib.buffer].size()),
});
mVertexBufferLayout[attrib.buffer].stepMode = wgpu::VertexStepMode::Vertex;
if (mVertexBufferLayout[attrib.buffer].arrayStride == 0) {
mVertexBufferLayout[attrib.buffer].arrayStride = attrib.stride;
} else {
assert_invariant(mVertexBufferLayout[attrib.buffer].arrayStride == attrib.stride);
}
}
for (uint32_t bufferIndex = 0; bufferIndex < bufferCount; bufferIndex++) {
mVertexBufferLayout[bufferIndex].attributeCount = mAttributes[bufferIndex].size();
mVertexBufferLayout[bufferIndex].attributes = mAttributes[bufferIndex].data();
}
}
WGPUIndexBuffer::WGPUIndexBuffer(wgpu::Device const& device, uint8_t elementSize,
uint32_t indexCount)
: buffer(createBuffer(device, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Index,
elementSize * indexCount, "index_buffer")),
indexFormat(elementSize == 2 ? wgpu::IndexFormat::Uint16 : wgpu::IndexFormat::Uint32) {}
: buffer(createIndexBuffer(device, elementSize, indexCount)) {}
WGPUVertexBuffer::WGPUVertexBuffer(wgpu::Device const& device, uint32_t vertexCount,
uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount),
vbih(vbih),
buffers(bufferCount) {}
WGPUVertexBuffer::WGPUVertexBuffer(wgpu::Device const &device, uint32_t vextexCount, uint32_t bufferCount,
Handle<WGPUVertexBufferInfo> vbih)
: HwVertexBuffer(vextexCount),
vbih(vbih),
buffers(bufferCount) {
wgpu::BufferDescriptor descriptor {
.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Vertex,
.size = vextexCount * bufferCount,
.mappedAtCreation = false };
WGPUBufferObject::WGPUBufferObject(wgpu::Device const& device, BufferObjectBinding bindingType,
uint32_t byteCount)
for (uint32_t i = 0; i < bufferCount; ++i) {
descriptor.label = ("vertex_buffer_" + std::to_string(i)).c_str();
buffers[i] = device.CreateBuffer(&descriptor);
}
}
// TODO: Empty function is a place holder for verxtex buffer updates and should be
// updated for that purpose.
void WGPUVertexBuffer::setBuffer(WGPUBufferObject* bufferObject, uint32_t index) {}
WGPUBufferObject::WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount)
: HwBufferObject(byteCount),
buffer(createBuffer(device, wgpu::BufferUsage::CopyDst | getBufferObjectUsage(bindingType),
byteCount, "buffer_object")),
bufferObjectBinding(bindingType) {}
wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStageFlags fFlags) {
@@ -259,397 +146,7 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
.entries = wEntries.data()
};
// TODO Do we need to defer this until we have more info on textures and samplers??
mLayoutSize = wEntries.size();
mLayout = device.CreateBindGroupLayout(&layoutDescriptor);
}
WebGPUDescriptorSetLayout::~WebGPUDescriptorSetLayout() {}
WebGPUDescriptorSet::WebGPUDescriptorSet(const wgpu::BindGroupLayout& layout, uint layoutSize)
: mLayout(layout),
entries(layoutSize, wgpu::BindGroupEntry{}) {
// Establish the size of entries based on the layout. This should be reliable and efficient.
}
WebGPUDescriptorSet::~WebGPUDescriptorSet() {}
wgpu::BindGroup WebGPUDescriptorSet::lockAndReturn(const wgpu::Device& device) {
if (mBindGroup) {
return mBindGroup;
}
// TODO label? Should we just copy layout label?
wgpu::BindGroupDescriptor desc{ .layout = mLayout,
.entryCount = entries.size(),
.entries = entries.data() };
mBindGroup = device.CreateBindGroup(&desc);
return mBindGroup;
}
void WebGPUDescriptorSet::addEntry(uint index, wgpu::BindGroupEntry&& entry) {
if (mBindGroup) {
// We will keep getting hits from future updates, but shouldn't do anything
// Filament guarantees this won't change after things have locked.
return;
}
// 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);
}
// 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

View File

@@ -43,44 +43,23 @@ public:
};
struct WGPUBufferObject;
// VertexBufferInfo contains layout info for Vertex Buffer based on WebGPU structs. In WebGPU each
// VertexBufferLayout is associated with a single vertex buffer. So number of mVertexBufferLayout
// is equal to bufferCount. Each VertexBufferLayout can contain multiple VertexAttribute. Bind index
// of vertex buffer is implicitly calculated by the position of VertexBufferLayout in an array.
class WGPUVertexBufferInfo : public HwVertexBufferInfo {
public:
// TODO: Currently WGPUVertexBufferInfo is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
struct WGPUVertexBufferInfo : public HwVertexBufferInfo {
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes);
inline wgpu::VertexBufferLayout const* getVertexBufferLayout() const {
return mVertexBufferLayout.data();
}
inline uint32_t getVertexBufferLayoutSize() const {
return mVertexBufferLayout.size();
}
inline wgpu::VertexAttribute const* getVertexAttributeForIndex(uint32_t index) const {
assert_invariant(index < mAttributes.size());
return mAttributes[index].data();
}
inline uint32_t getVertexAttributeSize(uint32_t index) const {
assert_invariant(index < mAttributes.size());
return mAttributes[index].size();
}
private:
// TODO: can we do better in terms on heap management.
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout {};
std::vector<std::vector<wgpu::VertexAttribute>> mAttributes {};
AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
attributes(attributes) {}
AttributeArray attributes;
};
struct WGPUVertexBuffer : public HwVertexBuffer {
WGPUVertexBuffer(wgpu::Device const &device, uint32_t vertexCount, uint32_t bufferCount,
Handle<HwVertexBufferInfo> vbih);
WGPUVertexBuffer(wgpu::Device const &device, uint32_t vextexCount, uint32_t bufferCount,
Handle<WGPUVertexBufferInfo> vbih);
Handle<HwVertexBufferInfo> vbih;
void setBuffer(WGPUBufferObject *bufferObject, uint32_t index);
Handle<WGPUVertexBufferInfo> vbih;
utils::FixedCapacityVector<wgpu::Buffer> buffers;
};
@@ -89,79 +68,43 @@ struct WGPUIndexBuffer : public HwIndexBuffer {
uint32_t indexCount);
wgpu::Buffer buffer;
wgpu::IndexFormat indexFormat;
};
// TODO: Currently WGPUVertexBufferInfo is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
struct WGPUBufferObject : HwBufferObject {
WGPUBufferObject(wgpu::Device const &device, BufferObjectBinding bindingType, uint32_t byteCount);
WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount);
wgpu::Buffer buffer = nullptr;
wgpu::Buffer buffer;
const BufferObjectBinding bufferObjectBinding;
};
class WebGPUDescriptorSetLayout final : public HwDescriptorSetLayout {
class WebGPUDescriptorSetLayout : public HwDescriptorSetLayout {
public:
WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout, wgpu::Device const& device);
~WebGPUDescriptorSetLayout();
[[nodiscard]] const wgpu::BindGroupLayout& getLayout() const { return mLayout; }
[[nodiscard]] uint getLayoutSize() const { return mLayoutSize; }
private:
// TODO: If this is useful elsewhere, remove it from this class
// Convert Filament Shader Stage Flags bitmask to webgpu equivilant
static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags);
uint mLayoutSize;
wgpu::BindGroupLayout mLayout;
};
class WebGPUDescriptorSet final : public HwDescriptorSet {
public:
WebGPUDescriptorSet(const wgpu::BindGroupLayout& layout, uint layoutSize);
~WebGPUDescriptorSet();
wgpu::BindGroup lockAndReturn(wgpu::Device const& device);
void addEntry(uint index, wgpu::BindGroupEntry&& entry);
[[nodiscard]] bool getIsLocked() const { return mBindGroup != nullptr; }
private:
// TODO: Consider storing what we used to make the layout. However we need to essentially
// Recreate some of the info (Sampler in slot X with the actual sampler) so letting Dawn confirm
// there isn't a mismatch may be easiest.
// Also storing the wgpu ObjectBase takes care of ownership challenges in theory
wgpu::BindGroupLayout mLayout;
std::vector<wgpu::BindGroupEntry> entries;
wgpu::BindGroup mBindGroup;
};
class WGPUTexture : public HwTexture {
public:
// TODO: Currently WGPUTexture is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
struct WGPUTexture : public HwTexture {
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;
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage) noexcept;
WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
// constructors for creating texture views
WGPUTexture(WGPUTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
const wgpu::Texture& getTexture() const { return texture; }
const wgpu::Sampler& getSampler() const { return sampler; }
const wgpu::TextureView& getTexView() const { return texView; }
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 {
WGPURenderPrimitive() {}
WGPURenderPrimitive();
void setBuffers(WGPUVertexBufferInfo const* const vbi,
WGPUVertexBuffer* vertexBuffer, WGPUIndexBuffer* indexBuffer);
@@ -181,7 +124,7 @@ struct WGPURenderTarget : public HwRenderTarget {
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
: level(level),
layer(layer),
texture(gpuTexture->getTexture()),
texture(gpuTexture->texture),
mWGPUTexture(gpuTexture) {}
uint8_t level = 0;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 B

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

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