diff --git a/BUILDING.md b/BUILDING.md index 28e6861caa4..de1549f230f 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,62 @@ 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 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] + +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`. +and then set `VK_ICD_FILENAMES` to the ICD json produced in the build. For example, +```shell +export VK_ICD_FILENAMES=/Users/user/swiftshader/build/Darwin/vk_swiftshader_icd.json +``` + +Build and run Filament as usual and specify the Vulkan backend when creating the Engine. -## SwiftShader for CI +### Mesa's LLVMPipe (GL) and Lavapipe (Vulkan) [tested on Linux] -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: +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 -docker pull ghcr.io/filament-assets/swiftshader -docker run -it ghcr.io/filament-assets/swiftshader +git clone https://gitlab.freedesktop.org/mesa/mesa.git +sudo apt-get build-dep mesa ``` -To do more with the container, see the helper script at `build/swiftshader/test.sh`. +To build both the GL and Vulkan rasterizers, -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`. +```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/CMakeLists.txt b/CMakeLists.txt index 533abf6a49c..e17aba1a7f4 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() @@ -451,8 +440,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 +460,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") @@ -530,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. @@ -678,21 +674,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/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/RELEASE_NOTES.md b/RELEASE_NOTES.md index ed35954e2f5..cb2fcd7c62f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,7 +7,11 @@ 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.0 +## v1.52.1 + +- Add instructions for using Mesa for software rasterization + +## v1.51.9 ## v1.51.8 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-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/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 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/build.sh b/build.sh index 434a0045fa3..ab6440f45de 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" @@ -66,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" @@ -165,8 +166,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" @@ -179,6 +178,8 @@ ASAN_UBSAN_OPTION="" BACKEND_DEBUG_FLAG_OPTION="" +STEREOSCOPIC_OPTION="" + IOS_BUILD_SIMULATOR=false BUILD_UNIVERSAL_LIBRARIES=false @@ -233,12 +234,12 @@ 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} \ ${ASAN_UBSAN_OPTION} \ ${BACKEND_DEBUG_FLAG_OPTION} \ + ${STEREOSCOPIC_OPTION} \ ${architectures} \ ../.. ln -sf "out/cmake-${lc_target}/compile_commands.json" \ @@ -373,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 @@ -607,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 @@ -794,7 +796,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:S:" opt; do case ${opt} in h) print_help @@ -913,10 +915,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." @@ -938,6 +936,20 @@ while getopts ":hacCfgijmp:q:uvslwtedk: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/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 075815f4ac0..6a7f66cc2fd 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/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index aeae5e2e9b9..d69a9991d28 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/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/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index 712386b93c6..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) @@ -501,7 +501,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/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/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.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 f438da44280..a9d963e784f 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -102,12 +102,12 @@ mContext(new MetalContext(driverConfig.textureUseAfterFreePoolSize)), mHandleAllocator("Handles", driverConfig.handleArenaSize, - driverConfig.disableHandleUseAfterFreeCheck) { + driverConfig.disableHandleUseAfterFreeCheck), + 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); @@ -202,7 +202,7 @@ } MetalDriver::~MetalDriver() noexcept { - MetalBufferTracking::setPlatform(nullptr); + TrackedMetalBuffer::setPlatform(nullptr); ScopedAllocationTimer::setPlatform(nullptr); mContext->device = nil; mContext->emptyTexture = nil; @@ -224,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)); } } @@ -803,13 +800,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; } } @@ -1648,7 +1647,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/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 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."); diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index b21a7796ae8..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; } @@ -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/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/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index d22f4b23f5e..884db62459e 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; } // ------------------------------------------------------------------------------------------------ @@ -924,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)) { @@ -949,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]); @@ -2040,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; } } @@ -3837,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); @@ -3875,20 +3885,35 @@ 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; } -#ifdef FILAMENT_ENABLE_MATDBG + 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 CHECK_GL_ERROR(utils::slog.e) @@ -3907,7 +3932,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) { @@ -3934,7 +3963,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) 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; 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 4bedc8566f4..0e62a75e1da 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 mContext.isClipDistanceSupported(); + case backend::StereoscopicType::MULTIVIEW: + // TODO: implement multiview feature in Vulkan. + case backend::StereoscopicType::NONE: + return false; } } @@ -1763,7 +1765,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"); @@ -1873,6 +1875,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 +1971,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/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/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 diff --git a/filament/backend/src/vulkan/platform/VulkanPlatform.cpp b/filament/backend/src/vulkan/platform/VulkanPlatform.cpp index 687887a6988..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" @@ -44,7 +46,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 +86,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 +94,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 +154,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 +199,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 +249,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 +264,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 +287,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 +296,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; @@ -311,6 +315,7 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, .samplerAnisotropy = features.samplerAnisotropy, .textureCompressionETC2 = features.textureCompressionETC2, .textureCompressionBC = features.textureCompressionBC, + .shaderClipDistance = features.shaderClipDistance, }; deviceCreateInfo.pEnabledFeatures = &enabledFeatures; @@ -323,12 +328,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 +347,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 +562,7 @@ struct VulkanPlatformPrivate { std::unordered_set mHeadlessSwapChains; bool mSharedContext = false; + bool mForceXCBSwapchain = false; }; void VulkanPlatform::terminate() { @@ -610,7 +616,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()); } @@ -644,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 @@ -672,10 +702,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 +776,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; } 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..85f065bf83e 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 { @@ -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/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/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/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index ea636a57367..3a4a4824e0e 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -550,15 +550,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/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 ) 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/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/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 -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(); 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..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) @@ -1094,9 +1097,18 @@ 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) + 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); 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)) { 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 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",