From c76f67c139b39f7c277a8933f8e0b13caa5f7e69 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Sat, 11 May 2024 23:41:10 -0700 Subject: [PATCH 01/23] fix typo checking FILAMENT_ENABLE_MATDBG this macro must be checked with #if not #ifdef --- filament/backend/src/opengl/OpenGLDriver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index e70cba339a1..77fec7d41db 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -3888,7 +3888,7 @@ void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins #endif } -#ifdef FILAMENT_ENABLE_MATDBG +#if FILAMENT_ENABLE_MATDBG CHECK_GL_ERROR_NON_FATAL(utils::slog.e) #else CHECK_GL_ERROR(utils::slog.e) @@ -3934,7 +3934,7 @@ void OpenGLDriver::dispatchCompute(Handle program, math::uint3 workGr glDispatchCompute(workGroupCount.x, workGroupCount.y, workGroupCount.z); #endif // BACKEND_OPENGL_LEVEL_GLES31 -#ifdef FILAMENT_ENABLE_MATDBG +#if FILAMENT_ENABLE_MATDBG CHECK_GL_ERROR_NON_FATAL(utils::slog.e) #else CHECK_GL_ERROR(utils::slog.e) From 6dd6db89d5c47938186650668c400cd2ca540225 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 13 May 2024 17:28:22 -0700 Subject: [PATCH 02/23] align android libraries to 16 KiB --- CMakeLists.txt | 7 ++++++- android/filamat-android/CMakeLists.txt | 1 + android/filament-android/CMakeLists.txt | 1 + android/filament-utils-android/CMakeLists.txt | 1 + android/gltfio-android/CMakeLists.txt | 1 + 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 533abf6a49c..7a68864aa8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -451,8 +451,13 @@ endif() if (NOT WEBGL) set(GC_SECTIONS "-Wl,--gc-sections") endif() + set(B_SYMBOLIC_FUNCTIONS "-Wl,-Bsymbolic-functions") +if (ANDROID) +set(BINARY_ALIGNMENT "-Wl,-z,max-page-size=16384") +endif() + if (APPLE) set(GC_SECTIONS "-Wl,-dead_strip") set(B_SYMBOLIC_FUNCTIONS "") @@ -466,7 +471,7 @@ if (APPLE) endif() set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GC_SECTIONS}") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GC_SECTIONS} ${B_SYMBOLIC_FUNCTIONS}") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GC_SECTIONS} ${B_SYMBOLIC_FUNCTIONS} ${BINARY_ALIGNMENT}") if (WEBGL_PTHREADS) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pthread") diff --git a/android/filamat-android/CMakeLists.txt b/android/filamat-android/CMakeLists.txt index 283610e3b14..3b7bc49c762 100644 --- a/android/filamat-android/CMakeLists.txt +++ b/android/filamat-android/CMakeLists.txt @@ -38,6 +38,7 @@ set(FILAMAT_INCLUDE_DIRS include_directories(${FILAMENT_DIR}/include) set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${CMAKE_SOURCE_DIR}/libfilamat-jni.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384") add_library(filamat-jni SHARED src/main/cpp/MaterialBuilder.cpp) target_include_directories(filamat-jni PRIVATE ${FILAMAT_INCLUDE_DIRS}) diff --git a/android/filament-android/CMakeLists.txt b/android/filament-android/CMakeLists.txt index f2a12683ff5..7f3122f6456 100644 --- a/android/filament-android/CMakeLists.txt +++ b/android/filament-android/CMakeLists.txt @@ -59,6 +59,7 @@ endif() set(VERSION_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/libfilament-jni.map") set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${VERSION_SCRIPT}") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384") add_library(filament-jni SHARED src/main/cpp/BufferObject.cpp diff --git a/android/filament-utils-android/CMakeLists.txt b/android/filament-utils-android/CMakeLists.txt index 92c4a319b34..6c77cfa2b32 100644 --- a/android/filament-utils-android/CMakeLists.txt +++ b/android/filament-utils-android/CMakeLists.txt @@ -31,6 +31,7 @@ set_target_properties(iblprefilter PROPERTIES IMPORTED_LOCATION ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilament-iblprefilter.a) set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/libfilament-utils-jni.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384") add_library(filament-utils-jni SHARED src/main/cpp/AutomationEngine.cpp diff --git a/android/gltfio-android/CMakeLists.txt b/android/gltfio-android/CMakeLists.txt index a5075fefa84..1b921d1e046 100644 --- a/android/gltfio-android/CMakeLists.txt +++ b/android/gltfio-android/CMakeLists.txt @@ -44,6 +44,7 @@ set_target_properties(uberarchive PROPERTIES IMPORTED_LOCATION ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libuberarchive.a) set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384") set(GLTFIO_SRCS ${GLTFIO_DIR}/include/gltfio/Animator.h From 85eb724a9028ddc94d55c3831ceaf240812ddf50 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 14 May 2024 10:40:54 -0700 Subject: [PATCH 03/23] Reduce explicit swiftshader paths (#7848) - Use custom ICD path to enable Swiftshader instead of specifying direct path to the lib. - Remove unused `swiftshader` directory in `build` - Remove swiftshader options in `build.sh` and cmakefiles - Change BUILD.md - Correctly handle XCB-only swapchain surface in VulkanPlatform for swiftshader. - Refactor `VulkanPlatform::ExtensionSet` so that `utils::CString` is used instead of string_view, so that we don't get into tricky lifetime issues with `const char*` --- BUILDING.md | 32 ++--- CMakeLists.txt | 26 ---- build.sh | 11 +- build/swiftshader/Dockerfile | 53 -------- build/swiftshader/gallery.py | 56 -------- build/swiftshader/patch_00.diff | 62 --------- build/swiftshader/test.sh | 127 ------------------ filament/backend/CMakeLists.txt | 2 +- .../backend/platforms/VulkanPlatform.h | 22 ++- filament/backend/src/PlatformFactory.cpp | 11 +- .../src/vulkan/platform/VulkanPlatform.cpp | 103 ++++++++------ .../VulkanPlatformAndroidLinuxWindows.cpp | 45 ++++--- .../vulkan/platform/VulkanPlatformApple.mm | 11 +- 13 files changed, 131 insertions(+), 430 deletions(-) delete mode 100644 build/swiftshader/Dockerfile delete mode 100755 build/swiftshader/gallery.py delete mode 100644 build/swiftshader/patch_00.diff delete mode 100755 build/swiftshader/test.sh diff --git a/BUILDING.md b/BUILDING.md index 28e6861caa4..9c8cd1d2b22 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -73,7 +73,6 @@ The following CMake options are boolean options specific to Filament: - `FILAMENT_SUPPORTS_VULKAN`: Include the Vulkan backend - `FILAMENT_INSTALL_BACKEND_TEST`: Install the backend test library so it can be consumed on iOS - `FILAMENT_USE_EXTERNAL_GLES3`: Experimental: Compile Filament against OpenGL ES 3 -- `FILAMENT_USE_SWIFTSHADER`: Compile Filament against SwiftShader - `FILAMENT_SKIP_SAMPLES`: Don't build sample apps To turn an option on or off: @@ -426,7 +425,7 @@ value is the desired roughness between 0 and 1. ## Generating C++ documentation -To generate the documentation you must first install `doxygen` and `graphviz`, then run the +To generate the documentation you must first install `doxygen` and `graphviz`, then run the following commands: ```shell @@ -436,32 +435,27 @@ doxygen docs/doxygen/filament.doxygen Finally simply open `docs/html/index.html` in your web browser. -## SwiftShader +## Software Rasterization -To try out Filament's Vulkan support with SwiftShader, first build SwiftShader and set the -`SWIFTSHADER_LD_LIBRARY_PATH` variable to the folder that contains `libvk_swiftshader.dylib`: +We have tested swiftshader for running software rasterization on the Vulkan backend. To use this, +please first make sure that the [Vulkan SDK](https://www.lunarg.com/vulkan-sdk/) is installed on +your machine. If you are doing a manual installation of the SDK on Linux, you will have to source +`setup-env.sh` in the SDK's root folder to make sure the Vulkan loader is the first lib loaded. + +### Swiftshader (Vulkan) [tested on macOS and Linux] + +First, build SwiftShader ```shell git clone https://github.com/google/swiftshader.git cd swiftshader/build cmake .. && make -j -export SWIFTSHADER_LD_LIBRARY_PATH=`pwd` ``` -Next, go to your Filament repo and use the [easy build](#easy-build) script with `-t`. - -## SwiftShader for CI - -Continuous testing turnaround can be quite slow if you need to build SwiftShader from scratch, so we -provide an Ubuntu-based Docker image that has it already built. The Docker image also includes -everything necessary for building Filament. You can fetch and run the image as follows: - +and then set `VK_ICD_FILENAMES` to the ICD json produced in the build. For example, ```shell -docker pull ghcr.io/filament-assets/swiftshader -docker run -it ghcr.io/filament-assets/swiftshader +export VK_ICD_FILENAMES=/Users/user/swiftshader/build/Darwin/vk_swiftshader_icd.json ``` -To do more with the container, see the helper script at `build/swiftshader/test.sh`. +Build Filament as normal and use the vulkan backend. -If you are a team member, you can update the public image to the latest SwiftShader by -following the instructions at the top of `build/swiftshader/Dockerfile`. diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a68864aa8e..7809decd280 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,8 +21,6 @@ project(TNT) # ================================================================================================== option(FILAMENT_USE_EXTERNAL_GLES3 "Experimental: Compile Filament against OpenGL ES 3" OFF) -option(FILAMENT_USE_SWIFTSHADER "Compile Filament against SwiftShader" OFF) - option(FILAMENT_ENABLE_LTO "Enable link-time optimizations if supported by the compiler" OFF) option(FILAMENT_SKIP_SAMPLES "Don't build samples" OFF) @@ -145,11 +143,6 @@ if (LINUX) add_definitions(-DFILAMENT_SUPPORTS_XCB) endif() - # Default Swiftshader build does not enable the xlib extension - if (FILAMENT_SUPPORTS_XLIB AND FILAMENT_USE_SWIFTSHADER) - set(FILAMENT_SUPPORTS_XLIB OFF) - endif() - if (FILAMENT_SUPPORTS_XLIB) add_definitions(-DFILAMENT_SUPPORTS_XLIB) endif() @@ -327,10 +320,6 @@ if (FILAMENT_SUPPORTS_EGL_ON_LINUX) set(EGL TRUE) endif() -if (FILAMENT_USE_SWIFTSHADER) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DFILAMENT_USE_SWIFTSHADER") -endif() - if (WIN32) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_USE_MATH_DEFINES=1") endif() @@ -683,21 +672,6 @@ else() set(IMPORT_EXECUTABLES ${FILAMENT}/${IMPORT_EXECUTABLES_DIR}/ImportExecutables-${CMAKE_BUILD_TYPE}.cmake) endif() -# ================================================================================================== -# Try to find Vulkan if the SDK is installed, otherwise fall back to the bundled version. -# This needs to stay in our top-level CMakeLists because it sets up variables that are used by the -# "bluevk" and "samples" targets. -# ================================================================================================== - -if (FILAMENT_USE_SWIFTSHADER) - if (NOT FILAMENT_SUPPORTS_VULKAN) - message(ERROR "SwiftShader is only useful when Vulkan is enabled.") - endif() - find_library(SWIFTSHADER_VK NAMES vk_swiftshader HINTS "$ENV{SWIFTSHADER_LD_LIBRARY_PATH}") - message(STATUS "Found SwiftShader VK library in: ${SWIFTSHADER_VK}.") - add_definitions(-DFILAMENT_VKLIBRARY_PATH=\"${SWIFTSHADER_VK}\") -endif() - # ================================================================================================== # Common Functions # ================================================================================================== diff --git a/build.sh b/build.sh index 434a0045fa3..f2c986344da 100755 --- a/build.sh +++ b/build.sh @@ -44,8 +44,6 @@ function print_help { echo " Exclude Vulkan support from the Android build." echo " -s" echo " Add iOS simulator support to the iOS build." - echo " -t" - echo " Enable SwiftShader support for Vulkan in desktop builds." echo " -e" echo " Enable EGL on Linux support for desktop builds." echo " -l" @@ -165,8 +163,6 @@ INSTALL_COMMAND= VULKAN_ANDROID_OPTION="-DFILAMENT_SUPPORTS_VULKAN=ON" VULKAN_ANDROID_GRADLE_OPTION="" -SWIFTSHADER_OPTION="-DFILAMENT_USE_SWIFTSHADER=OFF" - EGL_ON_LINUX_OPTION="-DFILAMENT_SUPPORTS_EGL_ON_LINUX=OFF" MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=OFF" @@ -233,7 +229,6 @@ function build_desktop_target { -DIMPORT_EXECUTABLES_DIR=out \ -DCMAKE_BUILD_TYPE="$1" \ -DCMAKE_INSTALL_PREFIX="../${lc_target}/filament" \ - ${SWIFTSHADER_OPTION} \ ${EGL_ON_LINUX_OPTION} \ ${MATDBG_OPTION} \ ${MATOPT_OPTION} \ @@ -794,7 +789,7 @@ function check_debug_release_build { pushd "$(dirname "$0")" > /dev/null -while getopts ":hacCfgijmp:q:uvslwtedk:bx:" opt; do +while getopts ":hacCfgijmp:q:uvslwedk:bx:" opt; do case ${opt} in h) print_help @@ -913,10 +908,6 @@ while getopts ":hacCfgijmp:q:uvslwtedk:bx:" opt; do IOS_BUILD_SIMULATOR=true echo "iOS simulator support enabled." ;; - t) - SWIFTSHADER_OPTION="-DFILAMENT_USE_SWIFTSHADER=ON" - echo "SwiftShader support enabled." - ;; e) EGL_ON_LINUX_OPTION="-DFILAMENT_SUPPORTS_EGL_ON_LINUX=ON -DFILAMENT_SKIP_SDL2=ON -DFILAMENT_SKIP_SAMPLES=ON" echo "EGL on Linux support enabled; skipping SDL2." diff --git a/build/swiftshader/Dockerfile b/build/swiftshader/Dockerfile deleted file mode 100644 index 4c0ce984fd0..00000000000 --- a/build/swiftshader/Dockerfile +++ /dev/null @@ -1,53 +0,0 @@ -# Build the image: -# docker build --no-cache --tag ssfilament -f build/swiftshader/Dockerfile . -# docker tag ssfilament ghcr.io/filament-assets/swiftshader -# -# Publish the image: -# docker login ghcr.io --username --password -# docker push ghcr.io/filament-assets/swiftshader -# -# Run the image and mount the current directory: -# docker run -it -v `pwd`:/trees/filament -t ssfilament - -FROM ubuntu:focal -WORKDIR /trees -ARG DEBIAN_FRONTEND=noninteractive -ENV SWIFTSHADER_LD_LIBRARY_PATH=/trees/swiftshader/build -ENV CXXFLAGS='-fno-builtin -Wno-pass-failed' - -RUN apt-get update && \ - apt-get --no-install-recommends install -y \ - apt-transport-https \ - apt-utils \ - build-essential \ - cmake \ - ca-certificates \ - git \ - ninja-build \ - python \ - python3 \ - xorg-dev \ - clang-7 \ - libc++-7-dev \ - libc++abi-7-dev \ - lldb - -# Ensure that clang is used instead of gcc. -RUN set -eux ;\ - update-alternatives --install /usr/bin/clang clang /usr/bin/clang-7 100 ;\ - update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-7 100 ;\ - update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 ;\ - update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 - -# Get patch files from the local Filament tree. -COPY build/swiftshader/*.diff . - -# Clone SwiftShader, apply patches, and build it. -RUN set -eux ;\ - git clone https://swiftshader.googlesource.com/SwiftShader swiftshader ;\ - cd swiftshader ;\ - git checkout 139f5c3 ;\ - git apply /trees/*.diff ;\ - cd build ;\ - cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release ;\ - ninja diff --git a/build/swiftshader/gallery.py b/build/swiftshader/gallery.py deleted file mode 100755 index 01c27cafb39..00000000000 --- a/build/swiftshader/gallery.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 - -from pathlib import Path -import os - -spath = os.path.dirname(os.path.realpath(__file__)) - -path = Path(spath) - -folder = "../../results/" - -images = list(path.glob(folder + '*.png')) - -images.sort() - -gallery = open(path.absolute().joinpath(folder + 'index.html'), 'w') - -gallery.write(""" - - - - - - - -""") - -tag = '' - -for image in images: - group = image.stem.rstrip('0123456789') - before = f'https://filament-assets.github.io/golden/{group}/{image.name}' - after = image.name - gallery.write('\n') - gallery.write(f'

{image.stem}.json

\n') - gallery.write('\n') - gallery.write(f' \n') - gallery.write('\n') - -gallery.write(""" - -""") diff --git a/build/swiftshader/patch_00.diff b/build/swiftshader/patch_00.diff deleted file mode 100644 index fbdfd646991..00000000000 --- a/build/swiftshader/patch_00.diff +++ /dev/null @@ -1,62 +0,0 @@ -diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp -index 86913ec72..3b35345af 100644 ---- a/src/Vulkan/VkPipeline.cpp -+++ b/src/Vulkan/VkPipeline.cpp -@@ -71,7 +71,56 @@ std::vector preprocessSpirv( - if(optimize) - { - // Full optimization list taken from spirv-opt. -- opt.RegisterPerformancePasses(); -+ -+ // We have removed CreateRedundancyEliminationPass because it segfaults when encountering: -+ // %389 = OpCompositeConstruct %7 %386 %387 %388 %86 -+ // When inserting an entry into instruction_to_value_ (which is an unordered_map) -+ // This could perhaps be investigated further with help from asan. -+ -+ using namespace spvtools; -+ opt.RegisterPass(CreateWrapOpKillPass()) -+ .RegisterPass(CreateDeadBranchElimPass()) -+ .RegisterPass(CreateMergeReturnPass()) -+ .RegisterPass(CreateInlineExhaustivePass()) -+ .RegisterPass(CreateAggressiveDCEPass()) -+ .RegisterPass(CreatePrivateToLocalPass()) -+ .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) -+ .RegisterPass(CreateLocalSingleStoreElimPass()) -+ .RegisterPass(CreateAggressiveDCEPass()) -+ .RegisterPass(CreateScalarReplacementPass()) -+ .RegisterPass(CreateLocalAccessChainConvertPass()) -+ .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) -+ .RegisterPass(CreateLocalSingleStoreElimPass()) -+ .RegisterPass(CreateAggressiveDCEPass()) -+ .RegisterPass(CreateLocalMultiStoreElimPass()) -+ .RegisterPass(CreateAggressiveDCEPass()) -+ .RegisterPass(CreateCCPPass()) -+ .RegisterPass(CreateAggressiveDCEPass()) -+ .RegisterPass(CreateLoopUnrollPass(true)) -+ .RegisterPass(CreateDeadBranchElimPass()) -+ .RegisterPass(CreateRedundancyEliminationPass()) // workaround for SEGFAULT -+ .RegisterPass(CreateCombineAccessChainsPass()) -+ .RegisterPass(CreateSimplificationPass()) -+ .RegisterPass(CreateScalarReplacementPass()) -+ .RegisterPass(CreateLocalAccessChainConvertPass()) -+ .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) -+ .RegisterPass(CreateLocalSingleStoreElimPass()) -+ .RegisterPass(CreateAggressiveDCEPass()) -+ .RegisterPass(CreateSSARewritePass()) -+ .RegisterPass(CreateAggressiveDCEPass()) -+ .RegisterPass(CreateVectorDCEPass()) -+ .RegisterPass(CreateDeadInsertElimPass()) -+ .RegisterPass(CreateDeadBranchElimPass()) -+ .RegisterPass(CreateSimplificationPass()) -+ .RegisterPass(CreateIfConversionPass()) -+ .RegisterPass(CreateCopyPropagateArraysPass()) -+ .RegisterPass(CreateReduceLoadSizePass()) -+ .RegisterPass(CreateAggressiveDCEPass()) -+ .RegisterPass(CreateBlockMergePass()) -+ .RegisterPass(CreateRedundancyEliminationPass()) // workaround for SEGFAULT -+ .RegisterPass(CreateDeadBranchElimPass()) -+ .RegisterPass(CreateBlockMergePass()) -+ .RegisterPass(CreateSimplificationPass()); - } - - std::vector optimized; diff --git a/build/swiftshader/test.sh b/build/swiftshader/test.sh deleted file mode 100755 index 47320e217f5..00000000000 --- a/build/swiftshader/test.sh +++ /dev/null @@ -1,127 +0,0 @@ -#!/bin/bash -set -e - -function print_help { - local self_name=$(basename "$0") - echo "This script issues docker commands for testing Filament with SwiftShader." - echo "The usual sequence of commands is: fetch, start, build filament release, and run." - echo "" - echo "Usage:" - echo " $self_name [command]" - echo "" - echo "Commands:" - echo " build filament [debug | release]" - echo " Use the container to build Filament." - echo " build swiftshader [debug | release]" - echo " Use the container to do a clean rebuild of SwiftShader." - echo " (Note that the container already has SwiftShader built.)" - echo " fetch" - echo " Download the docker image from the central repository." - echo " help" - echo " Print this help message." - echo " logs" - echo " Print messages from the container's kernel ring buffer." - echo " This is useful for diagnosing OOM issues." - echo " run [lldb]" - echo " Launch a test inside the container, optionally via lldb." - echo " shell" - echo " Interact with a bash prompt in the container." - echo " start" - echo " Start a container from the image." - echo " stop" - echo " Stop the container." - echo "" -} - -# Change the current working directory to the Filament root. -pushd "$(dirname "$0")/../.." > /dev/null - -if [[ "$1" == "build" ]] && [[ "$2" == "filament" ]]; then - docker exec runner filament/build.sh -t $3 gltf_viewer - exit $? -fi - -if [[ "$1" == "build" ]] && [[ "$2" == "swiftshader" ]]; then - BUILD_TYPE="$3" - BUILD_TYPE="$(tr '[:lower:]' '[:upper:]' <<< ${BUILD_TYPE:0:1})${BUILD_TYPE:1}" - docker exec --workdir /trees/swiftshader runner rm -rf build - docker exec --workdir /trees/swiftshader runner mkdir build - docker exec --workdir /trees/swiftshader/build runner cmake -GNinja -DCMAKE_BUILD_TYPE="$BUILD_TYPE" .. - docker exec --workdir /trees/swiftshader/build runner ninja - exit $? -fi - -if [[ "$1" == "fetch" ]]; then - docker pull ghcr.io/filament-assets/swiftshader:latest - docker tag ghcr.io/filament-assets/swiftshader:latest ssfilament - exit $? -fi - -if [[ "$1" == "help" ]]; then - print_help - exit 0 -fi - -if [[ "$1" == "logs" ]]; then - docker exec runner dmesg --human --read-clear - exit $? -fi - -if [[ "$1" == "run" ]] && [[ "$2" == "lldb" ]]; then - docker exec -i --workdir /trees/filament/results runner \ - lldb --batch -o run -o bt -- \ - ../out/cmake-release/samples/gltf_viewer \ - --headless \ - --batch ../libs/viewer/tests/basic.json \ - --api vulkan - docker exec runner /trees/filament/build/swiftshader/gallery.py - exit $? -fi - -if [[ "$1" == "run" ]]; then - docker exec --tty --workdir /trees/filament/results runner \ - /usr/bin/catchsegv \ - ../out/cmake-release/samples/gltf_viewer \ - --headless \ - --batch ../libs/viewer/tests/basic.json \ - --api vulkan - docker exec runner /trees/filament/build/swiftshader/gallery.py - exit $? -fi - -if [[ "$1" == "shell" ]]; then - docker exec --interactive --tty runner /bin/bash - exit $? -fi - -# Notes on options being passed to docker's run command: -# -# - The memory constraint seems to prevent an OOM signal in GitHub Actions. -# - The cap / security args allow use of lldb and creation of core dumps. -# - The privileged arg allows use of dmesg for examining OOM logs. -# -# Currently, a GitHub Actions VM has 2 CPUs, 7 GB RAM, and 14 GB of SSD disk space. -# -# Please be aware that Docker Desktop might impose additional resource constraints, and that those -# settings can only be controlled with its GUI. We recommend at least 7 GB of memory and 2 GB swap. -if [[ "$1" == "start" ]]; then - mkdir -p results - docker run --tty --rm --detach --privileged \ - --memory 6.5g \ - --name runner \ - --cap-add=SYS_PTRACE \ - --security-opt seccomp=unconfined \ - --security-opt apparmor=unconfined \ - --volume `pwd`:/trees/filament \ - --workdir /trees \ - ssfilament - exit $? -fi - -if [[ "$1" == "stop" ]]; then - docker container rm runner --force - exit $? -fi - -print_help -exit 1 diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index b10a95a36ec..6a3cb5cd0d4 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -66,7 +66,7 @@ set(PRIVATE_HDRS # OpenGL / OpenGL ES Sources # ================================================================================================== -if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3 AND NOT FILAMENT_USE_SWIFTSHADER) +if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3) list(APPEND SRCS include/backend/platforms/OpenGLPlatform.h src/opengl/gl_headers.cpp diff --git a/filament/backend/include/backend/platforms/VulkanPlatform.h b/filament/backend/include/backend/platforms/VulkanPlatform.h index 6201d64464e..ddde79af905 100644 --- a/filament/backend/include/backend/platforms/VulkanPlatform.h +++ b/filament/backend/include/backend/platforms/VulkanPlatform.h @@ -23,9 +23,9 @@ #include #include +#include #include -#include #include #include @@ -47,6 +47,14 @@ struct VulkanPlatformPrivate; class VulkanPlatform : public Platform, utils::PrivateImplementation { public: + struct ExtensionHashFn { + std::size_t operator()(utils::CString const& s) const noexcept { + return std::hash{}(s.data()); + } + }; + // Utility for managing device or instance extensions during initialization. + using ExtensionSet = std::unordered_set; + /** * A collection of handles to objects and metadata that comprises a Vulkan context. The client * can instantiate this struct and pass to Engine::Builder::sharedContext if they wishes to @@ -192,6 +200,13 @@ class VulkanPlatform : public Platform, utils::PrivateImplementation; - static ExtensionSet getRequiredInstanceExtensions(); + static ExtensionSet getSwapchainInstanceExtensions(); + // Platform dependent helper methods using SurfaceBundle = std::tuple; static SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance, uint64_t flags) noexcept; diff --git a/filament/backend/src/PlatformFactory.cpp b/filament/backend/src/PlatformFactory.cpp index 0156bba0b03..a97eec8f4f5 100644 --- a/filament/backend/src/PlatformFactory.cpp +++ b/filament/backend/src/PlatformFactory.cpp @@ -29,21 +29,21 @@ #include "backend/platforms/PlatformCocoaTouchGL.h" #endif #elif defined(__APPLE__) - #if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) && !defined(FILAMENT_USE_SWIFTSHADER) + #if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) #include #endif #elif defined(__linux__) #if defined(FILAMENT_SUPPORTS_X11) - #if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) && !defined(FILAMENT_USE_SWIFTSHADER) + #if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) #include "backend/platforms/PlatformGLX.h" #endif #elif defined(FILAMENT_SUPPORTS_EGL_ON_LINUX) - #if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) && !defined(FILAMENT_USE_SWIFTSHADER) + #if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) #include "backend/platforms/PlatformEGLHeadless.h" #endif #endif #elif defined(WIN32) - #if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) && !defined(FILAMENT_USE_SWIFTSHADER) + #if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) #include "backend/platforms/PlatformWGL.h" #endif #elif defined(__EMSCRIPTEN__) @@ -111,8 +111,7 @@ Platform* PlatformFactory::create(Backend* backend) noexcept { } assert_invariant(*backend == Backend::OPENGL); #if defined(FILAMENT_SUPPORTS_OPENGL) - #if defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(FILAMENT_USE_SWIFTSHADER) - // Swiftshader OpenGLES support is deprecated and incomplete + #if defined(FILAMENT_USE_EXTERNAL_GLES3) return nullptr; #elif defined(__ANDROID__) return new PlatformEGLAndroid(); diff --git a/filament/backend/src/vulkan/platform/VulkanPlatform.cpp b/filament/backend/src/vulkan/platform/VulkanPlatform.cpp index 687887a6988..7eb5cfe3850 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatform.cpp +++ b/filament/backend/src/vulkan/platform/VulkanPlatform.cpp @@ -44,7 +44,11 @@ namespace { constexpr uint32_t const INVALID_VK_INDEX = 0xFFFFFFFF; -typedef std::unordered_set ExtensionSet; +using ExtensionSet = VulkanPlatform::ExtensionSet; + +inline bool setContains(ExtensionSet const& set, utils::CString const& extension) { + return set.find(extension) != set.end(); +}; #if FVK_ENABLED(FVK_DEBUG_VALIDATION) // These strings need to be allocated outside a function stack @@ -80,7 +84,7 @@ FixedCapacityVector getEnabledLayers() { void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) { // Print some driver or MoltenVK information if it is available. - if (vkGetPhysicalDeviceProperties2KHR) { + if (vkGetPhysicalDeviceProperties2) { VkPhysicalDeviceDriverProperties driverProperties = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES, }; @@ -88,7 +92,7 @@ void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = &driverProperties, }; - vkGetPhysicalDeviceProperties2KHR(device, &physicalDeviceProperties2); + vkGetPhysicalDeviceProperties2(device, &physicalDeviceProperties2); utils::slog.i << "Vulkan device driver: " << driverProperties.driverName << " " << driverProperties.driverInfo << utils::io::endl; } @@ -148,38 +152,37 @@ void printDepthFormats(VkPhysicalDevice device) { } #endif -ExtensionSet getInstanceExtensions() { - std::string_view const TARGET_EXTS[] = { - // Request all cross-platform extensions. - VK_KHR_SURFACE_EXTENSION_NAME, - VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, +ExtensionSet getInstanceExtensions(ExtensionSet const& externallyRequiredExts = {}) { + ExtensionSet const TARGET_EXTS = { + // Request all cross-platform extensions. + VK_KHR_SURFACE_EXTENSION_NAME, + VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, - // Request these if available. + // Request these if available. #if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) - VK_EXT_DEBUG_UTILS_EXTENSION_NAME, + VK_EXT_DEBUG_UTILS_EXTENSION_NAME, #endif - VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, + VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, #if FVK_ENABLED(FVK_DEBUG_VALIDATION) - VK_EXT_DEBUG_REPORT_EXTENSION_NAME, + VK_EXT_DEBUG_REPORT_EXTENSION_NAME, #endif }; ExtensionSet exts; - FixedCapacityVector const availableExts - = filament::backend::enumerate(vkEnumerateInstanceExtensionProperties, + FixedCapacityVector const availableExts = + filament::backend::enumerate(vkEnumerateInstanceExtensionProperties, static_cast(nullptr) /* pLayerName */); for (auto const& extProps: availableExts) { - for (auto const& targetExt: TARGET_EXTS) { - if (targetExt == extProps.extensionName) { - exts.insert(targetExt); - } + utils::CString name { extProps.extensionName }; + if (setContains(TARGET_EXTS, name) || setContains(externallyRequiredExts, name)) { + exts.insert(name); } } return exts; } ExtensionSet getDeviceExtensions(VkPhysicalDevice device) { - std::string_view const TARGET_EXTS[] = { + ExtensionSet const TARGET_EXTS = { #if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) VK_EXT_DEBUG_MARKER_EXTENSION_NAME, #endif @@ -194,10 +197,9 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) { = filament::backend::enumerate(vkEnumerateDeviceExtensionProperties, device, static_cast(nullptr) /* pLayerName */); for (auto const& extension: extensions) { - for (auto const& targetExt: TARGET_EXTS) { - if (targetExt == extension.extensionName) { - exts.insert(targetExt); - } + utils::CString name { extension.extensionName }; + if (setContains(TARGET_EXTS, name)) { + exts.insert(name); } } return exts; @@ -245,7 +247,7 @@ VkInstance createInstance(ExtensionSet const& requiredExts) { ppEnabledExtensions[enabledExtensionCount++] = VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME; } // Request platform-specific extensions. - for (auto const requiredExt: requiredExts) { + for (auto const& requiredExt: requiredExts) { assert_invariant(enabledExtensionCount < MAX_INSTANCE_EXTENSION_COUNT); ppEnabledExtensions[enabledExtensionCount++] = requiredExt.data(); } @@ -260,7 +262,7 @@ VkInstance createInstance(ExtensionSet const& requiredExts) { instanceCreateInfo.pApplicationInfo = &appInfo; instanceCreateInfo.enabledExtensionCount = enabledExtensionCount; instanceCreateInfo.ppEnabledExtensionNames = ppEnabledExtensions; - if (requiredExts.find(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME) != requiredExts.end()) { + if (setContains(requiredExts, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) { instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; } @@ -283,8 +285,8 @@ VkInstance createInstance(ExtensionSet const& requiredExts) { } VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, - const VkPhysicalDeviceFeatures& features, uint32_t graphicsQueueFamilyIndex, - const ExtensionSet& deviceExtensions) { + VkPhysicalDeviceFeatures const& features, uint32_t graphicsQueueFamilyIndex, + ExtensionSet const& deviceExtensions) { VkDevice device; VkDeviceQueueCreateInfo deviceQueueCreateInfo[1] = {}; const float queuePriority[] = {1.0f}; @@ -292,9 +294,9 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, FixedCapacityVector requestExtensions; requestExtensions.reserve(deviceExtensions.size() + 1); - // TODO:We don't really need this if we only ever expect headless swapchains. + // TODO: We don't really need this if we only ever expect headless swapchains. requestExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); - for (auto ext: deviceExtensions) { + for (auto const& ext: deviceExtensions) { requestExtensions.push_back(ext.data()); } deviceQueueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; @@ -323,12 +325,12 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, .imageViewFormatSwizzle = VK_TRUE, .mutableComparisonSamplers = VK_TRUE, }; - if (deviceExtensions.find(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) != deviceExtensions.end()) { + if (setContains(deviceExtensions, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) { deviceCreateInfo.pNext = &portability; } VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device); - ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateDevice error."); + ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateDevice error=%d.", result); return device; } @@ -342,16 +344,16 @@ std::tuple pruneExtensions(VkPhysicalDevice device, #if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) // debugUtils and debugMarkers extensions are used mutually exclusively. - if (newInstExts.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != newInstExts.end() - && newDeviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != newDeviceExts.end()) { + if (setContains(newInstExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) && + setContains(newInstExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) { newDeviceExts.erase(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); } #endif #if FVK_ENABLED(FVK_DEBUG_VALIDATION) // debugMarker must also request debugReport the instance extension. So check if that's present. - if (newDeviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != newDeviceExts.end() - && newInstExts.find(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == newInstExts.end()) { + if (setContains(newInstExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME) && + !setContains(newInstExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) { newDeviceExts.erase(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); } #endif @@ -557,6 +559,7 @@ struct VulkanPlatformPrivate { std::unordered_set mHeadlessSwapChains; bool mSharedContext = false; + bool mForceXCBSwapchain = false; }; void VulkanPlatform::terminate() { @@ -610,7 +613,25 @@ Driver* VulkanPlatform::createDriver(void* sharedContext, ExtensionSet instExts; // If using a shared context, we do not assume any extensions. if (!mImpl->mSharedContext) { - instExts = getInstanceExtensions(); + // This constains instance extensions that are required for the platform, which includes + // swapchain surface extensions. + auto const& swapchainExts = getSwapchainInstanceExtensions(); + instExts = getInstanceExtensions(swapchainExts); + +#if defined(FILAMENT_SUPPORTS_XCB) && defined(FILAMENT_SUPPORTS_XLIB) + // For the special case where we're on linux and both xcb and xlib are "required", then we + // check if the set of supported extensions contain both of them. If only xcb is supported, + // we force XCB surface creation. This workaround is needed for the default swiftshader + // build where only XCB is available. + if (setContains(swapchainExts, VK_KHR_XCB_SURFACE_EXTENSION_NAME) && + setContains(swapchainExts, VK_KHR_XLIB_SURFACE_EXTENSION_NAME)) { + // Assume only XCB is left, then we force the XCB path in the swapchain creation. + mImpl->mForceXCBSwapchain = !setContains(instExts, VK_KHR_XLIB_SURFACE_EXTENSION_NAME); + assert_invariant(!mImpl->mForceXCBSwapchain || + setContains(instExts, VK_KHR_XCB_SURFACE_EXTENSION_NAME)); + } +#endif + instExts.merge(getRequiredInstanceExtensions()); } @@ -672,10 +693,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext, assert_invariant(mImpl->mGraphicsQueue != VK_NULL_HANDLE); // Store the extension support in the context - context.mDebugUtilsSupported - = instExts.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != instExts.end(); - context.mDebugMarkersSupported - = deviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != deviceExts.end(); + context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME); #ifdef NDEBUG // If we are in release build, we should not have turned on debug extensions @@ -748,6 +767,10 @@ SwapChainPtr VulkanPlatform::createSwapChain(void* nativeWindow, uint64_t flags, return swapchain; } + if (mImpl->mForceXCBSwapchain) { + flags |= SWAP_CHAIN_CONFIG_ENABLE_XCB; + } + auto [surface, fallbackExtent] = createVkSurfaceKHR(nativeWindow, mImpl->mInstance, flags); // The VulkanPlatformSurfaceSwapChain now `owns` the surface. VulkanPlatformSurfaceSwapChain* swapchain = new VulkanPlatformSurfaceSwapChain(mImpl->mContext, diff --git a/filament/backend/src/vulkan/platform/VulkanPlatformAndroidLinuxWindows.cpp b/filament/backend/src/vulkan/platform/VulkanPlatformAndroidLinuxWindows.cpp index 82be25cd0d7..67a63b7279f 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatformAndroidLinuxWindows.cpp +++ b/filament/backend/src/vulkan/platform/VulkanPlatformAndroidLinuxWindows.cpp @@ -46,7 +46,7 @@ uint32_t height; } wl; }// anonymous namespace -#elif LINUX_OR_FREEBSD && defined(FILAMENT_SUPPORTS_X11) +#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11) // TODO: we should allow for headless on Linux explicitly. Right now this is the headless path // (with no FILAMENT_SUPPORTS_XCB or FILAMENT_SUPPORTS_XLIB). #include @@ -86,22 +86,23 @@ using namespace bluevk; namespace filament::backend { -VulkanPlatform::ExtensionSet VulkanPlatform::getRequiredInstanceExtensions() { - VulkanPlatform::ExtensionSet ret; - #if defined(__ANDROID__) - ret.insert("VK_KHR_android_surface"); - #elif defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND) - ret.insert("VK_KHR_wayland_surface"); - #elif LINUX_OR_FREEBSD && defined(FILAMENT_SUPPORTS_X11) - #if defined(FILAMENT_SUPPORTS_XCB) - ret.insert("VK_KHR_xcb_surface"); - #endif - #if defined(FILAMENT_SUPPORTS_XLIB) - ret.insert("VK_KHR_xlib_surface"); - #endif - #elif defined(WIN32) - ret.insert("VK_KHR_win32_surface"); +VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() { + VulkanPlatform::ExtensionSet const ret = { +#if defined(__ANDROID__) + VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, +#elif defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND) + VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, +#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11) + #if defined(FILAMENT_SUPPORTS_XCB) + VK_KHR_XCB_SURFACE_EXTENSION_NAME, + #endif + #if defined(FILAMENT_SUPPORTS_XLIB) + VK_KHR_XLIB_SURFACE_EXTENSION_NAME, #endif +#elif defined(WIN32) + VK_KHR_WIN32_SURFACE_EXTENSION_NAME, +#endif + }; return ret; } @@ -138,7 +139,7 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin VkResult const result = vkCreateWaylandSurfaceKHR(instance, &createInfo, VKALLOC, (VkSurfaceKHR*) &surface); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateWaylandSurfaceKHR error."); - #elif LINUX_OR_FREEBSD && defined(FILAMENT_SUPPORTS_X11) + #elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11) if (g_x11_vk.library == nullptr) { g_x11_vk.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW); ASSERT_PRECONDITION(g_x11_vk.library, "Unable to open X11 library."); @@ -146,15 +147,11 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin g_x11_vk.xcbConnect = (XCB_CONNECT) dlsym(g_x11_vk.library, "xcb_connect"); int screen; g_x11_vk.connection = g_x11_vk.xcbConnect(nullptr, &screen); - ASSERT_POSTCONDITION(vkCreateXcbSurfaceKHR, - "Unable to load vkCreateXcbSurfaceKHR function."); #endif #if defined(FILAMENT_SUPPORTS_XLIB) g_x11_vk.openDisplay = (X11_OPEN_DISPLAY) dlsym(g_x11_vk.library, "XOpenDisplay"); g_x11_vk.display = g_x11_vk.openDisplay(NULL); ASSERT_PRECONDITION(g_x11_vk.display, "Unable to open X11 display."); - ASSERT_POSTCONDITION(vkCreateXlibSurfaceKHR, - "Unable to load vkCreateXlibSurfaceKHR function."); #endif } #if defined(FILAMENT_SUPPORTS_XCB) || defined(FILAMENT_SUPPORTS_XLIB) @@ -167,6 +164,9 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin useXcb = true; #endif if (useXcb) { + ASSERT_POSTCONDITION(vkCreateXcbSurfaceKHR, + "Unable to load vkCreateXcbSurfaceKHR function."); + VkXcbSurfaceCreateInfoKHR const createInfo = { .sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR, .connection = g_x11_vk.connection, @@ -179,6 +179,9 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin #endif #if defined(FILAMENT_SUPPORTS_XLIB) if (!useXcb) { + ASSERT_POSTCONDITION(vkCreateXlibSurfaceKHR, + "Unable to load vkCreateXlibSurfaceKHR function."); + VkXlibSurfaceCreateInfoKHR const createInfo = { .sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, .dpy = g_x11_vk.display, diff --git a/filament/backend/src/vulkan/platform/VulkanPlatformApple.mm b/filament/backend/src/vulkan/platform/VulkanPlatformApple.mm index 0540a294caa..a24e9ca3dcb 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatformApple.mm +++ b/filament/backend/src/vulkan/platform/VulkanPlatformApple.mm @@ -52,13 +52,14 @@ namespace filament::backend { -VulkanPlatform::ExtensionSet VulkanPlatform::getRequiredInstanceExtensions() { - ExtensionSet ret; +VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() { + ExtensionSet const ret = { #if defined(__APPLE__) - ret.insert("VK_MVK_macos_surface"); // TODO: replace with VK_EXT_metal_surface - #elif defined(IOS) - ret.insert("VK_MVK_ios_surface"); + VK_MVK_MACOS_SURFACE_EXTENSION_NAME, // TODO: replace with VK_EXT_metal_surface + #elif defined(IOS) && defined(METAL_AVAILABLE) + VK_MVK_IOS_SURFACE_EXTENSION_NAME, #endif + }; return ret; } From 7267696cbf8976856b6918dcde82aab99087559c Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 14 May 2024 12:08:50 -0700 Subject: [PATCH 04/23] vk: fix dynamic scissor validation error (#7853) --- filament/backend/src/vulkan/VulkanDriver.cpp | 8 +++++++- filament/backend/src/vulkan/VulkanPipelineCache.cpp | 5 ----- filament/backend/src/vulkan/VulkanPipelineCache.h | 6 ------ 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 1cdc8a20cc0..0aa27d5c593 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -1873,6 +1873,12 @@ void VulkanDriver::bindPipeline(PipelineState pipelineState) { mPipelineCache.bindLayout(pipelineLayout); mPipelineCache.bindPipeline(commands); + + // Since we don't statically define scissor as part of the pipeline, we need to call scissor at + // least once. Context: VUID-vkCmdDrawIndexed-None-07832. + auto const& extent = rt->getExtent(); + scissor({0, 0, extent.width, extent.height}); + FVK_SYSTRACE_END(); } @@ -1963,7 +1969,7 @@ void VulkanDriver::scissor(Viewport scissorBox) { const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget; rt->transformClientRectToPlatform(&scissor); - mPipelineCache.bindScissor(cmdbuffer, scissor); + vkCmdSetScissor(cmdbuffer, 0, 1, &scissor); } void VulkanDriver::beginTimerQuery(Handle tqh) { diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.cpp b/filament/backend/src/vulkan/VulkanPipelineCache.cpp index 7cfc74162e0..521d017b8e7 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.cpp +++ b/filament/backend/src/vulkan/VulkanPipelineCache.cpp @@ -79,10 +79,6 @@ void VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) { commands->setPipeline(cacheEntry->handle); } -void VulkanPipelineCache::bindScissor(VkCommandBuffer cmdbuffer, VkRect2D scissor) noexcept { - vkCmdSetScissor(cmdbuffer, 0, 1, &scissor); -} - VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept { assert_invariant(mPipelineRequirements.shaders[0] && "Vertex shader is not bound."); assert_invariant(mPipelineRequirements.layout && "No pipeline layout specified"); @@ -306,7 +302,6 @@ void VulkanPipelineCache::gc() noexcept { // The Vulkan spec says: "When a command buffer begins recording, all state in that command // buffer is undefined." Therefore, we need to clear all bindings at this time. mBoundPipeline = {}; - mCurrentScissor = {}; // NOTE: Due to robin_map restrictions, we cannot use auto or range-based loops. diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.h b/filament/backend/src/vulkan/VulkanPipelineCache.h index 53eaf71287c..e6816497c05 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.h +++ b/filament/backend/src/vulkan/VulkanPipelineCache.h @@ -120,9 +120,6 @@ class VulkanPipelineCache { // Creates a new pipeline if necessary and binds it using vkCmdBindPipeline. void bindPipeline(VulkanCommandBuffer* commands); - // Sets up a new scissor rectangle if it has been dirtied. - void bindScissor(VkCommandBuffer cmdbuffer, VkRect2D scissor) noexcept; - // Each of the following methods are fast and do not make Vulkan calls. void bindProgram(VulkanProgram* program) noexcept; void bindRasterState(const RasterState& rasterState) noexcept; @@ -263,9 +260,6 @@ class VulkanPipelineCache { // Current bindings for the pipeline and descriptor sets. PipelineKey mBoundPipeline = {}; - - // Current state for scissoring. - VkRect2D mCurrentScissor = {}; }; } // namespace filament::backend From 6be97ee01d232b3fe6dad9fcce62d3a2f4562e3f Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 14 May 2024 11:15:17 -0700 Subject: [PATCH 05/23] backend: zero-cost ES2 draw() we use a different hook for the draw() call when on an ES2 context, this eliminates completely the overhead of supporting ES2 for the draw call. draw calls are expected to be the most common calls. --- filament/backend/src/opengl/OpenGLDriver.cpp | 49 +++++++++++++++----- filament/backend/src/opengl/OpenGLDriver.h | 2 + 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 77fec7d41db..53d01cc4407 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -241,7 +241,15 @@ OpenGLDriver::~OpenGLDriver() noexcept { // NOLINT(modernize-use-equals-default) } Dispatcher OpenGLDriver::getDispatcher() const noexcept { - return ConcreteDispatcher::make(); + auto dispatcher = ConcreteDispatcher::make(); + if (mContext.isES2()) { + dispatcher.draw2_ = +[](Driver& driver, CommandBase* base, intptr_t* next){ + using Cmd = COMMAND_TYPE(draw2); + OpenGLDriver& concreteDriver = static_cast(driver); + Cmd::execute(&OpenGLDriver::draw2GLES2, concreteDriver, base, next); + }; + } + return dispatcher; } // ------------------------------------------------------------------------------------------------ @@ -3875,19 +3883,34 @@ void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins return; } - if (UTILS_LIKELY(instanceCount <= 1)) { - glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(), - reinterpret_cast(indexOffset * rp->gl.indicesSize)); - } else { - assert_invariant(!mContext.isES2()); #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - glDrawElementsInstanced(GLenum(rp->type), (GLsizei)indexCount, - rp->gl.getIndicesType(), - reinterpret_cast(indexOffset * rp->gl.indicesSize), - (GLsizei)instanceCount); + assert_invariant(!mContext.isES2()); + glDrawElementsInstanced(GLenum(rp->type), (GLsizei)indexCount, + rp->gl.getIndicesType(), + reinterpret_cast(indexOffset * rp->gl.indicesSize), + (GLsizei)instanceCount); +#endif + +#if FILAMENT_ENABLE_MATDBG + CHECK_GL_ERROR_NON_FATAL(utils::slog.e) +#else + CHECK_GL_ERROR(utils::slog.e) #endif +} + +void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { + GLRenderPrimitive const* const rp = mBoundRenderPrimitive; + if (UTILS_UNLIKELY(!rp || !mValidProgram)) { + return; } + assert_invariant(mContext.isES2()); + assert_invariant(instanceCount == 1); + + glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(), + reinterpret_cast(indexOffset * rp->gl.indicesSize)); + + #if FILAMENT_ENABLE_MATDBG CHECK_GL_ERROR_NON_FATAL(utils::slog.e) #else @@ -3907,7 +3930,11 @@ void OpenGLDriver::draw(PipelineState state, Handle rph, state.vertexBufferInfo = rp->vbih; bindPipeline(state); bindRenderPrimitive(rph); - draw2(indexOffset, indexCount, instanceCount); + if (UTILS_UNLIKELY(mContext.isES2())) { + draw2GLES2(indexOffset, indexCount, instanceCount); + } else { + draw2(indexOffset, indexCount, instanceCount); + } } void OpenGLDriver::dispatchCompute(Handle program, math::uint3 workGroupCount) { diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h index 667262b715e..d5e1b797b68 100644 --- a/filament/backend/src/opengl/OpenGLDriver.h +++ b/filament/backend/src/opengl/OpenGLDriver.h @@ -336,6 +336,8 @@ class OpenGLDriver final : public DriverBase { void setScissor(Viewport const& scissor) noexcept; + void draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount); + // ES2 only. Uniform buffer emulation binding points GLuint mLastAssignedEmulatedUboId = 0; From 499939ed3cb22c87804d3f0040fa1b3bc9dabfec Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 14 May 2024 11:13:20 -0700 Subject: [PATCH 06/23] backend: bindPipeline now takes const& --- filament/backend/include/private/backend/DriverAPI.inc | 2 +- filament/backend/src/metal/MetalDriver.mm | 2 +- filament/backend/src/noop/NoopDriver.cpp | 2 +- filament/backend/src/opengl/OpenGLDriver.cpp | 8 +++++--- filament/backend/src/vulkan/VulkanDriver.cpp | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index 3e13c4c3547..6132cf64a7e 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -502,7 +502,7 @@ DECL_DRIVER_API_N(blit, math::uint2, size) DECL_DRIVER_API_N(bindPipeline, - backend::PipelineState, state) + backend::PipelineState const&, state) DECL_DRIVER_API_N(bindRenderPrimitive, backend::RenderPrimitiveHandle, rph) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 7f2bbc072f8..db9af5b163b 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -1637,7 +1637,7 @@ } } -void MetalDriver::bindPipeline(PipelineState ps) { +void MetalDriver::bindPipeline(PipelineState const& ps) { ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr, "bindPipeline() without a valid command encoder."); diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index 9b3b21f168a..145d602798e 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -359,7 +359,7 @@ void NoopDriver::blit( math::uint2 size) { } -void NoopDriver::bindPipeline(PipelineState pipelineState) { +void NoopDriver::bindPipeline(PipelineState const& pipelineState) { } void NoopDriver::bindRenderPrimitive(Handle rph) { diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 53d01cc4407..5b60a755323 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -932,16 +932,18 @@ void OpenGLDriver::importTextureR(Handle th, intptr_t id, } void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer const* vb) { - // NOTE: this is called from draw() and must be as efficient as possible. + // NOTE: this is called often and must be as efficient as possible. auto& gl = mContext; +#ifndef NDEBUG if (UTILS_LIKELY(gl.ext.OES_vertex_array_object)) { // The VAO for the given render primitive must already be bound. GLint vaoBinding; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &vaoBinding); assert_invariant(vaoBinding == (GLint)rp->gl.vao[gl.contextIndex]); } +#endif if (UTILS_LIKELY(rp->gl.vertexBufferVersion == vb->bufferObjectsVersion && rp->gl.stateVersion == gl.state.age)) { @@ -957,7 +959,7 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer // if a buffer is defined it must not be invalid. assert_invariant(vb->gl.buffers[bi]); - // if w're on ES2, the user shouldn't use FLAG_INTEGER_TARGET + // if we're on ES2, the user shouldn't use FLAG_INTEGER_TARGET assert_invariant(!(gl.isES2() && (attribute.flags & Attribute::FLAG_INTEGER_TARGET))); gl.bindBuffer(GL_ARRAY_BUFFER, vb->gl.buffers[bi]); @@ -3845,7 +3847,7 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel) #endif } -void OpenGLDriver::bindPipeline(PipelineState state) { +void OpenGLDriver::bindPipeline(PipelineState const& state) { DEBUG_MARKER() auto& gl = mContext; setRasterState(state.rasterState); diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 0aa27d5c593..488243dd77d 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -1763,7 +1763,7 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers, FVK_SYSTRACE_END(); } -void VulkanDriver::bindPipeline(PipelineState pipelineState) { +void VulkanDriver::bindPipeline(PipelineState const& pipelineState) { FVK_SYSTRACE_CONTEXT(); FVK_SYSTRACE_START("draw"); From c93aa4c90df7a814a076ebc8d92cc94d4fa96910 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 15 Mar 2024 13:59:56 -0700 Subject: [PATCH 07/23] minor libutils improvements --- libs/utils/include/utils/FixedCapacityVector.h | 10 ++++++++++ libs/utils/include/utils/bitset.h | 12 +++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/libs/utils/include/utils/FixedCapacityVector.h b/libs/utils/include/utils/FixedCapacityVector.h index f3990124854..d734117a358 100644 --- a/libs/utils/include/utils/FixedCapacityVector.h +++ b/libs/utils/include/utils/FixedCapacityVector.h @@ -299,6 +299,16 @@ class UTILS_PUBLIC FixedCapacityVector { } } + UTILS_NOINLINE + void shrink_to_fit() { + if (size() < capacity()) { + FixedCapacityVector t(construct_with_capacity, size(), allocator()); + t.mSize = size(); + std::uninitialized_move(begin(), end(), t.begin()); + this->swap(t); + } + } + private: enum construct_with_capacity_tag{ construct_with_capacity }; diff --git a/libs/utils/include/utils/bitset.h b/libs/utils/include/utils/bitset.h index 281e5dfcf26..8844fdb80ac 100644 --- a/libs/utils/include/utils/bitset.h +++ b/libs/utils/include/utils/bitset.h @@ -60,6 +60,11 @@ class UTILS_PUBLIC bitset { std::fill(std::begin(storage), std::end(storage), 0); } + template> + explicit bitset(U value) noexcept { + storage[0] = value; + } + T getBitsAt(size_t n) const noexcept { assert_invariant(n Date: Wed, 15 May 2024 10:50:10 -0700 Subject: [PATCH 08/23] Metal, fix callbacks being called only once (#7856) --- filament/backend/CMakeLists.txt | 1 + .../include/private/backend/DriverAPI.inc | 3 +- filament/backend/src/metal/MetalDriver.mm | 6 +- filament/backend/src/metal/MetalHandles.h | 7 +- filament/backend/src/metal/MetalHandles.mm | 39 ++++-- filament/backend/src/noop/NoopDriver.cpp | 2 +- filament/backend/src/opengl/OpenGLDriver.cpp | 2 +- filament/backend/src/vulkan/VulkanDriver.cpp | 2 +- filament/backend/test/test_Callbacks.cpp | 122 ++++++++++++++++++ filament/src/details/SwapChain.cpp | 23 +--- 10 files changed, 163 insertions(+), 44 deletions(-) create mode 100644 filament/backend/test/test_Callbacks.cpp diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index 6a3cb5cd0d4..34cc4b5d59b 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -417,6 +417,7 @@ if (APPLE OR LINUX) test/test_MissingRequiredAttributes.cpp test/test_ReadPixels.cpp test/test_BufferUpdates.cpp + test/test_Callbacks.cpp test/test_MRT.cpp test/test_LoadImage.cpp test/test_StencilBuffer.cpp diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index 6132cf64a7e..bb64f3d4367 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -144,8 +144,7 @@ DECL_DRIVER_API_N(setFrameScheduledCallback, DECL_DRIVER_API_N(setFrameCompletedCallback, backend::SwapChainHandle, sch, backend::CallbackHandler*, handler, - backend::CallbackHandler::Callback, callback, - void*, user) + utils::Invocable&&, callback) DECL_DRIVER_API_N(setPresentationTime, int64_t, monotonic_clock_ns) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index db9af5b163b..0172be1ce19 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -243,10 +243,10 @@ swapChain->setFrameScheduledCallback(handler, std::move(callback)); } -void MetalDriver::setFrameCompletedCallback(Handle sch, - CallbackHandler* handler, CallbackHandler::Callback callback, void* user) { +void MetalDriver::setFrameCompletedCallback( + Handle sch, CallbackHandler* handler, utils::Invocable&& callback) { auto* swapChain = handle_cast(sch); - swapChain->setFrameCompletedCallback(handler, callback, user); + swapChain->setFrameCompletedCallback(handler, std::move(callback)); } void MetalDriver::execute(std::function const& fn) noexcept { diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index c6c65e1f7d6..05ce7529d27 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -75,7 +75,7 @@ class MetalSwapChain : public HwSwapChain { void setFrameScheduledCallback(CallbackHandler* handler, FrameScheduledCallback&& callback); void setFrameCompletedCallback( - CallbackHandler* handler, CallbackHandler::Callback callback, void* user); + CallbackHandler* handler, utils::Invocable&& callback); // For CAMetalLayer-backed SwapChains, presents the drawable or schedules a // FrameScheduledCallback. @@ -119,13 +119,12 @@ class MetalSwapChain : public HwSwapChain { // PresentCallable object. struct { CallbackHandler* handler = nullptr; - FrameScheduledCallback callback = {}; + std::shared_ptr callback = nullptr; } frameScheduled; struct { CallbackHandler* handler = nullptr; - CallbackHandler::Callback callback = {}; - void* user = nullptr; + std::shared_ptr> callback = nullptr; } frameCompleted; }; diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 15958ab60ef..2b770241ab0 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -224,14 +224,13 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) { void MetalSwapChain::setFrameScheduledCallback( CallbackHandler* handler, FrameScheduledCallback&& callback) { frameScheduled.handler = handler; - frameScheduled.callback = std::move(callback); + frameScheduled.callback = std::make_shared(std::move(callback)); } -void MetalSwapChain::setFrameCompletedCallback(CallbackHandler* handler, - CallbackHandler::Callback callback, void* user) { +void MetalSwapChain::setFrameCompletedCallback( + CallbackHandler* handler, utils::Invocable&& callback) { frameCompleted.handler = handler; - frameCompleted.callback = callback; - frameCompleted.user = user; + frameCompleted.callback = std::make_shared>(std::move(callback)); } void MetalSwapChain::present() { @@ -304,17 +303,17 @@ void presentDrawable(bool presentFrame, void* user) { assert_invariant(drawable); struct Callback { - Callback(FrameScheduledCallback&& callback, id drawable, + Callback(std::shared_ptr callback, id drawable, MetalDriver* driver) - : f(std::move(callback)), data(PresentDrawableData::create(drawable, driver)) {} - FrameScheduledCallback f; + : f(callback), data(PresentDrawableData::create(drawable, driver)) {} + std::shared_ptr f; // PresentDrawableData* is destroyed by maybePresentAndDestroyAsync() later. std::unique_ptr data; static void func(void* user) { auto* const c = reinterpret_cast(user); PresentDrawableData* presentDrawableData = c->data.release(); PresentCallable presentCallable(presentDrawable, presentDrawableData); - c->f(presentCallable); + c->f->operator()(presentCallable); delete c; } }; @@ -322,7 +321,7 @@ static void func(void* user) { // This callback pointer will be captured by the block. Even if the scheduled handler is never // called, the unique_ptr will still ensure we don't leak memory. __block auto callback = - std::make_unique(std::move(frameScheduled.callback), drawable, context.driver); + std::make_unique(frameScheduled.callback, drawable, context.driver); backend::CallbackHandler* handler = frameScheduled.handler; MetalDriver* driver = context.driver; @@ -337,13 +336,25 @@ static void func(void* user) { return; } - CallbackHandler* handler = frameCompleted.handler; - void* user = frameCompleted.user; - CallbackHandler::Callback callback = frameCompleted.callback; + struct Callback { + Callback(std::shared_ptr> callback) : f(callback) {} + std::shared_ptr> f; + static void func(void* user) { + auto* const c = reinterpret_cast(user); + c->f->operator()(); + delete c; + } + }; + + // This callback pointer will be captured by the block. Even if the completed handler is never + // called, the unique_ptr will still ensure we don't leak memory. + __block auto callback = std::make_unique(frameCompleted.callback); + CallbackHandler* handler = frameCompleted.handler; MetalDriver* driver = context.driver; [getPendingCommandBuffer(&context) addCompletedHandler:^(id cb) { - driver->scheduleCallback(handler, user, callback); + Callback* user = callback.release(); + driver->scheduleCallback(handler, user, &Callback::func); }]; } diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index 145d602798e..4b4b73fe185 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -59,7 +59,7 @@ void NoopDriver::setFrameScheduledCallback(Handle sch, } void NoopDriver::setFrameCompletedCallback(Handle sch, - CallbackHandler* handler, CallbackHandler::Callback callback, void* user) { + CallbackHandler* handler, utils::Invocable&& callback) { } diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 5b60a755323..38d82a68662 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -3469,7 +3469,7 @@ void OpenGLDriver::setFrameScheduledCallback(Handle sch, } void OpenGLDriver::setFrameCompletedCallback(Handle sch, - CallbackHandler* handler, CallbackHandler::Callback callback, void* user) { + CallbackHandler* handler, utils::Invocable&& callback) { DEBUG_MARKER() } diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 488243dd77d..e0f62822bf2 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -398,7 +398,7 @@ void VulkanDriver::setFrameScheduledCallback(Handle sch, } void VulkanDriver::setFrameCompletedCallback(Handle sch, - CallbackHandler* handler, CallbackHandler::Callback callback, void* user) { + CallbackHandler* handler, utils::Invocable&& callback) { } void VulkanDriver::setPresentationTime(int64_t monotonic_clock_ns) { diff --git a/filament/backend/test/test_Callbacks.cpp b/filament/backend/test/test_Callbacks.cpp new file mode 100644 index 00000000000..0f02f1b2141 --- /dev/null +++ b/filament/backend/test/test_Callbacks.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2024 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 "BackendTest.h" + +using namespace filament; +using namespace filament::backend; + +namespace test { + +TEST_F(BackendTest, FrameScheduledCallback) { + auto& api = getDriverApi(); + + // Create a SwapChain. + // In order for the frameScheduledCallback to be called, this must be a real SwapChain (not + // headless) so we obtain a drawable. + auto swapChain = createSwapChain(); + + Handle renderTarget = api.createDefaultRenderTarget(); + + int callbackCountA = 0; + api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountA](PresentCallable callable) { + callable(); + callbackCountA++; + }); + + // Render the first frame. + api.makeCurrent(swapChain, swapChain); + api.beginFrame(0, 0, 0); + api.beginRenderPass(renderTarget, {}); + api.endRenderPass(0); + api.commit(swapChain); + api.endFrame(0); + + // Render the next frame. The same callback should be called. + api.makeCurrent(swapChain, swapChain); + api.beginFrame(0, 0, 0); + api.beginRenderPass(renderTarget, {}); + api.endRenderPass(0); + api.commit(swapChain); + api.endFrame(0); + + // Now switch out the callback. + int callbackCountB = 0; + api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountB](PresentCallable callable) { + callable(); + callbackCountB++; + }); + + // Render one final frame. + api.makeCurrent(swapChain, swapChain); + api.beginFrame(0, 0, 0); + api.beginRenderPass(renderTarget, {}); + api.endRenderPass(0); + api.commit(swapChain); + api.endFrame(0); + + api.finish(); + + executeCommands(); + getDriver().purge(); + + EXPECT_EQ(callbackCountA, 2); + EXPECT_EQ(callbackCountB, 1); +} + +TEST_F(BackendTest, FrameCompletedCallback) { + auto& api = getDriverApi(); + + // Create a SwapChain. + auto swapChain = api.createSwapChainHeadless(256, 256, 0); + + int callbackCountA = 0; + api.setFrameCompletedCallback(swapChain, nullptr, + [&callbackCountA]() { callbackCountA++; }); + + // Render the first frame. + api.makeCurrent(swapChain, swapChain); + api.beginFrame(0, 0, 0); + api.commit(swapChain); + api.endFrame(0); + + // Render the next frame. The same callback should be called. + api.makeCurrent(swapChain, swapChain); + api.beginFrame(0, 0, 0); + api.commit(swapChain); + api.endFrame(0); + + // Now switch out the callback. + int callbackCountB = 0; + api.setFrameCompletedCallback(swapChain, nullptr, + [&callbackCountB]() { callbackCountB++; }); + + // Render one final frame. + api.makeCurrent(swapChain, swapChain); + api.beginFrame(0, 0, 0); + api.commit(swapChain); + api.endFrame(0); + + api.finish(); + + executeCommands(); + getDriver().purge(); + + EXPECT_EQ(callbackCountA, 2); + EXPECT_EQ(callbackCountB, 1); +} + +} // namespace test diff --git a/filament/src/details/SwapChain.cpp b/filament/src/details/SwapChain.cpp index 0407d893763..20589c38f14 100644 --- a/filament/src/details/SwapChain.cpp +++ b/filament/src/details/SwapChain.cpp @@ -79,24 +79,11 @@ bool FSwapChain::isFrameScheduledCallbackSet() const noexcept { return mFrameScheduledCallbackIsSet; } -void FSwapChain::setFrameCompletedCallback(backend::CallbackHandler* handler, - utils::Invocable&& callback) noexcept { - struct Callback { - utils::Invocable f; - SwapChain* s; - static void func(void* user) { - auto* const c = reinterpret_cast(user); - c->f(c->s); - delete c; - } - }; - if (callback) { - auto* const user = new(std::nothrow) Callback{ std::move(callback), this }; - mEngine.getDriverApi().setFrameCompletedCallback( - mHwSwapChain, handler, &Callback::func, static_cast(user)); - } else { - mEngine.getDriverApi().setFrameCompletedCallback(mHwSwapChain, nullptr, nullptr, nullptr); - } +void FSwapChain::setFrameCompletedCallback( + backend::CallbackHandler* handler, FrameCompletedCallback&& callback) noexcept { + using namespace std::placeholders; + auto boundCallback = std::bind(std::move(callback), this); + mEngine.getDriverApi().setFrameCompletedCallback(mHwSwapChain, handler, std::move(boundCallback)); } bool FSwapChain::isSRGBSwapChainSupported(FEngine& engine) noexcept { From ab129849129f96ec7e97b954942b8c61d8bcc7df Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 16 May 2024 10:03:50 -0700 Subject: [PATCH 09/23] Add Mesa software rasterizer BUILD.md instructions (#7860) --- BUILDING.md | 45 +++++++++++++++++++++++++++++++++++++++----- NEW_RELEASE_NOTES.md | 2 ++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index 9c8cd1d2b22..de1549f230f 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -437,10 +437,11 @@ Finally simply open `docs/html/index.html` in your web browser. ## Software Rasterization -We have tested swiftshader for running software rasterization on the Vulkan backend. To use this, -please first make sure that the [Vulkan SDK](https://www.lunarg.com/vulkan-sdk/) is installed on -your machine. If you are doing a manual installation of the SDK on Linux, you will have to source -`setup-env.sh` in the SDK's root folder to make sure the Vulkan loader is the first lib loaded. +We have tested swiftshader and Mesa for software rasterization on the Vulkan/GL backends. + +To use this for Vulkan, please first make sure that the [Vulkan SDK](https://www.lunarg.com/vulkan-sdk/) is +installed on your machine. If you are doing a manual installation of the SDK on Linux, you will have +to source `setup-env.sh` in the SDK's root folder to make sure the Vulkan loader is the first lib loaded. ### Swiftshader (Vulkan) [tested on macOS and Linux] @@ -457,5 +458,39 @@ and then set `VK_ICD_FILENAMES` to the ICD json produced in the build. For examp export VK_ICD_FILENAMES=/Users/user/swiftshader/build/Darwin/vk_swiftshader_icd.json ``` -Build Filament as normal and use the vulkan backend. +Build and run Filament as usual and specify the Vulkan backend when creating the Engine. + +### Mesa's LLVMPipe (GL) and Lavapipe (Vulkan) [tested on Linux] + +We will only cover steps that build Mesa from source. The official documentation of Mesa mentioned +that in general precompiled libraries [are **not** made available](https://docs.mesa3d.org/precompiled.html). + +Download the repo and make sure you have the build depedencies. For example (assuming an Ubuntu/Debian distro), +```shell +git clone https://gitlab.freedesktop.org/mesa/mesa.git +sudo apt-get build-dep mesa +``` + +To build both the GL and Vulkan rasterizers, + +```shell +cd mesa +mkdir -p out +meson setup builddir/ -Dprefix=$(pwd)/out -Dglx=xlib -Dgallium-drivers=swrast -Dvulkan-drivers=swrast +meson install -C builddir/ +``` + +For GL, we need to ensure that we load the GL lib from the mesa output directory. For example, to run +the debug `gltf_viewer`, we would execute +```shell +LD_LIBRARY_PATH=/Users/user/mesa/out/lib/x86_64-linux-gnu \ + ./out/cmake-debug/samples/gltf_viewer -a opengl +``` + +For Vulkan, we need to set the path to the ICD json, which tells the loader where to find the driver +library. To run `gltf_viewer`, we would execute +```shell +VK_ICD_FILENAMES=/Users/user/mesa/out/share/vulkan/icd.d/lvp_icd.x86_64.json \ + ./out/cmake-debug/samples/gltf_viewer -a vulkan +``` diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 4a1a9c7fa7e..2554073b9e5 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,3 +7,5 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut + +- Add instructions for using Mesa for software rasterization From a9f3971989a42f08aba554addb49e825b07a7402 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 16 May 2024 10:37:27 -0700 Subject: [PATCH 10/23] Refactor stereo build flags and configs (#7857) - Add option to build.sh to build for paritcular stereo techniques (default to NONE). Only applies to samples. - Consoldiate viewer checkbox for debugging stereo rendering - Add DriverConfig flag for stereoscopic type so that it can be used to determine availability of the feature and (to be completed) enable corresponding GPU features. Co-authored-by: Mathias Agopian --- CMakeLists.txt | 10 +++++--- .../com/google/android/filament/Engine.java | 7 ++++-- build.sh | 25 +++++++++++++++++-- .../backend/include/backend/DriverEnums.h | 9 ++----- filament/backend/include/backend/Platform.h | 25 +++++++++++++++++++ .../include/private/backend/DriverAPI.inc | 2 +- filament/backend/src/metal/MetalDriver.h | 2 ++ filament/backend/src/metal/MetalDriver.mm | 19 ++++++++------ filament/backend/src/noop/NoopDriver.cpp | 2 +- filament/backend/src/opengl/OpenGLDriver.cpp | 16 ++++++------ filament/backend/src/vulkan/VulkanDriver.cpp | 18 +++++++------ filament/backend/src/vulkan/VulkanDriver.h | 3 ++- filament/include/filament/Engine.h | 2 +- filament/src/Engine.cpp | 2 +- filament/src/details/Engine.cpp | 7 ++++-- filament/src/details/Engine.h | 4 +-- filament/src/details/Material.cpp | 3 +-- filament/src/details/Skybox.cpp | 4 ++- filament/src/details/View.cpp | 2 +- libs/filamat/src/GLSLPostProcessor.cpp | 7 +++--- libs/filamat/src/shaders/CodeGenerator.cpp | 8 ++++++ libs/filamentapp/src/FilamentApp.cpp | 2 ++ libs/viewer/CMakeLists.txt | 6 +++++ libs/viewer/src/ViewerGui.cpp | 14 ++++++++--- samples/gltf_viewer.cpp | 5 ---- 25 files changed, 141 insertions(+), 63 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7809decd280..e17aba1a7f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -524,13 +524,15 @@ else() endif() # This only affects the prebuilt shader files in gltfio and samples, not filament library. -# The value can be either "instanced" or "multiview". -set(FILAMENT_SAMPLES_STEREO_TYPE "instanced" CACHE STRING +# The value can be either "instanced", "multiview", or "none" +set(FILAMENT_SAMPLES_STEREO_TYPE "none" CACHE STRING "Stereoscopic type that shader files in gltfio and samples are built for." ) string(TOLOWER "${FILAMENT_SAMPLES_STEREO_TYPE}" FILAMENT_SAMPLES_STEREO_TYPE) -if (NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "instanced" AND NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview") - message(FATAL_ERROR "Invalid stereo type: \"${FILAMENT_SAMPLES_STEREO_TYPE}\" choose either \"instanced\" or \"multiview\" ") +if (NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "instanced" + AND NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview" + AND NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "none") + message(FATAL_ERROR "Invalid stereo type: \"${FILAMENT_SAMPLES_STEREO_TYPE}\" choose either \"instanced\", \"multiview\", or \"none\" ") endif () # Compiling samples for multiview implies enabling multiview feature as well. diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java index 8d53c412b45..827fcf11caf 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java @@ -159,9 +159,12 @@ public enum FeatureLevel { }; /** - * The type of technique for stereoscopic rendering + * The type of technique for stereoscopic rendering. (Note that the materials used will need to be + * compatible with the chosen technique.) */ public enum StereoscopicType { + /** No stereoscopic rendering. */ + NONE, /** Stereoscopic rendering is performed using instanced rendering technique. */ INSTANCED, /** Stereoscopic rendering is performed using the multiview feature from the graphics backend. */ @@ -405,7 +408,7 @@ public static class Config { * * @see View#setStereoscopicOptions */ - public StereoscopicType stereoscopicType = StereoscopicType.INSTANCED; + public StereoscopicType stereoscopicType = StereoscopicType.NONE; /** * The number of eyes to render when stereoscopic rendering is enabled. Supported values are diff --git a/build.sh b/build.sh index f2c986344da..ab6440f45de 100755 --- a/build.sh +++ b/build.sh @@ -64,6 +64,9 @@ function print_help { echo " enabling debug paths in the backend from the build script. For example, make a" echo " systrace-enabled build without directly changing #defines. Remember to add -f when" echo " changing this option." + echo " -S type" + echo " Enable stereoscopic rendering where type is one of [instanced|multiview]. This is only" + echo " meant for building the samples." echo "" echo "Build types:" echo " release" @@ -175,6 +178,8 @@ ASAN_UBSAN_OPTION="" BACKEND_DEBUG_FLAG_OPTION="" +STEREOSCOPIC_OPTION="" + IOS_BUILD_SIMULATOR=false BUILD_UNIVERSAL_LIBRARIES=false @@ -234,6 +239,7 @@ function build_desktop_target { ${MATOPT_OPTION} \ ${ASAN_UBSAN_OPTION} \ ${BACKEND_DEBUG_FLAG_OPTION} \ + ${STEREOSCOPIC_OPTION} \ ${architectures} \ ../.. ln -sf "out/cmake-${lc_target}/compile_commands.json" \ @@ -368,6 +374,7 @@ function build_android_target { ${MATOPT_OPTION} \ ${VULKAN_ANDROID_OPTION} \ ${BACKEND_DEBUG_FLAG_OPTION} \ + ${STEREOSCOPIC_OPTION} \ ../.. ln -sf "out/cmake-android-${lc_target}-${arch}/compile_commands.json" \ ../../compile_commands.json @@ -602,7 +609,7 @@ function build_ios_target { -DCMAKE_TOOLCHAIN_FILE=../../third_party/clang/iOS.cmake \ ${MATDBG_OPTION} \ ${MATOPT_OPTION} \ - ${BACKEND_DEBUG_FLAG_OPTION} \ + ${STEREOSCOPIC_OPTION} \ ../.. ln -sf "out/cmake-ios-${lc_target}-${arch}/compile_commands.json" \ ../../compile_commands.json @@ -789,7 +796,7 @@ function check_debug_release_build { pushd "$(dirname "$0")" > /dev/null -while getopts ":hacCfgijmp:q:uvslwedk:bx:" opt; do +while getopts ":hacCfgijmp:q:uvslwedk:bx:S:" opt; do case ${opt} in h) print_help @@ -929,6 +936,20 @@ while getopts ":hacCfgijmp:q:uvslwedk:bx:" opt; do ;; x) BACKEND_DEBUG_FLAG_OPTION="-DFILAMENT_BACKEND_DEBUG_FLAG=${OPTARG}" ;; + S) case $(echo "${OPTARG}" | tr '[:upper:]' '[:lower:]') in + instanced) + STEREOSCOPIC_OPTION="-DFILAMENT_SAMPLES_STEREO_TYPE=instanced" + ;; + multiview) + STEREOSCOPIC_OPTION="-DFILAMENT_SAMPLES_STEREO_TYPE=multiview" + ;; + *) + echo "Unknown stereoscopic type ${OPTARG}" + echo "Type must be one of [instanced|multiview]" + echo "" + exit 1 + esac + ;; \?) echo "Invalid option: -${OPTARG}" >&2 echo "" diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index 490b889fa2e..c6965a17e5b 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -22,6 +22,7 @@ #include #include // Because we define ERROR in the FenceStatus enum. +#include #include #include @@ -1250,13 +1251,7 @@ enum class Workaround : uint16_t { POWER_VR_SHADER_WORKAROUNDS, }; -//! The type of technique for stereoscopic rendering -enum class StereoscopicType : uint8_t { - // Stereoscopic rendering is performed using instanced rendering technique. - INSTANCED, - // Stereoscopic rendering is performed using the multiview feature from the graphics backend. - MULTIVIEW, -}; +using StereoscopicType = backend::Platform::StereoscopicType; } // namespace filament::backend diff --git a/filament/backend/include/backend/Platform.h b/filament/backend/include/backend/Platform.h index 45b0bb82683..4f73c4375cd 100644 --- a/filament/backend/include/backend/Platform.h +++ b/filament/backend/include/backend/Platform.h @@ -41,6 +41,26 @@ class UTILS_PUBLIC Platform { struct Fence {}; struct Stream {}; + /** + * The type of technique for stereoscopic rendering. (Note that the materials used will need to + * be compatible with the chosen technique.) + */ + enum class StereoscopicType : uint8_t { + /** + * No stereoscopic rendering + */ + NONE, + /** + * Stereoscopic rendering is performed using instanced rendering technique. + */ + INSTANCED, + /** + * Stereoscopic rendering is performed using the multiview feature from the graphics + * backend. + */ + MULTIVIEW, + }; + struct DriverConfig { /** * Size of handle arena in bytes. Setting to 0 indicates default value is to be used. @@ -71,6 +91,11 @@ class UTILS_PUBLIC Platform { * GLES 3.x backends. */ bool forceGLES2Context = false; + + /** + * Sets the technique for stereoscopic rendering. + */ + StereoscopicType stereoscopicType = StereoscopicType::NONE; }; Platform() noexcept; diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index bb64f3d4367..b92a07f37b4 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -300,7 +300,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameTimeSupported) DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAutoDepthResolveSupported) DECL_DRIVER_API_SYNCHRONOUS_0(bool, isSRGBSwapChainSupported) DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedContentSupported) -DECL_DRIVER_API_SYNCHRONOUS_N(bool, isStereoSupported, backend::StereoscopicType, stereoscopicType) +DECL_DRIVER_API_SYNCHRONOUS_0(bool, isStereoSupported) DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported) DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported) DECL_DRIVER_API_SYNCHRONOUS_N(bool, isDepthStencilBlitSupported, backend::TextureFormat, format) diff --git a/filament/backend/src/metal/MetalDriver.h b/filament/backend/src/metal/MetalDriver.h index 51cd7902ddb..e38b65b3755 100644 --- a/filament/backend/src/metal/MetalDriver.h +++ b/filament/backend/src/metal/MetalDriver.h @@ -17,6 +17,7 @@ #ifndef TNT_FILAMENT_DRIVER_METALDRIVER_H #define TNT_FILAMENT_DRIVER_METALDRIVER_H +#include #include "private/backend/Driver.h" #include "DriverBase.h" @@ -140,6 +141,7 @@ class MetalDriver final : public DriverBase { void enumerateBoundBuffers(BufferObjectBinding bindingType, const std::function& f); + backend::StereoscopicType const mStereoscopicType; }; } // namespace backend diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 0172be1ce19..043a7b02dbe 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -102,7 +102,8 @@ mContext(new MetalContext(driverConfig.textureUseAfterFreePoolSize)), mHandleAllocator("Handles", driverConfig.handleArenaSize, - driverConfig.disableHandleUseAfterFreeCheck) { + driverConfig.disableHandleUseAfterFreeCheck), + mStereoscopicType(driverConfig.stereoscopicType) { mContext->driver = this; ScopedAllocationTimer::setPlatform(platform); @@ -803,13 +804,15 @@ return false; } -bool MetalDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) { - switch (stereoscopicType) { - case backend::StereoscopicType::INSTANCED: - return true; - case backend::StereoscopicType::MULTIVIEW: - // TODO: implement multiview feature in Metal. - return false; +bool MetalDriver::isStereoSupported() { + switch (mStereoscopicType) { + case backend::StereoscopicType::INSTANCED: + return true; + case backend::StereoscopicType::MULTIVIEW: + // TODO: implement multiview feature in Metal. + return false; + case backend::StereoscopicType::NONE: + return false; } } diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index 4b4b73fe185..ea5c526402d 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -182,7 +182,7 @@ bool NoopDriver::isProtectedContentSupported() { return false; } -bool NoopDriver::isStereoSupported(backend::StereoscopicType) { +bool NoopDriver::isStereoSupported() { return false; } diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 38d82a68662..884db62459e 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -2050,19 +2050,19 @@ bool OpenGLDriver::isProtectedContentSupported() { return mPlatform.isProtectedContextSupported(); } -bool OpenGLDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) { +bool OpenGLDriver::isStereoSupported() { // Instanced-stereo requires instancing and EXT_clip_cull_distance. // Multiview-stereo requires ES 3.0 and OVR_multiview2. if (UTILS_UNLIKELY(mContext.isES2())) { return false; } - switch (stereoscopicType) { - case backend::StereoscopicType::INSTANCED: - return mContext.ext.EXT_clip_cull_distance; - case backend::StereoscopicType::MULTIVIEW: - return mContext.ext.OVR_multiview2; - default: - return false; + switch (mDriverConfig.stereoscopicType) { + case backend::StereoscopicType::INSTANCED: + return mContext.ext.EXT_clip_cull_distance; + case backend::StereoscopicType::MULTIVIEW: + return mContext.ext.OVR_multiview2; + case backend::StereoscopicType::NONE: + return false; } } diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index e0f62822bf2..e9c4f499ce9 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -228,7 +228,8 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex mBlitter(mPlatform->getPhysicalDevice(), &mCommands), mReadPixels(mPlatform->getDevice()), mDescriptorSetManager(mPlatform->getDevice(), &mResourceAllocator), - mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported) { + mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported), + mStereoscopicType(driverConfig.stereoscopicType) { #if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) DebugUtils::mSingleton = @@ -899,13 +900,14 @@ bool VulkanDriver::isProtectedContentSupported() { return false; } -bool VulkanDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) { - switch (stereoscopicType) { - case backend::StereoscopicType::INSTANCED: - return true; - case backend::StereoscopicType::MULTIVIEW: - // TODO: implement multiview feature in Vulkan. - return false; +bool VulkanDriver::isStereoSupported() { + switch (mStereoscopicType) { + case backend::StereoscopicType::INSTANCED: + return true; + case backend::StereoscopicType::MULTIVIEW: + // TODO: implement multiview feature in Vulkan. + case backend::StereoscopicType::NONE: + return false; } } diff --git a/filament/backend/src/vulkan/VulkanDriver.h b/filament/backend/src/vulkan/VulkanDriver.h index 2ededd06c2c..abdbc6304f6 100644 --- a/filament/backend/src/vulkan/VulkanDriver.h +++ b/filament/backend/src/vulkan/VulkanDriver.h @@ -28,6 +28,7 @@ #include "VulkanSamplerCache.h" #include "VulkanStagePool.h" #include "VulkanUtility.h" +#include "backend/DriverEnums.h" #include "caching/VulkanDescriptorSetManager.h" #include "caching/VulkanPipelineLayoutCache.h" @@ -166,10 +167,10 @@ class VulkanDriver final : public DriverBase { VkPipelineLayout pipelineLayout; }; BoundPipeline mBoundPipeline = {}; - RenderPassFboBundle mRenderPassFboInfo; bool const mIsSRGBSwapChainSupported; + backend::StereoscopicType const mStereoscopicType; }; } // namespace filament::backend diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index e77fc5f45f1..bade124fbe0 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -315,7 +315,7 @@ class UTILS_PUBLIC Engine { * * @see View::setStereoscopicOptions */ - StereoscopicType stereoscopicType = StereoscopicType::INSTANCED; + StereoscopicType stereoscopicType = StereoscopicType::NONE; /* * The number of eyes to render when stereoscopic rendering is enabled. Supported values are diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp index ca370bc85cc..75e16b109b3 100644 --- a/filament/src/Engine.cpp +++ b/filament/src/Engine.cpp @@ -355,7 +355,7 @@ const Engine::Config& Engine::getConfig() const noexcept { } bool Engine::isStereoSupported(StereoscopicType stereoscopicType) const noexcept { - return downcast(this)->isStereoSupported(stereoscopicType); + return downcast(this)->isStereoSupported(); } size_t Engine::getMaxStereoscopicEyes() noexcept { diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index 0f8077bb092..fa75cf93edb 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -103,7 +103,8 @@ Engine* FEngine::create(Engine::Builder const& builder) { .textureUseAfterFreePoolSize = instance->getConfig().textureUseAfterFreePoolSize, .disableParallelShaderCompile = instance->getConfig().disableParallelShaderCompile, .disableHandleUseAfterFreeCheck = instance->getConfig().disableHandleUseAfterFreeCheck, - .forceGLES2Context = instance->getConfig().forceGLES2Context + .forceGLES2Context = instance->getConfig().forceGLES2Context, + .stereoscopicType = instance->getConfig().stereoscopicType, }; instance->mDriver = platform->createDriver(sharedContext, driverConfig); @@ -352,6 +353,7 @@ void FEngine::init() { FMaterial::DefaultMaterialBuilder defaultMaterialBuilder; switch (mConfig.stereoscopicType) { + case StereoscopicType::NONE: case StereoscopicType::INSTANCED: defaultMaterialBuilder.package( MATERIALS_DEFAULTMATERIAL_DATA, MATERIALS_DEFAULTMATERIAL_SIZE); @@ -675,7 +677,8 @@ int FEngine::loop() { .textureUseAfterFreePoolSize = mConfig.textureUseAfterFreePoolSize, .disableParallelShaderCompile = mConfig.disableParallelShaderCompile, .disableHandleUseAfterFreeCheck = mConfig.disableHandleUseAfterFreeCheck, - .forceGLES2Context = mConfig.forceGLES2Context + .forceGLES2Context = mConfig.forceGLES2Context, + .stereoscopicType = mConfig.stereoscopicType, }; mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig); diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index 5d72ac9c2ee..a0de5720b20 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -184,8 +184,8 @@ class FEngine : public Engine { return CONFIG_MAX_INSTANCES; } - bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept { - return getDriver().isStereoSupported(stereoscopicType); + bool isStereoSupported() const noexcept { + return getDriver().isStereoSupported(); } static size_t getMaxStereoscopicEyes() noexcept { diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 80011d7f318..e0d2f86984f 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -385,8 +385,7 @@ void FMaterial::compile(CompilerPriorityQueue priority, utils::Invocable&& callback) noexcept { // Turn off the STE variant if stereo is not supported. - const StereoscopicType stereoscopicType = mEngine.getConfig().stereoscopicType; - if (!mEngine.getDriverApi().isStereoSupported(stereoscopicType)) { + if (!mEngine.getDriverApi().isStereoSupported()) { variantSpec &= ~UserVariantFilterMask(UserVariantFilterBit::STE); } diff --git a/filament/src/details/Skybox.cpp b/filament/src/details/Skybox.cpp index 311e5f4b2fa..07da6280514 100644 --- a/filament/src/details/Skybox.cpp +++ b/filament/src/details/Skybox.cpp @@ -132,6 +132,7 @@ FMaterial const* FSkybox::createMaterial(FEngine& engine) { #endif { switch (engine.getConfig().stereoscopicType) { + case Engine::StereoscopicType::NONE: case Engine::StereoscopicType::INSTANCED: builder.package(MATERIALS_SKYBOX_DATA, MATERIALS_SKYBOX_SIZE); break; @@ -139,7 +140,8 @@ FMaterial const* FSkybox::createMaterial(FEngine& engine) { #ifdef FILAMENT_ENABLE_MULTIVIEW builder.package(MATERIALS_SKYBOX_MULTIVIEW_DATA, MATERIALS_SKYBOX_MULTIVIEW_SIZE); #else - assert_invariant(false); + PANIC_POSTCONDITION("Multiview is enabled in the Engine, but this build has not " + "been compiled for multiview."); #endif break; } diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp index 19b50ce7762..7b7d160cb4d 100644 --- a/filament/src/details/View.cpp +++ b/filament/src/details/View.cpp @@ -60,7 +60,7 @@ static constexpr float PID_CONTROLLER_Kd = 0.0f; FView::FView(FEngine& engine) : mFroxelizer(engine), mFogEntity(engine.getEntityManager().create()), - mIsStereoSupported(engine.getDriverApi().isStereoSupported(engine.getConfig().stereoscopicType)), + mIsStereoSupported(engine.getDriverApi().isStereoSupported()), mPerViewUniforms(engine) { DriverApi& driver = engine.getDriverApi(); diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 6765b122f51..5ee352a9c9e 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -539,15 +539,16 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader, if (config.variant.hasStereo() && config.shaderType == ShaderStage::VERTEX) { switch (config.materialInfo->stereoscopicType) { - case StereoscopicType::INSTANCED: - // Nothing to generate - break; case StereoscopicType::MULTIVIEW: // For stereo variants using multiview feature, this generates the shader code below. // #extension GL_OVR_multiview2 : require // layout(num_views = 2) in; glslOptions.ovr_multiview_view_count = config.materialInfo->stereoscopicEyeCount; break; + case StereoscopicType::INSTANCED: + case StereoscopicType::NONE: + // Nothing to generate + break; } } diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index 09a085a4076..75d2b940d8c 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -78,6 +78,8 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade case StereoscopicType::MULTIVIEW: out << "#extension GL_OVR_multiview2 : require\n"; break; + case StereoscopicType::NONE: + break; } } break; @@ -99,6 +101,8 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade case StereoscopicType::MULTIVIEW: out << "#extension GL_OVR_multiview2 : require\n"; break; + case StereoscopicType::NONE: + break; } } break; @@ -120,6 +124,8 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade case StereoscopicType::MULTIVIEW: out << "layout(num_views = " << material.stereoscopicEyeCount << ") in;\n"; break; + case StereoscopicType::NONE: + break; } } @@ -217,6 +223,8 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade case StereoscopicType::MULTIVIEW: generateDefine(out, "FILAMENT_STEREO_MULTIVIEW", true); break; + case StereoscopicType::NONE: + break; } if (stage == ShaderStage::VERTEX) { diff --git a/libs/filamentapp/src/FilamentApp.cpp b/libs/filamentapp/src/FilamentApp.cpp index 043fb4c950e..635f482ccfe 100644 --- a/libs/filamentapp/src/FilamentApp.cpp +++ b/libs/filamentapp/src/FilamentApp.cpp @@ -619,6 +619,8 @@ FilamentApp::Window::Window(FilamentApp* filamentApp, engineConfig.stereoscopicType = Engine::StereoscopicType::INSTANCED; #elif defined (FILAMENT_SAMPLES_STEREO_TYPE_MULTIVIEW) engineConfig.stereoscopicType = Engine::StereoscopicType::MULTIVIEW; +#else + engineConfig.stereoscopicType = Engine::StereoscopicType::NONE; #endif if (backend == Engine::Backend::VULKAN) { diff --git a/libs/viewer/CMakeLists.txt b/libs/viewer/CMakeLists.txt index 5d2a13ecbcd..58fe66c98fe 100644 --- a/libs/viewer/CMakeLists.txt +++ b/libs/viewer/CMakeLists.txt @@ -34,6 +34,12 @@ target_link_libraries(${TARGET} PUBLIC imgui filament gltfio_core filagui jsmn c target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR}) set_target_properties(${TARGET} PROPERTIES FOLDER Libs) +if (FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "instanced") + add_definitions(-DFILAMENT_SAMPLES_STEREO_TYPE_INSTANCED) +elseif (FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview") + add_definitions(-DFILAMENT_SAMPLES_STEREO_TYPE_MULTIVIEW) +endif () + # ================================================================================================== # Compiler flags # ================================================================================================== diff --git a/libs/viewer/src/ViewerGui.cpp b/libs/viewer/src/ViewerGui.cpp index 216288d3f86..d29868c1850 100644 --- a/libs/viewer/src/ViewerGui.cpp +++ b/libs/viewer/src/ViewerGui.cpp @@ -1094,9 +1094,17 @@ void ViewerGui::updateUserInterface() { ImGui::ListBox("Cameras", &mCurrentCamera, cstrings.data(), cstrings.size()); } - ImGui::Checkbox("Instanced stereo", &mSettings.view.stereoscopicOptions.enabled); - ImGui::SliderFloat( - "Ocular distance", &mSettings.viewer.cameraEyeOcularDistance, 0.0f, 1.0f); +#if defined(FILAMENT_SAMPLES_STEREO_TYPE_INSTANCED) \ + || defined(FILAMENT_SAMPLES_STEREO_TYPE_MULTIVIEW) + ImGui::Checkbox("Stereo mode", &mSettings.view.stereoscopicOptions.enabled); + + #if defined(FILAMENT_SAMPLES_STEREO_TYPE_MULTIVIEW) + *debug.getPropertyAddress("d.stereo.combine_multiview_images") + = mSettings.view.stereoscopicOptions.enabled; + #endif +#endif + ImGui::SliderFloat("Ocular distance", &mSettings.viewer.cameraEyeOcularDistance, 0.0f, + 1.0f); float toeInDegrees = mSettings.viewer.cameraEyeToeIn / f::PI * 180.0f; ImGui::SliderFloat("Toe in", &toeInDegrees, 0.0f, 30.0, "%.3f°"); diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index 80a06298e71..b846fea6f93 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -893,11 +893,6 @@ int main(int argc, char** argv) { "d.shadowmap.display_shadow_texture_channel"), 0, 3); ImGui::Unindent(); } -#if defined(FILAMENT_SAMPLES_STEREO_TYPE_MULTIVIEW) - ImGui::Checkbox("Combine Multiview Images", - debug.getPropertyAddress("d.stereo.combine_multiview_images")); -#endif - bool debugFroxelVisualization; if (debug.getProperty("d.lighting.debug_froxel_visualization", &debugFroxelVisualization)) { From 5b80407f6c1d77f53809c2d03a74f84cff173187 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Thu, 16 May 2024 13:20:12 -0700 Subject: [PATCH 11/23] Implement push constants for Metal (#7858) --- filament/backend/CMakeLists.txt | 1 + .../backend/include/backend/DriverEnums.h | 2 +- filament/backend/src/metal/MetalContext.h | 14 ++ filament/backend/src/metal/MetalContext.mm | 63 +++++++ filament/backend/src/metal/MetalDriver.mm | 21 ++- filament/backend/src/metal/MetalState.h | 6 +- filament/backend/test/test_PushConstants.cpp | 168 ++++++++++++++++++ libs/filamat/src/GLSLPostProcessor.cpp | 11 ++ 8 files changed, 282 insertions(+), 4 deletions(-) create mode 100644 filament/backend/test/test_PushConstants.cpp diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index 34cc4b5d59b..6a7f66cc2fd 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -419,6 +419,7 @@ if (APPLE OR LINUX) test/test_BufferUpdates.cpp test/test_Callbacks.cpp test/test_MRT.cpp + test/test_PushConstants.cpp test/test_LoadImage.cpp test/test_StencilBuffer.cpp test/test_Scissor.cpp diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index c6965a17e5b..d69a9991d28 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -118,7 +118,7 @@ static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT, "The number of buffer objects that can be attached to a VertexBuffer must be " "less than or equal to the maximum number of vertex attributes."); -static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 10; // This is guaranteed by OpenGL ES. +static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 9; // This is guaranteed by OpenGL ES. static constexpr size_t CONFIG_SAMPLER_BINDING_COUNT = 4; // This is guaranteed by OpenGL ES. /** diff --git a/filament/backend/src/metal/MetalContext.h b/filament/backend/src/metal/MetalContext.h index 33b8f92c829..8a14d9a36c8 100644 --- a/filament/backend/src/metal/MetalContext.h +++ b/filament/backend/src/metal/MetalContext.h @@ -55,6 +55,18 @@ struct MetalVertexBuffer; constexpr static uint8_t MAX_SAMPLE_COUNT = 8; // Metal devices support at most 8 MSAA samples +class MetalPushConstantBuffer { +public: + void setPushConstant(PushConstantVariant value, uint8_t index); + bool isDirty() const { return mDirty; } + void setBytes(id encoder, ShaderStage stage); + void clear(); + +private: + std::vector mPushConstants; + bool mDirty = false; +}; + struct MetalContext { explicit MetalContext(size_t metalFreedTextureListSize) : texturesToDestroy(metalFreedTextureListSize) {} @@ -109,6 +121,8 @@ struct MetalContext { PolygonOffset currentPolygonOffset = {0.0f, 0.0f}; + std::array currentPushConstants; + MetalSamplerGroup* samplerBindings[Program::SAMPLER_BINDING_COUNT] = {}; // Keeps track of sampler groups we've finalized for the current render pass. diff --git a/filament/backend/src/metal/MetalContext.mm b/filament/backend/src/metal/MetalContext.mm index fce98eb5237..3997944521c 100644 --- a/filament/backend/src/metal/MetalContext.mm +++ b/filament/backend/src/metal/MetalContext.mm @@ -153,5 +153,68 @@ bool isInRenderPass(MetalContext* context) { return context->currentRenderPassEncoder != nil; } +void MetalPushConstantBuffer::setPushConstant(PushConstantVariant value, uint8_t index) { + if (mPushConstants.size() <= index) { + mPushConstants.resize(index + 1); + mDirty = true; + } + if (UTILS_LIKELY(mPushConstants[index] != value)) { + mDirty = true; + mPushConstants[index] = value; + } +} + +void MetalPushConstantBuffer::setBytes(id encoder, ShaderStage stage) { + constexpr size_t PUSH_CONSTANT_SIZE_BYTES = 4; + constexpr size_t PUSH_CONSTANT_BUFFER_INDEX = 26; + + static char buffer[MAX_PUSH_CONSTANT_COUNT * PUSH_CONSTANT_SIZE_BYTES]; + assert_invariant(mPushConstants.size() <= MAX_PUSH_CONSTANT_COUNT); + + size_t bufferSize = PUSH_CONSTANT_SIZE_BYTES * mPushConstants.size(); + for (size_t i = 0; i < mPushConstants.size(); i++) { + const auto& constant = mPushConstants[i]; + std::visit( + [i](auto arg) { + if constexpr (std::is_same_v) { + // bool push constants are converted to uints in MSL. + // We must ensure we write all the bytes for boolean values to work + // correctly. + uint32_t boolAsUint = arg ? 0x00000001 : 0x00000000; + *(reinterpret_cast(buffer + PUSH_CONSTANT_SIZE_BYTES * i)) = + boolAsUint; + } else { + *(decltype(arg)*)(buffer + PUSH_CONSTANT_SIZE_BYTES * i) = arg; + } + }, + constant); + } + + switch (stage) { + case ShaderStage::VERTEX: + [(id)encoder setVertexBytes:buffer + length:bufferSize + atIndex:PUSH_CONSTANT_BUFFER_INDEX]; + break; + case ShaderStage::FRAGMENT: + [(id)encoder setFragmentBytes:buffer + length:bufferSize + atIndex:PUSH_CONSTANT_BUFFER_INDEX]; + break; + case ShaderStage::COMPUTE: + [(id)encoder setBytes:buffer + length:bufferSize + atIndex:PUSH_CONSTANT_BUFFER_INDEX]; + break; + } + + mDirty = false; +} + +void MetalPushConstantBuffer::clear() { + mPushConstants.clear(); + mDirty = false; +} + } // namespace backend } // namespace filament diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 043a7b02dbe..4662b8b953a 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -1112,6 +1112,10 @@ mContext->currentPolygonOffset = {0.0f, 0.0f}; mContext->finalizedSamplerGroups.clear(); + + for (auto& pc : mContext->currentPushConstants) { + pc.clear(); + } } void MetalDriver::nextSubpass(int dummy) {} @@ -1260,7 +1264,14 @@ } void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index, - backend::PushConstantVariant value) {} + backend::PushConstantVariant value) { + ASSERT_PRECONDITION( + isInRenderPass(mContext), "setPushConstant must be called inside a render pass."); + assert_invariant(static_cast(stage) < mContext->currentPushConstants.size()); + MetalPushConstantBuffer& pushConstants = + mContext->currentPushConstants[static_cast(stage)]; + pushConstants.setPushConstant(value, index); +} void MetalDriver::insertEventMarker(const char* string, uint32_t len) { @@ -1868,6 +1879,14 @@ UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT, uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT); + // Update push constants. + for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) { + auto& pushConstants = mContext->currentPushConstants[i]; + if (UTILS_UNLIKELY(pushConstants.isDirty())) { + pushConstants.setBytes(mContext->currentRenderPassEncoder, static_cast(i)); + } + } + auto primitive = handle_cast(mContext->currentRenderPrimitive); MetalIndexBuffer* indexBuffer = primitive->indexBuffer; diff --git a/filament/backend/src/metal/MetalState.h b/filament/backend/src/metal/MetalState.h index 82de98d6961..83579cb5d50 100644 --- a/filament/backend/src/metal/MetalState.h +++ b/filament/backend/src/metal/MetalState.h @@ -43,7 +43,8 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) { // ------------------------------------------------------ // 0 Zero buffer (placeholder vertex buffer) 1 // 1-16 Filament vertex buffers 16 limited by MAX_VERTEX_BUFFER_COUNT -// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT +// 17-25 Uniform buffers 9 Program::UNIFORM_BINDING_COUNT +// 26 Push constants 1 // 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT // // Total 31 @@ -53,7 +54,8 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) { // Bindings Buffer name Count // ------------------------------------------------------ // 0-3 SSBO buffers 4 MAX_SSBO_COUNT -// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT +// 17-25 Uniform buffers 9 Program::UNIFORM_BINDING_COUNT +// 26 Push constants 1 // 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT // // Total 18 diff --git a/filament/backend/test/test_PushConstants.cpp b/filament/backend/test/test_PushConstants.cpp new file mode 100644 index 00000000000..80253fdf07e --- /dev/null +++ b/filament/backend/test/test_PushConstants.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2024 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 "BackendTest.h" + +#include "ShaderGenerator.h" +#include "TrianglePrimitive.h" + +#include + +namespace test { + +using namespace filament; +using namespace filament::backend; + +static constexpr struct { + size_t TRIANGLE_HIDE = 0; + size_t TRIANGLE_SCALE = 1; + size_t TRIANGLE_OFFSET_X = 2; + size_t TRIANGLE_OFFSET_Y = 3; + + size_t RED = 0; + size_t GREEN = 2; + size_t BLUE = 3; +} pushConstantIndex; + +static const char* const triangleVs = R"(#version 450 core + +layout(push_constant) uniform Constants { + bool hideTriangle; + float triangleScale; + float triangleOffsetX; + float triangleOffsetY; +} pushConstants; + +layout(location = 0) in vec4 mesh_position; +void main() { + if (pushConstants.hideTriangle) { + // Test that bools are written correctly. All bits must be 0 if the bool is false. + gl_Position = vec4(0.0); + return; + } + gl_Position = vec4(mesh_position.xy * pushConstants.triangleScale + + vec2(pushConstants.triangleOffsetX, pushConstants.triangleOffsetY), 0.0, 1.0); +#if defined(TARGET_VULKAN_ENVIRONMENT) + // In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up. + gl_Position.y = -gl_Position.y; +#endif +})"; + +static const char* const triangleFs = R"(#version 450 core + +layout(push_constant) uniform Constants { + float red; + bool padding; // test correct bool padding + float green; + float blue; +} pushConstants; + +precision mediump int; precision highp float; +layout(location = 0) out vec4 fragColor; +void main() { + fragColor = vec4(pushConstants.red, pushConstants.green, pushConstants.blue, 1.0); +})"; + +TEST_F(BackendTest, PushConstants) { + auto& api = getDriverApi(); + + api.startCapture(0); + + // The test is executed within this block scope to force destructors to run before + // executeCommands(). + { + // Create a SwapChain and make it current. + auto swapChain = createSwapChain(); + api.makeCurrent(swapChain, swapChain); + + // Create a program. + ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform); + Program p = shaderGen.getProgram(api); + ProgramHandle program = api.createProgram(std::move(p)); + + Handle renderTarget = api.createDefaultRenderTarget(); + + TrianglePrimitive triangle(api); + + RenderPassParams params = {}; + params.flags.clear = TargetBufferFlags::COLOR0; + params.viewport = { 0, 0, 512, 512 }; + params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f); + params.flags.discardStart = TargetBufferFlags::ALL; + params.flags.discardEnd = TargetBufferFlags::NONE; + + PipelineState ps = {}; + ps.program = program; + ps.rasterState.colorWrite = true; + ps.rasterState.depthWrite = false; + + api.makeCurrent(swapChain, swapChain); + api.beginFrame(0, 0, 0); + + api.beginRenderPass(renderTarget, params); + + // Set the push constants to scale the triangle in half + api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_HIDE, false); + api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_SCALE, 0.5f); + api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_X, 0.0f); + api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_Y, 0.0f); + api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.RED, 0.25f); + api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.GREEN, 0.5f); + api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.BLUE, 1.0f); + api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1); + + // Draw another triangle, transposed to the upper-right. + api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_X, 0.5f); + api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_Y, 0.5f); + + api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.RED, 1.00f); + api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.GREEN, 0.5f); + api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.BLUE, 0.25f); + + api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1); + + // Draw a final triangle, transposed to the lower-left. + api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_X, -0.5f); + api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_Y, -0.5f); + + api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.RED, 0.5f); + api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.GREEN, 0.25f); + api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.BLUE, 1.00f); + + api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1); + + api.endRenderPass(); + + readPixelsAndAssertHash("pushConstants", 512, 512, renderTarget, 1957275826, true); + + api.commit(swapChain); + api.endFrame(0); + + // Cleanup. + api.destroySwapChain(swapChain); + api.destroyRenderTarget(renderTarget); + } + + api.stopCapture(0); + + // Wait for the ReadPixels result to come back. + api.finish(); + + executeCommands(); + getDriver().purge(); +} + +} // namespace test diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 5ee352a9c9e..3a4a4824e0e 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -255,6 +255,17 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob *spirv, std::string *outMsl, mslCompiler.add_msl_resource_binding(argBufferBinding); } + // Bind push constants to [buffer(26)] + MSLResourceBinding pushConstantBinding; + // the baseType doesn't matter, but can't be UNKNOWN + pushConstantBinding.basetype = SPIRType::BaseType::Struct; + pushConstantBinding.stage = executionModel; + pushConstantBinding.desc_set = kPushConstDescSet; + pushConstantBinding.binding = kPushConstBinding; + pushConstantBinding.count = 1; + pushConstantBinding.msl_buffer = 26; + mslCompiler.add_msl_resource_binding(pushConstantBinding); + auto updateResourceBindingDefault = [executionModel, &mslCompiler](const auto& resource) { auto set = mslCompiler.get_decoration(resource.id, spv::DecorationDescriptorSet); auto binding = mslCompiler.get_decoration(resource.id, spv::DecorationBinding); From 18ccf0cd8d277f87d0c385305cf8ba61b6e105c9 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 16 May 2024 14:11:07 -0700 Subject: [PATCH 12/23] change the morphing API so it uses only one buffer per renderable The current API allowed to have a buffer for each primitive in a renderable. We instead restrict the API so that there is a single MorphTargetBuffer for the whole renderable, shared by all primitives. The buffer can be shared thanks to the "offset" parameter on setMorphTargetBufferAt(). Also - fix FMorphTargetBuffer::updateDataAt() - add support for the "offset" parameter of setMorphTargetBufferAt() --- .../src/main/cpp/RenderableManager.cpp | 27 +++++-- .../android/filament/RenderableManager.java | 59 ++++++++++---- filament/include/filament/RenderableManager.h | 78 +++++++++++-------- filament/src/RenderPass.cpp | 4 + filament/src/RenderPass.h | 3 +- filament/src/RenderableManager.cpp | 5 ++ filament/src/components/RenderableManager.cpp | 32 +++++++- filament/src/components/RenderableManager.h | 2 + filament/src/details/MorphTargetBuffer.cpp | 10 +-- .../include/private/filament/EngineEnums.h | 4 +- samples/hellomorphing.cpp | 25 +++--- shaders/src/getters.vs | 6 +- 12 files changed, 174 insertions(+), 81 deletions(-) diff --git a/android/filament-android/src/main/cpp/RenderableManager.cpp b/android/filament-android/src/main/cpp/RenderableManager.cpp index 4e586179baa..db0255d414e 100644 --- a/android/filament-android/src/main/cpp/RenderableManager.cpp +++ b/android/filament-android/src/main/cpp/RenderableManager.cpp @@ -244,13 +244,25 @@ Java_com_google_android_filament_RenderableManager_nBuilderMorphing(JNIEnv*, jcl builder->morphing(targetCount); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_RenderableManager_nBuilderMorphingStandard(JNIEnv*, jclass, + jlong nativeBuilder, jlong nativeMorphTargetBuffer) { + RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder; + MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer; + builder->morphing(morphTargetBuffer); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_RenderableManager_nBuilderSetMorphTargetBufferAt(JNIEnv*, jclass, jlong nativeBuilder, int level, int primitiveIndex, jlong nativeMorphTargetBuffer, int offset, int count) { RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder; - MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer; - builder->morphing(level, primitiveIndex, morphTargetBuffer, offset, count); + if (nativeMorphTargetBuffer) { + MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer; + builder->morphing(level, primitiveIndex, morphTargetBuffer, offset, count); + } else { + builder->morphing(level, primitiveIndex, offset, count); + } } extern "C" JNIEXPORT void JNICALL @@ -326,9 +338,14 @@ Java_com_google_android_filament_RenderableManager_nSetMorphTargetBufferAt(JNIEn jclass, jlong nativeRenderableManager, jint i, int level, jint primitiveIndex, jlong nativeMorphTargetBuffer, jint offset, jint count) { RenderableManager *rm = (RenderableManager *) nativeRenderableManager; - MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer; - rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level, - (size_t) primitiveIndex, morphTargetBuffer, (size_t) offset, (size_t) count); + if (nativeMorphTargetBuffer) { + MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer; + rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level, + (size_t) primitiveIndex, morphTargetBuffer, (size_t) offset, (size_t) count); + } else { + rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level, + (size_t) primitiveIndex, (size_t) offset, (size_t) count); + } } extern "C" JNIEXPORT jint JNICALL diff --git a/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java b/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java index f7a6319d7a8..dfce1af4a16 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java +++ b/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java @@ -524,14 +524,7 @@ public Builder skinning(@IntRange(from = 0, to = 255) int boneCount, @NonNull Bu } /** - * Controls if the renderable has vertex morphing targets, zero by default. This is - * required to enable GPU morphing. - * - *

Filament supports two morphing modes: standard (default) and legacy.

- * - *

For standard morphing, A {@link MorphTargetBuffer} must be created and provided via - * {@link RenderableManager#setMorphTargetBufferAt}. Standard morphing supports up to - * CONFIG_MAX_MORPH_TARGET_COUNT morph targets.

+ * Controls if the renderable has legacy vertex morphing targets, zero by default. * * For legacy morphing, the attached {@link VertexBuffer} must provide data in the * appropriate {@link VertexBuffer.VertexAttribute} slots (MORPH_POSITION_0 etc). @@ -549,6 +542,22 @@ public Builder morphing(@IntRange(from = 0, to = 255) int targetCount) { return this; } + /** + * Controls if the renderable has vertex morphing targets, zero by default. + * + *

For standard morphing, A {@link MorphTargetBuffer} must be provided. + * Standard morphing supports up to + * CONFIG_MAX_MORPH_TARGET_COUNT morph targets.

+ * + *

See also {@link RenderableManager#setMorphWeights}, which can be called on a per-frame basis + * to advance the animation.

+ */ + @NonNull + public Builder morphing(@NonNull MorphTargetBuffer morphTargetBuffer) { + nBuilderMorphingStandard(mNativeBuilder, morphTargetBuffer.getNativeObject()); + return this; + } + /** * Specifies the morph target buffer for a primitive. * @@ -565,6 +574,17 @@ public Builder morphing(@IntRange(from = 0, to = 255) int targetCount) { * @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3) */ @NonNull + public Builder morphing(@IntRange(from = 0) int level, + @IntRange(from = 0) int primitiveIndex, + @IntRange(from = 0) int offset, + @IntRange(from = 0) int count) { + nBuilderSetMorphTargetBufferAt(mNativeBuilder, level, primitiveIndex, 0, offset, count); + return this; + } + + /** @deprecated */ + @Deprecated + @NonNull public Builder morphing(@IntRange(from = 0) int level, @IntRange(from = 0) int primitiveIndex, @NonNull MorphTargetBuffer morphTargetBuffer, @@ -575,10 +595,8 @@ public Builder morphing(@IntRange(from = 0) int level, return this; } - /** - * Utility method to specify morph target buffer for a primitive. - * For details, see the {@link RenderableManager.Builder#morphing}. - */ + /** @deprecated */ + @Deprecated @NonNull public Builder morphing(@IntRange(from = 0) int level, @IntRange(from = 0) int primitiveIndex, @@ -687,6 +705,16 @@ public void setMorphWeights(@EntityInstance int i, @NonNull float[] weights, @In * * @see Builder#morphing */ + public void setMorphTargetBufferAt(@EntityInstance int i, + @IntRange(from = 0) int level, + @IntRange(from = 0) int primitiveIndex, + @IntRange(from = 0) int offset, + @IntRange(from = 0) int count) { + nSetMorphTargetBufferAt(mNativeObject, i, level, primitiveIndex, 0, offset, count); + } + + /** @deprecated */ + @Deprecated public void setMorphTargetBufferAt(@EntityInstance int i, @IntRange(from = 0) int level, @IntRange(from = 0) int primitiveIndex, @@ -697,10 +725,8 @@ public void setMorphTargetBufferAt(@EntityInstance int i, morphTargetBuffer.getNativeObject(), offset, count); } - /** - * Utility method to change morph target buffer for the given primitive. - * For details, see the {@link RenderableManager#setMorphTargetBufferAt}. - */ + /** @deprecated */ + @Deprecated public void setMorphTargetBufferAt(@EntityInstance int i, @IntRange(from = 0) int level, @IntRange(from = 0) int primitiveIndex, @@ -1006,6 +1032,7 @@ public long getNativeObject() { private static native int nBuilderSkinningBones(long nativeBuilder, int boneCount, Buffer bones, int remaining); private static native void nBuilderSkinningBuffer(long nativeBuilder, long nativeSkinningBuffer, int boneCount, int offset); private static native void nBuilderMorphing(long nativeBuilder, int targetCount); + private static native void nBuilderMorphingStandard(long nativeBuilder, long nativeMorphTargetBuffer); private static native void nBuilderSetMorphTargetBufferAt(long nativeBuilder, int level, int primitiveIndex, long nativeMorphTargetBuffer, int offset, int count); private static native void nBuilderEnableSkinningBuffers(long nativeBuilder, boolean enabled); private static native void nBuilderFog(long nativeBuilder, boolean enabled); diff --git a/filament/include/filament/RenderableManager.h b/filament/include/filament/RenderableManager.h index bb50b7d1db8..363ef2d7c1f 100644 --- a/filament/include/filament/RenderableManager.h +++ b/filament/include/filament/RenderableManager.h @@ -464,15 +464,30 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { Builder& boneIndicesAndWeights(size_t primitiveIndex, utils::FixedCapacityVector< utils::FixedCapacityVector> indicesAndWeightsVector) noexcept; + + /** + * Controls if the renderable has legacy vertex morphing targets, zero by default. This is + * required to enable GPU morphing. + * + * For legacy morphing, the attached VertexBuffer must provide data in the + * appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only + * supports up to 4 morph targets and will be deprecated in the future. Legacy morphing must + * be enabled on the material definition: either via the legacyMorphing material attribute + * or by calling filamat::MaterialBuilder::useLegacyMorphing(). + * + * See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis + * to advance the animation. + */ + Builder& morphing(size_t targetCount) noexcept; + /** * Controls if the renderable has vertex morphing targets, zero by default. This is * required to enable GPU morphing. * * Filament supports two morphing modes: standard (default) and legacy. * - * For standard morphing, A MorphTargetBuffer must be created and provided via - * RenderableManager::setMorphTargetBufferAt(). Standard morphing supports up to - * \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets. + * For standard morphing, A MorphTargetBuffer must be provided. + * Standard morphing supports up to \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets. * * For legacy morphing, the attached VertexBuffer must provide data in the * appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only @@ -483,29 +498,35 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { * See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis * to advance the animation. */ - Builder& morphing(size_t targetCount) noexcept; + Builder& morphing(MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept; /** - * Specifies the morph target buffer for a primitive. - * - * The morph target buffer must have an associated renderable and geometry. Two conditions - * must be met: - * 1. The number of morph targets in the buffer must equal the renderable's morph target - * count. - * 2. The vertex count of each morph target must equal the geometry's vertex count. + * @deprecated Use morphing(uint8_t level, size_t primitiveIndex, size_t offset, size_t count) instead + */ + Builder& morphing(uint8_t level, size_t primitiveIndex, + MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, + size_t offset, size_t count) noexcept; + + /** + * @deprecated Use morphing(uint8_t level, size_t primitiveIndex, size_t offset, size_t count) instead + */ + inline Builder& morphing(uint8_t level, size_t primitiveIndex, + MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept { + return morphing(level, primitiveIndex, morphTargetBuffer, 0, + morphTargetBuffer->getVertexCount()); + } + + /** + * Specifies the the range of the MorphTargetBuffer to use with this primitive. * * @param level the level of detail (lod), only 0 can be specified * @param primitiveIndex zero-based index of the primitive, must be less than the count passed to Builder constructor - * @param morphTargetBuffer specifies the morph target buffer * @param offset specifies where in the morph target buffer to start reading (expressed as a number of vertices) * @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3) */ Builder& morphing(uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, size_t offset, size_t count) noexcept; - inline Builder& morphing(uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept; /** * Sets the drawing order for blended primitives. The drawing order is either global or @@ -765,14 +786,19 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { /** * Associates a MorphTargetBuffer to the given primitive. */ + void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, + size_t offset, size_t count); + + /** @deprecated */ void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, size_t offset, size_t count); - /** - * Utility method to change a MorphTargetBuffer to the given primitive - */ + /** @deprecated */ inline void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer); + MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) { + setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0, + morphTargetBuffer->getVertexCount()); + } /** * Get a MorphTargetBuffer to the given primitive or null if it doesn't exist. @@ -906,20 +932,6 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { ~RenderableManager() = default; }; -RenderableManager::Builder& RenderableManager::Builder::morphing( - uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept { - return morphing(level, primitiveIndex, morphTargetBuffer, 0, - morphTargetBuffer->getVertexCount()); -} - -void RenderableManager::setMorphTargetBufferAt( - Instance instance, uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) { - setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0, - morphTargetBuffer->getVertexCount()); -} - template Box RenderableManager::computeAABB( VECTOR const* UTILS_NONNULL vertices, diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index 4c949a26bf4..e13b5da3055 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -684,6 +684,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla cmd.info.indexCount = primitive.getIndexCount(); cmd.info.type = primitive.getPrimitiveType(); cmd.info.morphTargetBuffer = morphTargets.buffer->getHwHandle(); + cmd.info.morphingOffset = morphTargets.offset; if constexpr (isColorPass) { RenderPass::setupColorCommand(cmd, renderableVariant, mi, inverseFrontFaces); @@ -1029,6 +1030,9 @@ void RenderPass::Executor::execute(FEngine& engine, rebindPipeline = false; currentPipeline = pipeline; driver.bindPipeline(pipeline); + + driver.setPushConstant(ShaderStage::VERTEX, + +PushConstantIds::MORPHING_BUFFER_OFFSET, int32_t(info.morphingOffset)); } if (info.rph != currentPrimitiveHandle) { diff --git a/filament/src/RenderPass.h b/filament/src/RenderPass.h index b8810de33b7..e1c7b3c1813 100644 --- a/filament/src/RenderPass.h +++ b/filament/src/RenderPass.h @@ -251,6 +251,7 @@ class RenderPass { uint32_t indexCount; // 4 bytes uint32_t index = 0; // 4 bytes backend::SamplerGroupHandle morphTargetBuffer; // 4 bytes + uint32_t morphingOffset = 0; // 4 bytes backend::RasterState rasterState; // 4 bytes @@ -261,7 +262,7 @@ class RenderPass { bool hasMorphing : 1; // 1 bit bool hasHybridInstancing : 1; // 1 bit - uint32_t rfu[3]; // 16 bytes + uint32_t rfu[2]; // 16 bytes }; static_assert(sizeof(PrimitiveInfo) == 56); diff --git a/filament/src/RenderableManager.cpp b/filament/src/RenderableManager.cpp index 133dd817c2c..9550197acf0 100644 --- a/filament/src/RenderableManager.cpp +++ b/filament/src/RenderableManager.cpp @@ -164,6 +164,11 @@ void RenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level, downcast(morphTargetBuffer), offset, count); } +void RenderableManager::setMorphTargetBufferAt( + Instance instance, uint8_t level, size_t primitiveIndex, size_t offset, size_t count) { + downcast(this)->setMorphTargetBufferAt(instance, level, primitiveIndex, offset, count); +} + MorphTargetBuffer* RenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex) const noexcept { return downcast(this)->getMorphTargetBufferAt(instance, level, primitiveIndex); diff --git a/filament/src/components/RenderableManager.cpp b/filament/src/components/RenderableManager.cpp index 2cd031059b3..c0907ee880f 100644 --- a/filament/src/components/RenderableManager.cpp +++ b/filament/src/components/RenderableManager.cpp @@ -86,6 +86,7 @@ struct RenderableManager::BuilderDetails { RenderableManager::Builder::GeometryType mGeometryType : 2; size_t mSkinningBoneCount = 0; size_t mMorphTargetCount = 0; + FMorphTargetBuffer* mMorphTargetBuffer = nullptr; Bone const* mUserBones = nullptr; mat4f const* mUserBoneMatrices = nullptr; FSkinningBuffer* mSkinningBuffer = nullptr; @@ -273,6 +274,18 @@ RenderableManager::Builder& RenderableManager::Builder::morphing(size_t targetCo return *this; } +RenderableManager::Builder& RenderableManager::Builder::morphing( + MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept { + mImpl->mMorphTargetBuffer = downcast(morphTargetBuffer); + mImpl->mMorphTargetCount = morphTargetBuffer->getCount(); + return *this; +} + +RenderableManager::Builder& RenderableManager::Builder::morphing( + uint8_t level, size_t primitiveIndex, size_t offset, size_t count) noexcept { + return morphing(level, primitiveIndex, mImpl->mMorphTargetBuffer, offset, count); +} + RenderableManager::Builder& RenderableManager::Builder::morphing(uint8_t, size_t primitiveIndex, MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) noexcept { std::vector& entries = mImpl->mEntries; @@ -646,10 +659,14 @@ void FRenderableManager::create( // Create and initialize all needed MorphTargets. // It's required to avoid branches in hot loops. + FMorphTargetBuffer* morphTargetBuffer = builder->mMorphTargetBuffer; + if (morphTargetBuffer == nullptr) { + morphTargetBuffer = mEngine.getDummyMorphTargetBuffer(); + } MorphTargets* const morphTargets = new MorphTargets[entryCount]; std::generate_n(morphTargets, entryCount, - [dummy = mEngine.getDummyMorphTargetBuffer()]() -> MorphTargets { - return { dummy, 0, 0 }; + [morphTargetBuffer]() -> MorphTargets { + return { morphTargetBuffer, 0, 0 }; }); mManager[ci].morphTargets = { morphTargets, size_type(entryCount) }; @@ -967,6 +984,17 @@ void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level } } +void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level, + size_t primitiveIndex, size_t offset, size_t count) { + if (instance) { + Slice& morphTargets = getMorphTargets(instance, level); + if (primitiveIndex < morphTargets.size()) { + setMorphTargetBufferAt(instance, level, + primitiveIndex, morphTargets[primitiveIndex].buffer, offset, count); + } + } +} + MorphTargetBuffer* FRenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex) const noexcept { if (instance) { diff --git a/filament/src/components/RenderableManager.h b/filament/src/components/RenderableManager.h index 70332c63bbf..6135ba2d733 100644 --- a/filament/src/components/RenderableManager.h +++ b/filament/src/components/RenderableManager.h @@ -157,6 +157,8 @@ class FRenderableManager : public RenderableManager { void setMorphWeights(Instance instance, float const* weights, size_t count, size_t offset); void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, FMorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count); + void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, + size_t offset, size_t count); MorphTargetBuffer* getMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex) const noexcept; size_t getMorphTargetCount(Instance instance) const noexcept; diff --git a/filament/src/details/MorphTargetBuffer.cpp b/filament/src/details/MorphTargetBuffer.cpp index b615e6e7054..c2f3d9a1384 100644 --- a/filament/src/details/MorphTargetBuffer.cpp +++ b/filament/src/details/MorphTargetBuffer.cpp @@ -153,7 +153,7 @@ void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t targetIndex, "MorphTargetBuffer (size=%lu) overflow (count=%u, offset=%u)", (unsigned)mVertexCount, (unsigned)count, (unsigned)offset); - auto size = getSize(mVertexCount); + auto size = getSize(count); ASSERT_PRECONDITION(targetIndex < mCount, "%d target index must be < %d", targetIndex, mCount); @@ -177,7 +177,7 @@ void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t targetIndex, "MorphTargetBuffer (size=%lu) overflow (count=%u, offset=%u)", (unsigned)mVertexCount, (unsigned)count, (unsigned)offset); - auto size = getSize(mVertexCount); + auto size = getSize(count); ASSERT_PRECONDITION(targetIndex < mCount, "%d target index must be < %d", targetIndex, mCount); @@ -200,7 +200,7 @@ void FMorphTargetBuffer::setTangentsAt(FEngine& engine, size_t targetIndex, "MorphTargetBuffer (size=%lu) overflow (count=%u, offset=%u)", (unsigned)mVertexCount, (unsigned)count, (unsigned)offset); - const auto size = getSize(mVertexCount); + const auto size = getSize(count); ASSERT_PRECONDITION(targetIndex < mCount, "%d target index must be < %d", targetIndex, mCount); @@ -226,8 +226,8 @@ void FMorphTargetBuffer::updateDataAt(backend::DriverApi& driver, size_t const xoffset = offset % MAX_MORPH_TARGET_BUFFER_WIDTH; size_t const textureWidth = getWidth(mVertexCount); size_t const alignment = ((textureWidth - xoffset) % textureWidth); - size_t const lineCount = (count - alignment) / textureWidth; - size_t const lastLineCount = (count - alignment) % textureWidth; + size_t const lineCount = (count > alignment) ? (count - alignment) / textureWidth : 0; + size_t const lastLineCount = (count > alignment) ? (count - alignment) % textureWidth : 0; // 'out' buffer is going to be used up to 3 times, so for simplicity we use a shared_buffer // to manage its lifetime. One side effect of this is that the callbacks below will allocate diff --git a/libs/filabridge/include/private/filament/EngineEnums.h b/libs/filabridge/include/private/filament/EngineEnums.h index a35a68c7932..4a8c042e756 100644 --- a/libs/filabridge/include/private/filament/EngineEnums.h +++ b/libs/filabridge/include/private/filament/EngineEnums.h @@ -72,7 +72,7 @@ enum class ReservedSpecializationConstants : uint8_t { CONFIG_STEREO_EYE_COUNT = 8, // don't change (hardcoded in ShaderCompilerService.cpp) }; -enum class PushConstantIds { +enum class PushConstantIds : uint8_t { MORPHING_BUFFER_OFFSET = 0, }; @@ -143,6 +143,8 @@ struct utils::EnableIntegerOperators : public st template<> struct utils::EnableIntegerOperators : public std::true_type {}; template<> +struct utils::EnableIntegerOperators : public std::true_type {}; +template<> struct utils::EnableIntegerOperators : public std::true_type {}; template<> diff --git a/samples/hellomorphing.cpp b/samples/hellomorphing.cpp index af5124bf4b8..6bd418cf43c 100644 --- a/samples/hellomorphing.cpp +++ b/samples/hellomorphing.cpp @@ -54,7 +54,6 @@ struct App { Skybox* skybox; Entity renderable; MorphTargetBuffer *mt1; - MorphTargetBuffer *mt2; }; struct Vertex { @@ -174,12 +173,7 @@ int main(int argc, char** argv) { .build(*engine); app.mt1 = MorphTargetBuffer::Builder() - .vertexCount(9) - .count(3) - .build(*engine); - - app.mt2 = MorphTargetBuffer::Builder() - .vertexCount(9) + .vertexCount(9 * 2) .count(3) .build(*engine); @@ -190,12 +184,12 @@ int main(int argc, char** argv) { app.mt1->setTangentsAt(*engine,1, targets_tan+3, 3, 0); app.mt1->setTangentsAt(*engine,2, targets_tan+6, 3, 0); - app.mt2->setPositionsAt(*engine,0, targets_pos2, 3, 0); - app.mt2->setPositionsAt(*engine,1, targets_pos2+3, 3, 0); - app.mt2->setPositionsAt(*engine,2, targets_pos2+6, 3, 0); - app.mt2->setTangentsAt(*engine,0, targets_tan, 3, 0); - app.mt2->setTangentsAt(*engine,1, targets_tan+3, 3, 0); - app.mt2->setTangentsAt(*engine,2, targets_tan+6, 3, 0); + app.mt1->setPositionsAt(*engine,0, targets_pos2, 3, 9); + app.mt1->setPositionsAt(*engine,1, targets_pos2+3, 3, 9); + app.mt1->setPositionsAt(*engine,2, targets_pos2+6, 3, 9); + app.mt1->setTangentsAt(*engine,0, targets_tan, 3, 9); + app.mt1->setTangentsAt(*engine,1, targets_tan+3, 3, 9); + app.mt1->setTangentsAt(*engine,2, targets_tan+6, 3, 9); app.renderable = EntityManager::get().create(); @@ -209,8 +203,8 @@ int main(int argc, char** argv) { .receiveShadows(false) .castShadows(false) .morphing(3) - .morphing(0,0,app.mt1) - .morphing(0,1,app.mt2) + .morphing(0,0,app.mt1, 0, app.mt1->getCount()) + .morphing(0,1,app.mt1, 9, app.mt1->getCount()) .build(*engine, app.renderable); scene->addEntity(app.renderable); @@ -226,7 +220,6 @@ int main(int argc, char** argv) { engine->destroy(app.vb); engine->destroy(app.ib); engine->destroy(app.mt1); - engine->destroy(app.mt2); engine->destroyCameraComponent(app.camera); utils::EntityManager::get().destroy(app.camera); }; diff --git a/shaders/src/getters.vs b/shaders/src/getters.vs index 4b5efda574f..cccc08319c7 100644 --- a/shaders/src/getters.vs +++ b/shaders/src/getters.vs @@ -146,7 +146,8 @@ void skinNormalTangent(inout vec3 n, inout vec3 t, const uvec4 ids, const vec4 w #define MAX_MORPH_TARGET_BUFFER_WIDTH 2048 void morphPosition(inout vec4 p) { - ivec3 texcoord = ivec3(getVertexIndex() % MAX_MORPH_TARGET_BUFFER_WIDTH, getVertexIndex() / MAX_MORPH_TARGET_BUFFER_WIDTH, 0); + int index = getVertexIndex() + pushConstants.morphingBufferOffset; + ivec3 texcoord = ivec3(index % MAX_MORPH_TARGET_BUFFER_WIDTH, index / MAX_MORPH_TARGET_BUFFER_WIDTH, 0); int c = object_uniforms_morphTargetCount; for (int i = 0; i < c; ++i) { float w = morphingUniforms.weights[i][0]; @@ -159,7 +160,8 @@ void morphPosition(inout vec4 p) { void morphNormal(inout vec3 n) { vec3 baseNormal = n; - ivec3 texcoord = ivec3(getVertexIndex() % MAX_MORPH_TARGET_BUFFER_WIDTH, getVertexIndex() / MAX_MORPH_TARGET_BUFFER_WIDTH, 0); + int index = getVertexIndex() + pushConstants.morphingBufferOffset; + ivec3 texcoord = ivec3(index % MAX_MORPH_TARGET_BUFFER_WIDTH, index / MAX_MORPH_TARGET_BUFFER_WIDTH, 0); int c = object_uniforms_morphTargetCount; for (int i = 0; i < c; ++i) { float w = morphingUniforms.weights[i][0]; From 979421c019fb78a739a28660ff6b2cee82530514 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 16 May 2024 16:39:47 -0700 Subject: [PATCH 13/23] fix/remove wrong asserts --- filament/src/components/RenderableManager.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/filament/src/components/RenderableManager.cpp b/filament/src/components/RenderableManager.cpp index c0907ee880f..8a09e0b34f0 100644 --- a/filament/src/components/RenderableManager.cpp +++ b/filament/src/components/RenderableManager.cpp @@ -966,15 +966,13 @@ void FRenderableManager::setMorphWeights(Instance instance, float const* weights void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, FMorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) { - assert_invariant(offset == 0 && "Offset not yet supported."); - assert_invariant(count == morphTargetBuffer->getVertexCount() && "Count not yet supported."); if (instance) { assert_invariant(morphTargetBuffer); MorphWeights const& morphWeights = mManager[instance].morphWeights; - ASSERT_PRECONDITION(morphWeights.count == morphTargetBuffer->getCount(), + ASSERT_PRECONDITION(morphWeights.count == count, "Only %d morph targets can be set (count=%d)", - morphWeights.count, morphTargetBuffer->getCount()); + morphWeights.count, count); Slice& morphTargets = getMorphTargets(instance, level); if (primitiveIndex < morphTargets.size()) { From 450644ccd557bae704fb99e4ae31bb919b65c701 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Fri, 17 May 2024 10:39:32 -0700 Subject: [PATCH 14/23] Add vk/gl conditions for enabling clip distance (#7861) --- filament/backend/src/opengl/OpenGLContext.cpp | 6 ++++-- filament/backend/src/opengl/OpenGLContext.h | 2 ++ filament/backend/src/vulkan/VulkanContext.h | 7 ++++++- filament/backend/src/vulkan/VulkanDriver.cpp | 2 +- filament/backend/src/vulkan/platform/VulkanPlatform.cpp | 9 +++++++++ 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/filament/backend/src/opengl/OpenGLContext.cpp b/filament/backend/src/opengl/OpenGLContext.cpp index 5021362a1ec..66327633f17 100644 --- a/filament/backend/src/opengl/OpenGLContext.cpp +++ b/filament/backend/src/opengl/OpenGLContext.cpp @@ -66,7 +66,8 @@ bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept { OpenGLContext::OpenGLContext(OpenGLPlatform& platform, Platform::DriverConfig const& driverConfig) noexcept : mPlatform(platform), - mSamplerMap(32) { + mSamplerMap(32), + mDriverConfig(driverConfig) { state.vao.p = &mDefaultVAO; @@ -366,7 +367,8 @@ void OpenGLContext::setDefaultState() noexcept { } #endif - if (ext.EXT_clip_cull_distance) { + if (ext.EXT_clip_cull_distance + && mDriverConfig.stereoscopicType == StereoscopicType::INSTANCED) { glEnable(GL_CLIP_DISTANCE0); glEnable(GL_CLIP_DISTANCE1); } diff --git a/filament/backend/src/opengl/OpenGLContext.h b/filament/backend/src/opengl/OpenGLContext.h index 2465c9f82d4..902fcc9f094 100644 --- a/filament/backend/src/opengl/OpenGLContext.h +++ b/filament/backend/src/opengl/OpenGLContext.h @@ -511,6 +511,8 @@ class OpenGLContext final : public TimerQueryFactoryInterface { mutable tsl::robin_map mSamplerMap; + Platform::DriverConfig const mDriverConfig; + void bindFramebufferResolved(GLenum target, GLuint buffer) noexcept; const std::array, sizeof(bugs)> mBugDatabase{{ diff --git a/filament/backend/src/vulkan/VulkanContext.h b/filament/backend/src/vulkan/VulkanContext.h index 995175ce943..f3ea6614c16 100644 --- a/filament/backend/src/vulkan/VulkanContext.h +++ b/filament/backend/src/vulkan/VulkanContext.h @@ -120,16 +120,21 @@ struct VulkanContext { } inline bool isImageCubeArraySupported() const noexcept { - return mPhysicalDeviceFeatures.imageCubeArray; + return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE; } inline bool isDebugMarkersSupported() const noexcept { return mDebugMarkersSupported; } + inline bool isDebugUtilsSupported() const noexcept { return mDebugUtilsSupported; } + inline bool isClipDistanceSupported() const noexcept { + return mPhysicalDeviceFeatures.shaderClipDistance == VK_TRUE; + } + private: VkPhysicalDeviceMemoryProperties mMemoryProperties = {}; VkPhysicalDeviceProperties mPhysicalDeviceProperties = {}; diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index e9c4f499ce9..0e62a75e1da 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -903,7 +903,7 @@ bool VulkanDriver::isProtectedContentSupported() { bool VulkanDriver::isStereoSupported() { switch (mStereoscopicType) { case backend::StereoscopicType::INSTANCED: - return true; + return mContext.isClipDistanceSupported(); case backend::StereoscopicType::MULTIVIEW: // TODO: implement multiview feature in Vulkan. case backend::StereoscopicType::NONE: diff --git a/filament/backend/src/vulkan/platform/VulkanPlatform.cpp b/filament/backend/src/vulkan/platform/VulkanPlatform.cpp index 7eb5cfe3850..c8403572161 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatform.cpp +++ b/filament/backend/src/vulkan/platform/VulkanPlatform.cpp @@ -16,6 +16,8 @@ #include "backend/platforms/VulkanPlatform.h" +#include + #include "vulkan/platform/VulkanPlatformSwapChainImpl.h" #include "vulkan/VulkanConstants.h" #include "vulkan/VulkanDriver.h" @@ -313,6 +315,7 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, .samplerAnisotropy = features.samplerAnisotropy, .textureCompressionETC2 = features.textureCompressionETC2, .textureCompressionBC = features.textureCompressionBC, + .shaderClipDistance = features.shaderClipDistance, }; deviceCreateInfo.pEnabledFeatures = &enabledFeatures; @@ -665,6 +668,12 @@ Driver* VulkanPlatform::createDriver(void* sharedContext, : mImpl->mGraphicsQueueFamilyIndex; assert_invariant(mImpl->mGraphicsQueueFamilyIndex != INVALID_VK_INDEX); + // Only enable shaderClipDistance if we are doing instanced stereoscopic rendering. + if (context.mPhysicalDeviceFeatures.shaderClipDistance == VK_TRUE + && driverConfig.stereoscopicType != StereoscopicType::INSTANCED) { + context.mPhysicalDeviceFeatures.shaderClipDistance = VK_FALSE; + } + // At this point, we should have a family index that points to a family that has > 0 queues for // graphics. In which case, we will allocate one queue for all of Filament (and assumes at least // one has been allocated by the client if context was shared). If the index of the target queue From 813e6f805b6195221c68fcf8582e674505242fcf Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Fri, 17 May 2024 19:42:23 +0000 Subject: [PATCH 15/23] Update combine_multiview_images flag (#7867) Set combine_multiview_images to false by default as it's the desirable setting for most Android devices. Set the flag to true for GUI by default. Put the `Combine Multiview Images` checkbox under the `Stereo mode` box for an easier access. --- filament/src/details/Engine.h | 2 +- libs/viewer/src/ViewerGui.cpp | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index a0de5720b20..85f065bf83e 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -600,7 +600,7 @@ class FEngine : public Engine { bool debug_froxel_visualization = false; } lighting; struct { - bool combine_multiview_images = true; + bool combine_multiview_images = false; } stereo; matdbg::DebugServer* server = nullptr; } debug; diff --git a/libs/viewer/src/ViewerGui.cpp b/libs/viewer/src/ViewerGui.cpp index d29868c1850..826d18c9446 100644 --- a/libs/viewer/src/ViewerGui.cpp +++ b/libs/viewer/src/ViewerGui.cpp @@ -387,6 +387,9 @@ ViewerGui::ViewerGui(filament::Engine* engine, filament::Scene* scene, filament: mSettings.view.ssao.enabled = true; mSettings.view.bloom.enabled = true; + DebugRegistry& debug = mEngine->getDebugRegistry(); + *debug.getPropertyAddress("d.stereo.combine_multiview_images") = true; + using namespace filament; LightManager::Builder(LightManager::Type::SUN) .color(mSettings.lighting.sunlightColor) @@ -1097,11 +1100,12 @@ void ViewerGui::updateUserInterface() { #if defined(FILAMENT_SAMPLES_STEREO_TYPE_INSTANCED) \ || defined(FILAMENT_SAMPLES_STEREO_TYPE_MULTIVIEW) ImGui::Checkbox("Stereo mode", &mSettings.view.stereoscopicOptions.enabled); - - #if defined(FILAMENT_SAMPLES_STEREO_TYPE_MULTIVIEW) - *debug.getPropertyAddress("d.stereo.combine_multiview_images") - = mSettings.view.stereoscopicOptions.enabled; - #endif +#if defined(FILAMENT_SAMPLES_STEREO_TYPE_MULTIVIEW) + ImGui::Indent(); + ImGui::Checkbox("Combine Multiview Images", + debug.getPropertyAddress("d.stereo.combine_multiview_images")); + ImGui::Unindent(); +#endif #endif ImGui::SliderFloat("Ocular distance", &mSettings.viewer.cameraEyeOcularDistance, 0.0f, 1.0f); From 7d80975c3cec617c274640fe2813f726770696bb Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 16 May 2024 16:05:53 -0700 Subject: [PATCH 16/23] add getReasonLiteral() on TPanic currently it only returns the format string. --- libs/utils/include/utils/Panic.h | 15 +++++++++------ libs/utils/src/Panic.cpp | 25 ++++++++++++------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/libs/utils/include/utils/Panic.h b/libs/utils/include/utils/Panic.h index c658da4b14f..5d560477e30 100644 --- a/libs/utils/include/utils/Panic.h +++ b/libs/utils/include/utils/Panic.h @@ -286,6 +286,11 @@ class UTILS_PUBLIC Panic { */ virtual const char* getReason() const noexcept = 0; + /** + * You know who you are. + */ + virtual const char* getReasonLiteral() const noexcept = 0; + /** * Get the function name where the panic was detected. On debug build the fully qualified * function name is returned; on release builds only the function name is. @@ -335,6 +340,7 @@ class UTILS_PUBLIC TPanic : public Panic { // Panic interface const char* getReason() const noexcept override; + const char* getReasonLiteral() const noexcept override; const char* getFunction() const noexcept override; const char* getFile() const noexcept override; int getLine() const noexcept override; @@ -374,11 +380,6 @@ class UTILS_PUBLIC TPanic : public Panic { } protected: - /** - * Creates a Panic. - * @param reason a description of the cause of the error - */ - explicit TPanic(std::string reason); /** * Creates a Panic with extra information about the error-site. @@ -387,7 +388,8 @@ class UTILS_PUBLIC TPanic : public Panic { * @param line the line in the above file where the error was detected * @param reason a description of the cause of the error */ - TPanic(char const* function, char const* file, int line, std::string reason); + TPanic(char const* function, char const* file, int line, + std::string reason, std::string reasonLiteral); ~TPanic() override; @@ -396,6 +398,7 @@ class UTILS_PUBLIC TPanic : public Panic { CallStack m_callstack; std::string m_reason; + std::string m_reason_literal; char const* const m_function = nullptr; char const* const m_file = nullptr; const int m_line = -1; diff --git a/libs/utils/src/Panic.cpp b/libs/utils/src/Panic.cpp index addec296c10..0f47addec86 100644 --- a/libs/utils/src/Panic.cpp +++ b/libs/utils/src/Panic.cpp @@ -123,22 +123,16 @@ void Panic::setPanicHandler(PanicHandlerCallback handler, void* user) noexcept { // ------------------------------------------------------------------------------------------------ template -TPanic::TPanic(std::string reason) : - m_reason(std::move(reason)) { +TPanic::TPanic(const char* function, const char* file, int line, + std::string reason, std::string reasonLiteral) + : m_reason(std::move(reason)), m_reason_literal(std::move(reasonLiteral)), + m_function(function), m_file(file), m_line(line) { m_callstack.update(1); buildMessage(); } template -TPanic::TPanic(const char* function, const char* file, int line, std::string reason) - : m_reason(std::move(reason)), m_function(function), m_file(file), m_line(line) { - m_callstack.update(1); - buildMessage(); -} - -template -TPanic::~TPanic() { -} +TPanic::~TPanic() = default; template const char* TPanic::what() const noexcept { @@ -150,6 +144,11 @@ const char* TPanic::getReason() const noexcept { return m_reason.c_str(); } +template +const char* TPanic::getReasonLiteral() const noexcept { + return m_reason_literal.c_str(); +} + template const char* TPanic::getFunction() const noexcept { return m_function; @@ -197,9 +196,9 @@ template void TPanic::panic(char const* function, char const* file, int line, const char* format, ...) { va_list args; va_start(args, format); - std::string const reason(formatString(format, args)); + std::string reason(formatString(format, args)); va_end(args); - T e(function, formatFile(file), line, reason); + T e(function, formatFile(file), line, std::move(reason), format); // always log the Panic at the point it is detected e.log(); From 180c326bb713397b307a4e36bee2ad80306c1902 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Mon, 20 May 2024 11:39:00 -0700 Subject: [PATCH 17/23] Include sstream.h in distribution headers --- libs/utils/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index 928e2f37755..72558167b32 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -39,6 +39,7 @@ set(DIST_HDRS ${PUBLIC_HDR_DIR}/${TARGET}/Slice.h ${PUBLIC_HDR_DIR}/${TARGET}/StructureOfArrays.h ${PUBLIC_HDR_DIR}/${TARGET}/Systrace.h + ${PUBLIC_HDR_DIR}/${TARGET}/sstream.h ${PUBLIC_HDR_DIR}/${TARGET}/unwindows.h ) From 8cfdab0c28c73d54ca4b32da64717c608b1e4596 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 21 May 2024 10:34:05 -0700 Subject: [PATCH 18/23] Fix stereo variant defines in common_getters (#7879) This caused a breakage in shader validation at runtime. Repro: - Remove ./out - ./build.sh release gltf_viewer - run gltf_viewer --- shaders/src/common_getters.glsl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/shaders/src/common_getters.glsl b/shaders/src/common_getters.glsl index a2bbebe4f8b..23a365549af 100644 --- a/shaders/src/common_getters.glsl +++ b/shaders/src/common_getters.glsl @@ -24,13 +24,11 @@ highp mat4 getViewFromClipMatrix() { /** @public-api */ highp mat4 getClipFromWorldMatrix() { -#if defined(VARIANT_HAS_STEREO) -#if defined(FILAMENT_STEREO_INSTANCED) +#if defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_INSTANCED) int eye = instance_index % CONFIG_STEREO_EYE_COUNT; return frameUniforms.clipFromWorldMatrix[eye]; -#elif defined(FILAMENT_STEREO_MULTIVIEW) +#elif defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_MULTIVIEW) return frameUniforms.clipFromWorldMatrix[gl_ViewID_OVR]; -#endif #else return frameUniforms.clipFromWorldMatrix[0]; #endif From c7202c575a4f93439ee30d4a199a4ce0b8dcf126 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Tue, 21 May 2024 12:47:57 -0700 Subject: [PATCH 19/23] Release Filament 1.52.0 --- NEW_RELEASE_NOTES.md | 2 -- README.md | 4 ++-- RELEASE_NOTES.md | 4 ++++ android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 2554073b9e5..4a1a9c7fa7e 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,5 +7,3 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut - -- Add instructions for using Mesa for software rasterization diff --git a/README.md b/README.md index d554705c90c..85e13450282 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.51.8' + implementation 'com.google.android.filament:filament-android:1.52.0' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.51.8' +pod 'Filament', '~> 1.52.0' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e07e626dad4..cb2fcd7c62f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,10 @@ A new header is inserted each time a *tag* is created. Instead, if you are authoring a PR for the main branch, add your release note to [NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md). +## v1.52.1 + +- Add instructions for using Mesa for software rasterization + ## v1.51.9 diff --git a/android/gradle.properties b/android/gradle.properties index dec17d0ce52..5ec300f0168 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.51.8 +VERSION_NAME=1.52.0 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 0b049607cba..98307d9121a 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.51.8" + spec.version = "1.52.0" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.8/filament-v1.51.8-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.52.0/filament-v1.52.0-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/web/filament-js/package.json b/web/filament-js/package.json index bed6c9029f6..57d43e329e0 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.51.8", + "version": "1.52.0", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From d56f769d4dbe6b4452c702c0b408840b76cb8222 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Tue, 21 May 2024 12:48:07 -0700 Subject: [PATCH 20/23] Bump version to 1.52.1 --- README.md | 4 ++-- android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 85e13450282..53655554e71 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.52.0' + implementation 'com.google.android.filament:filament-android:1.52.1' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.52.0' +pod 'Filament', '~> 1.52.1' ``` ### Snapshots diff --git a/android/gradle.properties b/android/gradle.properties index 5ec300f0168..94b176e0bfc 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.52.0 +VERSION_NAME=1.52.1 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 98307d9121a..d9dfd91dfdf 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.52.0" + spec.version = "1.52.1" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.52.0/filament-v1.52.0-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.52.1/filament-v1.52.1-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/web/filament-js/package.json b/web/filament-js/package.json index 57d43e329e0..2839181b214 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.52.0", + "version": "1.52.1", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From 11ecaa2fbf4af31738cb3cf5ea5ad835b48ff1fb Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Fri, 24 May 2024 13:11:10 -0700 Subject: [PATCH 21/23] Revert "Metal: implement more accurate buffer tracking (#7839)" This reverts commit 54a800a25dac26021cb001aa3740f4a0d8a0ffaa. --- filament/backend/src/metal/MetalBuffer.h | 106 +++++++++--------- filament/backend/src/metal/MetalBuffer.mm | 20 ++-- filament/backend/src/metal/MetalBufferPool.h | 2 +- filament/backend/src/metal/MetalBufferPool.mm | 3 +- filament/backend/src/metal/MetalDriver.mm | 22 ++-- filament/backend/src/metal/MetalHandles.mm | 4 +- 6 files changed, 76 insertions(+), 81 deletions(-) diff --git a/filament/backend/src/metal/MetalBuffer.h b/filament/backend/src/metal/MetalBuffer.h index c86e08a8c7a..081ca6c3fd0 100644 --- a/filament/backend/src/metal/MetalBuffer.h +++ b/filament/backend/src/metal/MetalBuffer.h @@ -65,12 +65,9 @@ class ScopedAllocationTimer { const char* mName; }; -#ifndef FILAMENT_METAL_BUFFER_TRACKING -#define FILAMENT_METAL_BUFFER_TRACKING 0 -#endif - -class MetalBufferTracking { +class TrackedMetalBuffer { public: + static constexpr size_t EXCESS_BUFFER_COUNT = 30000; enum class Type { @@ -94,57 +91,66 @@ class MetalBufferTracking { } } -#if FILAMENT_METAL_BUFFER_TRACKING - static void initialize() { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - for (size_t i = 0; i < TypeCount; i++) { - aliveBuffers[i] = [NSHashTable weakObjectsHashTable]; + TrackedMetalBuffer() noexcept : mBuffer(nil) {} + TrackedMetalBuffer(nullptr_t) noexcept : mBuffer(nil) {} + TrackedMetalBuffer(id buffer, Type type) : mBuffer(buffer), mType(type) { + assert_invariant(type != Type::NONE); + if (buffer) { + aliveBuffers[toIndex(type)]++; + mType = type; + if (getAliveBuffers() >= EXCESS_BUFFER_COUNT) { + if (platform && platform->hasDebugUpdateStatFunc()) { + platform->debugUpdateStat("filament.metal.excess_buffers_allocated", + TrackedMetalBuffer::getAliveBuffers()); + } } - }); + } } - static void setPlatform(MetalPlatform* p) { platform = p; } - - static void track(id buffer, Type type) { - assert_invariant(type != Type::NONE); - if (UTILS_UNLIKELY(getAliveBuffers() >= EXCESS_BUFFER_COUNT)) { - if (platform && platform->hasDebugUpdateStatFunc()) { - platform->debugUpdateStat("filament.metal.excess_buffers_allocated", - MetalBufferTracking::getAliveBuffers()); - } + ~TrackedMetalBuffer() { + if (mBuffer) { + assert_invariant(mType != Type::NONE); + aliveBuffers[toIndex(mType)]--; } - [aliveBuffers[toIndex(type)] addObject:buffer]; } + TrackedMetalBuffer(TrackedMetalBuffer&&) = delete; + TrackedMetalBuffer(TrackedMetalBuffer const&) = delete; + TrackedMetalBuffer& operator=(TrackedMetalBuffer const&) = delete; + + TrackedMetalBuffer& operator=(TrackedMetalBuffer&& rhs) noexcept { + swap(rhs); + return *this; + } + + id get() const noexcept { return mBuffer; } + operator bool() const noexcept { return bool(mBuffer); } + static uint64_t getAliveBuffers() { uint64_t sum = 0; - for (size_t i = 1; i < TypeCount; i++) { - sum += getAliveBuffers(static_cast(i)); + for (const auto& v : aliveBuffers) { + sum += v; } return sum; } static uint64_t getAliveBuffers(Type type) { assert_invariant(type != Type::NONE); - NSHashTable* hashTable = aliveBuffers[toIndex(type)]; - // Caution! We can't simply use hashTable.count here, which is inaccurate. - // See http://cocoamine.net/blog/2013/12/13/nsmaptable-and-zeroing-weak-references/ - return hashTable.objectEnumerator.allObjects.count; + return aliveBuffers[toIndex(type)]; } -#else - static void initialize() {} - static void setPlatform(MetalPlatform* p) {} - static id track(id buffer, Type type) { return buffer; } - static uint64_t getAliveBuffers() { return 0; } - static uint64_t getAliveBuffers(Type type) { return 0; } -#endif + static void setPlatform(MetalPlatform* p) { platform = p; } private: -#if FILAMENT_METAL_BUFFER_TRACKING - static std::array>*, TypeCount> aliveBuffers; + void swap(TrackedMetalBuffer& other) noexcept { + std::swap(mBuffer, other.mBuffer); + std::swap(mType, other.mType); + } + + id mBuffer; + Type mType = Type::NONE; + static MetalPlatform* platform; -#endif + static std::array aliveBuffers; }; class MetalBuffer { @@ -198,7 +204,7 @@ class MetalBuffer { private: - id mBuffer; + TrackedMetalBuffer mBuffer; size_t mBufferSize = 0; void* mCpuBuffer = nullptr; MetalContext& mContext; @@ -247,11 +253,9 @@ class MetalRingBuffer { mBufferOptions(options), mSlotSizeBytes(computeSlotSize(layout)), mSlotCount(slotCount) { - { - ScopedAllocationTimer timer("ring"); - mBuffer = [device newBufferWithLength:mSlotSizeBytes * mSlotCount options:mBufferOptions]; - } - MetalBufferTracking::track(mBuffer, MetalBufferTracking::Type::RING); + ScopedAllocationTimer timer("ring"); + mBuffer = { [device newBufferWithLength:mSlotSizeBytes * mSlotCount options:mBufferOptions], + TrackedMetalBuffer::Type::RING }; assert_invariant(mBuffer); } @@ -271,11 +275,11 @@ class MetalRingBuffer { // finishes executing. { ScopedAllocationTimer timer("ring"); - mAuxBuffer = [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions]; + mAuxBuffer = { [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions], + TrackedMetalBuffer::Type::RING }; } - MetalBufferTracking::track(mAuxBuffer, MetalBufferTracking::Type::RING); assert_invariant(mAuxBuffer); - return { mAuxBuffer, 0 }; + return { mAuxBuffer.get(), 0 }; } mCurrentSlot = (mCurrentSlot + 1) % mSlotCount; mOccupiedSlots->fetch_add(1, std::memory_order_relaxed); @@ -304,9 +308,9 @@ class MetalRingBuffer { */ std::pair, NSUInteger> getCurrentAllocation() const { if (UTILS_UNLIKELY(mAuxBuffer)) { - return { mAuxBuffer, 0 }; + return { mAuxBuffer.get(), 0 }; } - return { mBuffer, mCurrentSlot * mSlotSizeBytes }; + return { mBuffer.get(), mCurrentSlot * mSlotSizeBytes }; } bool canAccomodateLayout(MTLSizeAndAlign layout) const { @@ -315,8 +319,8 @@ class MetalRingBuffer { private: id mDevice; - id mBuffer; - id mAuxBuffer; + TrackedMetalBuffer mBuffer; + TrackedMetalBuffer mAuxBuffer; MTLResourceOptions mBufferOptions; diff --git a/filament/backend/src/metal/MetalBuffer.mm b/filament/backend/src/metal/MetalBuffer.mm index 32804f43f3c..5f09a290781 100644 --- a/filament/backend/src/metal/MetalBuffer.mm +++ b/filament/backend/src/metal/MetalBuffer.mm @@ -22,14 +22,10 @@ namespace filament { namespace backend { +std::array TrackedMetalBuffer::aliveBuffers = { 0 }; +MetalPlatform* TrackedMetalBuffer::platform = nullptr; MetalPlatform* ScopedAllocationTimer::platform = nullptr; -#if FILAMENT_METAL_BUFFER_TRACKING -std::array>*, MetalBufferTracking::TypeCount> - MetalBufferTracking::aliveBuffers; -MetalPlatform* MetalBufferTracking::platform = nullptr; -#endif - MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage, size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) { // If the buffer is less than 4K in size and is updated frequently, we don't use an explicit @@ -45,9 +41,9 @@ // Otherwise, we allocate a private GPU buffer. { ScopedAllocationTimer timer("generic"); - mBuffer = [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate]; + mBuffer = { [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate], + TrackedMetalBuffer::Type::GENERIC }; } - MetalBufferTracking::track(mBuffer, MetalBufferTracking::Type::GENERIC); ASSERT_POSTCONDITION(mBuffer, "Could not allocate Metal buffer of size %zu.", size); } @@ -74,7 +70,7 @@ // Acquire a staging buffer to hold the contents of this update. MetalBufferPool* bufferPool = mContext.bufferPool; const MetalBufferPoolEntry* const staging = bufferPool->acquireBuffer(size); - memcpy(staging->buffer.contents, src, size); + memcpy(staging->buffer.get().contents, src, size); // The blit below requires that byteOffset be a multiple of 4. ASSERT_PRECONDITION(!(byteOffset & 0x3u), "byteOffset must be a multiple of 4"); @@ -83,9 +79,9 @@ id cmdBuffer = getPendingCommandBuffer(&mContext); id blitEncoder = [cmdBuffer blitCommandEncoder]; blitEncoder.label = @"Buffer upload blit"; - [blitEncoder copyFromBuffer:staging->buffer + [blitEncoder copyFromBuffer:staging->buffer.get() sourceOffset:0 - toBuffer:mBuffer + toBuffer:mBuffer.get() destinationOffset:byteOffset size:size]; [blitEncoder endEncoding]; @@ -106,7 +102,7 @@ return nil; } assert_invariant(mBuffer); - return mBuffer; + return mBuffer.get(); } void MetalBuffer::bindBuffers(id cmdBuffer, id encoder, diff --git a/filament/backend/src/metal/MetalBufferPool.h b/filament/backend/src/metal/MetalBufferPool.h index 2aa7e805a0d..03688ab3c43 100644 --- a/filament/backend/src/metal/MetalBufferPool.h +++ b/filament/backend/src/metal/MetalBufferPool.h @@ -32,7 +32,7 @@ struct MetalContext; // Immutable POD representing a shared CPU-GPU buffer. struct MetalBufferPoolEntry { - id buffer; + TrackedMetalBuffer buffer; size_t capacity; mutable uint64_t lastAccessed; mutable uint32_t referenceCount; diff --git a/filament/backend/src/metal/MetalBufferPool.mm b/filament/backend/src/metal/MetalBufferPool.mm index 8c640f7d0a9..a1e54a46239 100644 --- a/filament/backend/src/metal/MetalBufferPool.mm +++ b/filament/backend/src/metal/MetalBufferPool.mm @@ -48,10 +48,9 @@ buffer = [mContext.device newBufferWithLength:numBytes options:MTLResourceStorageModeShared]; } - MetalBufferTracking::track(buffer, MetalBufferTracking::Type::STAGING); ASSERT_POSTCONDITION(buffer, "Could not allocate Metal staging buffer of size %zu.", numBytes); MetalBufferPoolEntry* stage = new MetalBufferPoolEntry { - .buffer = buffer, + .buffer = { buffer, TrackedMetalBuffer::Type::STAGING }, .capacity = numBytes, .lastAccessed = mCurrentFrame, .referenceCount = 1 diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 4662b8b953a..a9d963e784f 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -106,9 +106,8 @@ mStereoscopicType(driverConfig.stereoscopicType) { mContext->driver = this; + TrackedMetalBuffer::setPlatform(platform); ScopedAllocationTimer::setPlatform(platform); - MetalBufferTracking::initialize(); - MetalBufferTracking::setPlatform(platform); mContext->device = mPlatform.createDevice(); assert_invariant(mContext->device); @@ -203,7 +202,7 @@ } MetalDriver::~MetalDriver() noexcept { - MetalBufferTracking::setPlatform(nullptr); + TrackedMetalBuffer::setPlatform(nullptr); ScopedAllocationTimer::setPlatform(nullptr); mContext->device = nil; mContext->emptyTexture = nil; @@ -225,16 +224,13 @@ os_signpost_interval_begin(mContext->log, mContext->signpostId, "Frame encoding", "%{public}d", frameId); #endif if (mPlatform.hasDebugUpdateStatFunc()) { -#if FILAMENT_METAL_BUFFER_TRACKING - const uint64_t generic = MetalBufferTracking::getAliveBuffers(MetalBufferTracking::Type::GENERIC); - const uint64_t ring = MetalBufferTracking::getAliveBuffers(MetalBufferTracking::Type::RING); - const uint64_t staging = MetalBufferTracking::getAliveBuffers(MetalBufferTracking::Type::STAGING); - const uint64_t total = generic + ring + staging; - mPlatform.debugUpdateStat("filament.metal.alive_buffers", total); - mPlatform.debugUpdateStat("filament.metal.alive_buffers.generic", generic); - mPlatform.debugUpdateStat("filament.metal.alive_buffers.ring", ring); - mPlatform.debugUpdateStat("filament.metal.alive_buffers.staging", staging); -#endif + mPlatform.debugUpdateStat("filament.metal.alive_buffers", TrackedMetalBuffer::getAliveBuffers()); + mPlatform.debugUpdateStat("filament.metal.alive_buffers.generic", + TrackedMetalBuffer::getAliveBuffers(TrackedMetalBuffer::Type::GENERIC)); + mPlatform.debugUpdateStat("filament.metal.alive_buffers.ring", + TrackedMetalBuffer::getAliveBuffers(TrackedMetalBuffer::Type::RING)); + mPlatform.debugUpdateStat("filament.metal.alive_buffers.staging", + TrackedMetalBuffer::getAliveBuffers(TrackedMetalBuffer::Type::STAGING)); } } diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 2b770241ab0..dcf22137fb0 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -800,13 +800,13 @@ static void func(void* user) { PixelBufferDescriptor const& data, const PixelBufferShape& shape) { const size_t stagingBufferSize = shape.totalBytes; auto entry = context.bufferPool->acquireBuffer(stagingBufferSize); - memcpy(entry->buffer.contents, + memcpy(entry->buffer.get().contents, static_cast(data.buffer) + shape.sourceOffset, stagingBufferSize); id blitCommandBuffer = getPendingCommandBuffer(&context); id blitCommandEncoder = [blitCommandBuffer blitCommandEncoder]; blitCommandEncoder.label = @"Texture upload buffer blit"; - [blitCommandEncoder copyFromBuffer:entry->buffer + [blitCommandEncoder copyFromBuffer:entry->buffer.get() sourceOffset:0 sourceBytesPerRow:shape.bytesPerRow sourceBytesPerImage:shape.bytesPerSlice From 17f32d198adcc71c4af311406e1a7f20de951877 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 23 May 2024 11:14:29 -0700 Subject: [PATCH 22/23] Throw an exception when failing to build a Metal render pipeline state. (#7878) Currently, if this fails we log the error message to stderr (which doesn't get captured by most crash reporting systems) and then crash in a postcondition assert. By including the error message in an exception reason and throwing an ObjC exception, we get better discoverability of error causes. (Building a render pipeline state from shaders is usually when a shader actually gets JITted from LLVM IR to GPU-specific code, so if we accidentally used a feature that's not available on the local GPU, we'll find out about it here.) --- filament/backend/src/metal/MetalState.mm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/filament/backend/src/metal/MetalState.mm b/filament/backend/src/metal/MetalState.mm index 2b2e5bd65b2..bf288c9751f 100644 --- a/filament/backend/src/metal/MetalState.mm +++ b/filament/backend/src/metal/MetalState.mm @@ -90,9 +90,15 @@ NSError* error = nullptr; id pipeline = [device newRenderPipelineStateWithDescriptor:descriptor error:&error]; - if (error) { - auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding]; + if (UTILS_UNLIKELY(pipeline == nil)) { + NSString *errorMessage = + [NSString stringWithFormat:@"Could not create Metal pipeline state: %@", + error ? error.localizedDescription : @"unknown error"]; + auto description = [errorMessage cStringUsingEncoding:NSUTF8StringEncoding]; utils::slog.e << description << utils::io::endl; + [[NSException exceptionWithName:@"MetalRenderPipelineFailure" + reason:errorMessage + userInfo:nil] raise]; } ASSERT_POSTCONDITION(error == nil, "Could not create Metal pipeline state."); From 3fb9521c104e6f6bde88415164d2317892bd69e9 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Tue, 21 May 2024 12:50:35 -0700 Subject: [PATCH 23/23] Bump MATERIAL_VERSION to 52 --- libs/filabridge/include/filament/MaterialEnums.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index 9f348481895..25eb74d63bf 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -28,7 +28,7 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 51; +static constexpr size_t MATERIAL_VERSION = 52; /** * Supported shading models