Compare commits

..
Author SHA1 Message Date
Powei Feng bd697f667b test load image 2024-05-15 23:37:49 -07:00
86 changed files with 856 additions and 1169 deletions
+16 -45
View File
@@ -73,6 +73,7 @@ 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:
@@ -425,7 +426,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
@@ -435,62 +436,32 @@ doxygen docs/doxygen/filament.doxygen
Finally simply open `docs/html/index.html` in your web browser.
## Software Rasterization
## SwiftShader
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
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`:
```shell
git clone https://github.com/google/swiftshader.git
cd swiftshader/build
cmake .. && make -j
export SWIFTSHADER_LD_LIBRARY_PATH=`pwd`
```
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
```
Next, go to your Filament repo and use the [easy build](#easy-build) script with `-t`.
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]
We will only cover steps that build Mesa from source. The official documentation of Mesa mentioned
that in general precompiled libraries [are **not** made available](https://docs.mesa3d.org/precompiled.html).
Download the repo and make sure you have the build depedencies. For example (assuming an Ubuntu/Debian distro),
```shell
git clone https://gitlab.freedesktop.org/mesa/mesa.git
sudo apt-get build-dep mesa
```
To build both the GL and Vulkan rasterizers,
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:
```shell
cd mesa
mkdir -p out
meson setup builddir/ -Dprefix=$(pwd)/out -Dglx=xlib -Dgallium-drivers=swrast -Dvulkan-drivers=swrast
meson install -C builddir/
docker pull ghcr.io/filament-assets/swiftshader
docker run -it ghcr.io/filament-assets/swiftshader
```
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
```
To do more with the container, see the helper script at `build/swiftshader/test.sh`.
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
```
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`.
+31 -12
View File
@@ -21,6 +21,8 @@ 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)
@@ -143,6 +145,11 @@ 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()
@@ -320,6 +327,10 @@ 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()
@@ -440,13 +451,8 @@ 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 "")
@@ -460,7 +466,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} ${BINARY_ALIGNMENT}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GC_SECTIONS} ${B_SYMBOLIC_FUNCTIONS}")
if (WEBGL_PTHREADS)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pthread")
@@ -524,15 +530,13 @@ else()
endif()
# This only affects the prebuilt shader files in gltfio and samples, not filament library.
# The value can be either "instanced", "multiview", or "none"
set(FILAMENT_SAMPLES_STEREO_TYPE "none" CACHE STRING
# The value can be either "instanced" or "multiview".
set(FILAMENT_SAMPLES_STEREO_TYPE "instanced" 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"
AND NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "none")
message(FATAL_ERROR "Invalid stereo type: \"${FILAMENT_SAMPLES_STEREO_TYPE}\" choose either \"instanced\", \"multiview\", or \"none\" ")
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\" ")
endif ()
# Compiling samples for multiview implies enabling multiview feature as well.
@@ -674,6 +678,21 @@ 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
# ==================================================================================================
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.52.1'
implementation 'com.google.android.filament:filament-android:1.51.8'
}
```
@@ -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.1'
pod 'Filament', '~> 1.51.8'
```
### Snapshots
-4
View File
@@ -7,10 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.52.1
- Add instructions for using Mesa for software rasterization
## v1.51.9
-1
View File
@@ -38,7 +38,6 @@ 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})
-1
View File
@@ -59,7 +59,6 @@ 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
@@ -244,25 +244,13 @@ Java_com_google_android_filament_RenderableManager_nBuilderMorphing(JNIEnv*, jcl
builder->morphing(targetCount);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nBuilderMorphingStandard(JNIEnv*, jclass,
jlong nativeBuilder, jlong nativeMorphTargetBuffer) {
RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder;
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
builder->morphing(morphTargetBuffer);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nBuilderSetMorphTargetBufferAt(JNIEnv*, jclass,
jlong nativeBuilder, int level, int primitiveIndex, jlong nativeMorphTargetBuffer,
int offset, int count) {
RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder;
if (nativeMorphTargetBuffer) {
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
builder->morphing(level, primitiveIndex, morphTargetBuffer, offset, count);
} else {
builder->morphing(level, primitiveIndex, offset, count);
}
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
builder->morphing(level, primitiveIndex, morphTargetBuffer, offset, count);
}
extern "C" JNIEXPORT void JNICALL
@@ -338,14 +326,9 @@ Java_com_google_android_filament_RenderableManager_nSetMorphTargetBufferAt(JNIEn
jclass, jlong nativeRenderableManager, jint i, int level, jint primitiveIndex,
jlong nativeMorphTargetBuffer, jint offset, jint count) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
if (nativeMorphTargetBuffer) {
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level,
(size_t) primitiveIndex, morphTargetBuffer, (size_t) offset, (size_t) count);
} else {
rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level,
(size_t) primitiveIndex, (size_t) offset, (size_t) count);
}
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level,
(size_t) primitiveIndex, morphTargetBuffer, (size_t) offset, (size_t) count);
}
extern "C" JNIEXPORT jint JNICALL
@@ -159,12 +159,9 @@ public class Engine {
};
/**
* The type of technique for stereoscopic rendering. (Note that the materials used will need to be
* compatible with the chosen technique.)
* The type of technique for stereoscopic rendering
*/
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. */
@@ -408,7 +405,7 @@ public class Engine {
*
* @see View#setStereoscopicOptions
*/
public StereoscopicType stereoscopicType = StereoscopicType.NONE;
public StereoscopicType stereoscopicType = StereoscopicType.INSTANCED;
/**
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
@@ -524,7 +524,14 @@ public class RenderableManager {
}
/**
* Controls if the renderable has legacy vertex morphing targets, zero by default.
* Controls if the renderable has vertex morphing targets, zero by default. This is
* required to enable GPU morphing.
*
* <p>Filament supports two morphing modes: standard (default) and legacy.</p>
*
* <p>For standard morphing, A {@link MorphTargetBuffer} must be created and provided via
* {@link RenderableManager#setMorphTargetBufferAt}. Standard morphing supports up to
* <code>CONFIG_MAX_MORPH_TARGET_COUNT</code> morph targets.</p>
*
* For legacy morphing, the attached {@link VertexBuffer} must provide data in the
* appropriate {@link VertexBuffer.VertexAttribute} slots (<code>MORPH_POSITION_0</code> etc).
@@ -542,22 +549,6 @@ public class RenderableManager {
return this;
}
/**
* Controls if the renderable has vertex morphing targets, zero by default.
*
* <p>For standard morphing, A {@link MorphTargetBuffer} must be provided.
* Standard morphing supports up to
* <code>CONFIG_MAX_MORPH_TARGET_COUNT</code> morph targets.</p>
*
* <p>See also {@link RenderableManager#setMorphWeights}, which can be called on a per-frame basis
* to advance the animation.</p>
*/
@NonNull
public Builder morphing(@NonNull MorphTargetBuffer morphTargetBuffer) {
nBuilderMorphingStandard(mNativeBuilder, morphTargetBuffer.getNativeObject());
return this;
}
/**
* Specifies the morph target buffer for a primitive.
*
@@ -574,17 +565,6 @@ public class RenderableManager {
* @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3)
*/
@NonNull
public Builder morphing(@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
@IntRange(from = 0) int offset,
@IntRange(from = 0) int count) {
nBuilderSetMorphTargetBufferAt(mNativeBuilder, level, primitiveIndex, 0, offset, count);
return this;
}
/** @deprecated */
@Deprecated
@NonNull
public Builder morphing(@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
@NonNull MorphTargetBuffer morphTargetBuffer,
@@ -595,8 +575,10 @@ public class RenderableManager {
return this;
}
/** @deprecated */
@Deprecated
/**
* Utility method to specify morph target buffer for a primitive.
* For details, see the {@link RenderableManager.Builder#morphing}.
*/
@NonNull
public Builder morphing(@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
@@ -705,16 +687,6 @@ public class RenderableManager {
*
* @see Builder#morphing
*/
public void setMorphTargetBufferAt(@EntityInstance int i,
@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
@IntRange(from = 0) int offset,
@IntRange(from = 0) int count) {
nSetMorphTargetBufferAt(mNativeObject, i, level, primitiveIndex, 0, offset, count);
}
/** @deprecated */
@Deprecated
public void setMorphTargetBufferAt(@EntityInstance int i,
@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
@@ -725,8 +697,10 @@ public class RenderableManager {
morphTargetBuffer.getNativeObject(), offset, count);
}
/** @deprecated */
@Deprecated
/**
* Utility method to change morph target buffer for the given primitive.
* For details, see the {@link RenderableManager#setMorphTargetBufferAt}.
*/
public void setMorphTargetBufferAt(@EntityInstance int i,
@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
@@ -1032,7 +1006,6 @@ public class RenderableManager {
private static native int nBuilderSkinningBones(long nativeBuilder, int boneCount, Buffer bones, int remaining);
private static native void nBuilderSkinningBuffer(long nativeBuilder, long nativeSkinningBuffer, int boneCount, int offset);
private static native void nBuilderMorphing(long nativeBuilder, int targetCount);
private static native void nBuilderMorphingStandard(long nativeBuilder, long nativeMorphTargetBuffer);
private static native void nBuilderSetMorphTargetBufferAt(long nativeBuilder, int level, int primitiveIndex, long nativeMorphTargetBuffer, int offset, int count);
private static native void nBuilderEnableSkinningBuffers(long nativeBuilder, boolean enabled);
private static native void nBuilderFog(long nativeBuilder, boolean enabled);
@@ -31,7 +31,6 @@ 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
-1
View File
@@ -44,7 +44,6 @@ 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
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.52.1
VERSION_NAME=1.51.8
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+11 -23
View File
@@ -44,6 +44,8 @@ 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"
@@ -64,9 +66,6 @@ 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"
@@ -166,6 +165,8 @@ 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"
@@ -178,8 +179,6 @@ ASAN_UBSAN_OPTION=""
BACKEND_DEBUG_FLAG_OPTION=""
STEREOSCOPIC_OPTION=""
IOS_BUILD_SIMULATOR=false
BUILD_UNIVERSAL_LIBRARIES=false
@@ -234,12 +233,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" \
@@ -374,7 +373,6 @@ 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
@@ -609,7 +607,7 @@ function build_ios_target {
-DCMAKE_TOOLCHAIN_FILE=../../third_party/clang/iOS.cmake \
${MATDBG_OPTION} \
${MATOPT_OPTION} \
${STEREOSCOPIC_OPTION} \
${BACKEND_DEBUG_FLAG_OPTION} \
../..
ln -sf "out/cmake-ios-${lc_target}-${arch}/compile_commands.json" \
../../compile_commands.json
@@ -796,7 +794,7 @@ function check_debug_release_build {
pushd "$(dirname "$0")" > /dev/null
while getopts ":hacCfgijmp:q:uvslwedk:bx:S:" opt; do
while getopts ":hacCfgijmp:q:uvslwtedk:bx:" opt; do
case ${opt} in
h)
print_help
@@ -915,6 +913,10 @@ while getopts ":hacCfgijmp:q:uvslwedk:bx:S:" 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."
@@ -936,20 +938,6 @@ while getopts ":hacCfgijmp:q:uvslwedk:bx:S:" 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 ""
+53
View File
@@ -0,0 +1,53 @@
# 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 <user> --password <token>
# 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
+56
View File
@@ -0,0 +1,56 @@
#!/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("""<html>
<head>
<script type="module" src="https://unpkg.com/img-comparison-slider@latest/dist/component/component.esm.js"></script>
<script nomodule="" src="https://unpkg.com/img-comparison-slider@latest/dist/component/component.js"></script>
<link rel="stylesheet" href="https://unpkg.com/img-comparison-slider@latest/dist/collection/styles/initial.css"/>
<style>
h2 {
font-weight: normal;
margin-top: 150px;
margin-bottom: 20px;
}
a {
text-decoration: none;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: blue;
}
a:hover {
font-weight: bold;
}
</style>
</head>
<body>
""")
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'<h2><a href="{image.stem}.json">{image.stem}.json</a></h2>\n')
gallery.write('<img-comparison-slider>\n')
gallery.write(f'<img slot="before" src="{before}" /> <img slot="after" src="{after}" />\n')
gallery.write('</img-comparison-slider>\n')
gallery.write("""</body>
</html>
""")
+62
View File
@@ -0,0 +1,62 @@
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<uint32_t> 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<uint32_t> optimized;
+127
View File
@@ -0,0 +1,127 @@
#!/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
+1 -3
View File
@@ -66,7 +66,7 @@ set(PRIVATE_HDRS
# OpenGL / OpenGL ES Sources
# ==================================================================================================
if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3)
if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3 AND NOT FILAMENT_USE_SWIFTSHADER)
list(APPEND SRCS
include/backend/platforms/OpenGLPlatform.h
src/opengl/gl_headers.cpp
@@ -417,9 +417,7 @@ if (APPLE OR LINUX)
test/test_MissingRequiredAttributes.cpp
test/test_ReadPixels.cpp
test/test_BufferUpdates.cpp
test/test_Callbacks.cpp
test/test_MRT.cpp
test/test_PushConstants.cpp
test/test_LoadImage.cpp
test/test_StencilBuffer.cpp
test/test_Scissor.cpp
@@ -22,7 +22,6 @@
#include <utils/BitmaskEnum.h>
#include <utils/unwindows.h> // Because we define ERROR in the FenceStatus enum.
#include <backend/Platform.h>
#include <backend/PresentCallable.h>
#include <utils/Invocable.h>
@@ -118,7 +117,7 @@ static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT,
"The number of buffer objects that can be attached to a VertexBuffer must be "
"less than or equal to the maximum number of vertex attributes.");
static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 9; // This is guaranteed by OpenGL ES.
static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 10; // This is guaranteed by OpenGL ES.
static constexpr size_t CONFIG_SAMPLER_BINDING_COUNT = 4; // This is guaranteed by OpenGL ES.
/**
@@ -1251,7 +1250,13 @@ enum class Workaround : uint16_t {
POWER_VR_SHADER_WORKAROUNDS,
};
using StereoscopicType = backend::Platform::StereoscopicType;
//! 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,
};
} // namespace filament::backend
@@ -41,26 +41,6 @@ public:
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.
@@ -91,11 +71,6 @@ public:
* GLES 3.x backends.
*/
bool forceGLES2Context = false;
/**
* Sets the technique for stereoscopic rendering.
*/
StereoscopicType stereoscopicType = StereoscopicType::NONE;
};
Platform() noexcept;
@@ -23,9 +23,9 @@
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Hash.h>
#include <utils/PrivateImplementation.h>
#include <string_view>
#include <tuple>
#include <unordered_set>
@@ -47,14 +47,6 @@ struct VulkanPlatformPrivate;
class VulkanPlatform : public Platform, utils::PrivateImplementation<VulkanPlatformPrivate> {
public:
struct ExtensionHashFn {
std::size_t operator()(utils::CString const& s) const noexcept {
return std::hash<std::string>{}(s.data());
}
};
// Utility for managing device or instance extensions during initialization.
using ExtensionSet = std::unordered_set<utils::CString, ExtensionHashFn>;
/**
* 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
@@ -200,13 +192,6 @@ public:
virtual SwapChainPtr createSwapChain(void* nativeWindow, uint64_t flags = 0,
VkExtent2D extent = {0, 0});
/**
* Allows implementers to provide instance extensions that they'd like to include in the
* instance creation.
* @return A set of extensions to enable for the instance.
*/
virtual ExtensionSet getRequiredInstanceExtensions() { return {}; }
/**
* Destroy the swapchain.
* @param handle The handle returned by createSwapChain()
@@ -251,9 +236,10 @@ public:
VkQueue getGraphicsQueue() const noexcept;
private:
static ExtensionSet getSwapchainInstanceExtensions();
// Platform dependent helper methods
using ExtensionSet = std::unordered_set<std::string_view>;
static ExtensionSet getRequiredInstanceExtensions();
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
static SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) noexcept;
@@ -144,7 +144,8 @@ DECL_DRIVER_API_N(setFrameScheduledCallback,
DECL_DRIVER_API_N(setFrameCompletedCallback,
backend::SwapChainHandle, sch,
backend::CallbackHandler*, handler,
utils::Invocable<void(void)>&&, callback)
backend::CallbackHandler::Callback, callback,
void*, user)
DECL_DRIVER_API_N(setPresentationTime,
int64_t, monotonic_clock_ns)
@@ -300,7 +301,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_0(bool, isStereoSupported)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isStereoSupported, backend::StereoscopicType, stereoscopicType)
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 +502,7 @@ DECL_DRIVER_API_N(blit,
math::uint2, size)
DECL_DRIVER_API_N(bindPipeline,
backend::PipelineState const&, state)
backend::PipelineState, state)
DECL_DRIVER_API_N(bindRenderPrimitive,
backend::RenderPrimitiveHandle, rph)
+6 -5
View File
@@ -29,21 +29,21 @@
#include "backend/platforms/PlatformCocoaTouchGL.h"
#endif
#elif defined(__APPLE__)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) && !defined(FILAMENT_USE_SWIFTSHADER)
#include <backend/platforms/PlatformCocoaGL.h>
#endif
#elif defined(__linux__)
#if defined(FILAMENT_SUPPORTS_X11)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) && !defined(FILAMENT_USE_SWIFTSHADER)
#include "backend/platforms/PlatformGLX.h"
#endif
#elif defined(FILAMENT_SUPPORTS_EGL_ON_LINUX)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) && !defined(FILAMENT_USE_SWIFTSHADER)
#include "backend/platforms/PlatformEGLHeadless.h"
#endif
#endif
#elif defined(WIN32)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) && !defined(FILAMENT_USE_SWIFTSHADER)
#include "backend/platforms/PlatformWGL.h"
#endif
#elif defined(__EMSCRIPTEN__)
@@ -111,7 +111,8 @@ Platform* PlatformFactory::create(Backend* backend) noexcept {
}
assert_invariant(*backend == Backend::OPENGL);
#if defined(FILAMENT_SUPPORTS_OPENGL)
#if defined(FILAMENT_USE_EXTERNAL_GLES3)
#if defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(FILAMENT_USE_SWIFTSHADER)
// Swiftshader OpenGLES support is deprecated and incomplete
return nullptr;
#elif defined(__ANDROID__)
return new PlatformEGLAndroid();
+52 -56
View File
@@ -65,9 +65,12 @@ private:
const char* mName;
};
class TrackedMetalBuffer {
public:
#ifndef FILAMENT_METAL_BUFFER_TRACKING
#define FILAMENT_METAL_BUFFER_TRACKING 0
#endif
class MetalBufferTracking {
public:
static constexpr size_t EXCESS_BUFFER_COUNT = 30000;
enum class Type {
@@ -91,66 +94,57 @@ public:
}
}
TrackedMetalBuffer() noexcept : mBuffer(nil) {}
TrackedMetalBuffer(nullptr_t) noexcept : mBuffer(nil) {}
TrackedMetalBuffer(id<MTLBuffer> buffer, Type type) : mBuffer(buffer), mType(type) {
#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];
}
});
}
static void setPlatform(MetalPlatform* p) { platform = p; }
static void track(id<MTLBuffer> buffer, Type 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());
}
if (UTILS_UNLIKELY(getAliveBuffers() >= EXCESS_BUFFER_COUNT)) {
if (platform && platform->hasDebugUpdateStatFunc()) {
platform->debugUpdateStat("filament.metal.excess_buffers_allocated",
MetalBufferTracking::getAliveBuffers());
}
}
[aliveBuffers[toIndex(type)] addObject:buffer];
}
~TrackedMetalBuffer() {
if (mBuffer) {
assert_invariant(mType != Type::NONE);
aliveBuffers[toIndex(mType)]--;
}
}
TrackedMetalBuffer(TrackedMetalBuffer&&) = delete;
TrackedMetalBuffer(TrackedMetalBuffer const&) = delete;
TrackedMetalBuffer& operator=(TrackedMetalBuffer const&) = delete;
TrackedMetalBuffer& operator=(TrackedMetalBuffer&& rhs) noexcept {
swap(rhs);
return *this;
}
id<MTLBuffer> get() const noexcept { return mBuffer; }
operator bool() const noexcept { return bool(mBuffer); }
static uint64_t getAliveBuffers() {
uint64_t sum = 0;
for (const auto& v : aliveBuffers) {
sum += v;
for (size_t i = 1; i < TypeCount; i++) {
sum += getAliveBuffers(static_cast<Type>(i));
}
return sum;
}
static uint64_t getAliveBuffers(Type type) {
assert_invariant(type != Type::NONE);
return aliveBuffers[toIndex(type)];
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;
}
static void setPlatform(MetalPlatform* p) { platform = p; }
#else
static void initialize() {}
static void setPlatform(MetalPlatform* p) {}
static id<MTLBuffer> track(id<MTLBuffer> buffer, Type type) { return buffer; }
static uint64_t getAliveBuffers() { return 0; }
static uint64_t getAliveBuffers(Type type) { return 0; }
#endif
private:
void swap(TrackedMetalBuffer& other) noexcept {
std::swap(mBuffer, other.mBuffer);
std::swap(mType, other.mType);
}
id<MTLBuffer> mBuffer;
Type mType = Type::NONE;
#if FILAMENT_METAL_BUFFER_TRACKING
static std::array<NSHashTable<id<MTLBuffer>>*, TypeCount> aliveBuffers;
static MetalPlatform* platform;
static std::array<uint64_t, TypeCount> aliveBuffers;
#endif
};
class MetalBuffer {
@@ -204,7 +198,7 @@ public:
private:
TrackedMetalBuffer mBuffer;
id<MTLBuffer> mBuffer;
size_t mBufferSize = 0;
void* mCpuBuffer = nullptr;
MetalContext& mContext;
@@ -253,9 +247,11 @@ public:
mBufferOptions(options),
mSlotSizeBytes(computeSlotSize(layout)),
mSlotCount(slotCount) {
ScopedAllocationTimer timer("ring");
mBuffer = { [device newBufferWithLength:mSlotSizeBytes * mSlotCount options:mBufferOptions],
TrackedMetalBuffer::Type::RING };
{
ScopedAllocationTimer timer("ring");
mBuffer = [device newBufferWithLength:mSlotSizeBytes * mSlotCount options:mBufferOptions];
}
MetalBufferTracking::track(mBuffer, MetalBufferTracking::Type::RING);
assert_invariant(mBuffer);
}
@@ -275,11 +271,11 @@ public:
// finishes executing.
{
ScopedAllocationTimer timer("ring");
mAuxBuffer = { [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions],
TrackedMetalBuffer::Type::RING };
mAuxBuffer = [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions];
}
MetalBufferTracking::track(mAuxBuffer, MetalBufferTracking::Type::RING);
assert_invariant(mAuxBuffer);
return { mAuxBuffer.get(), 0 };
return { mAuxBuffer, 0 };
}
mCurrentSlot = (mCurrentSlot + 1) % mSlotCount;
mOccupiedSlots->fetch_add(1, std::memory_order_relaxed);
@@ -308,9 +304,9 @@ public:
*/
std::pair<id<MTLBuffer>, NSUInteger> getCurrentAllocation() const {
if (UTILS_UNLIKELY(mAuxBuffer)) {
return { mAuxBuffer.get(), 0 };
return { mAuxBuffer, 0 };
}
return { mBuffer.get(), mCurrentSlot * mSlotSizeBytes };
return { mBuffer, mCurrentSlot * mSlotSizeBytes };
}
bool canAccomodateLayout(MTLSizeAndAlign layout) const {
@@ -319,8 +315,8 @@ public:
private:
id<MTLDevice> mDevice;
TrackedMetalBuffer mBuffer;
TrackedMetalBuffer mAuxBuffer;
id<MTLBuffer> mBuffer;
id<MTLBuffer> mAuxBuffer;
MTLResourceOptions mBufferOptions;
+12 -8
View File
@@ -22,10 +22,14 @@
namespace filament {
namespace backend {
std::array<uint64_t, TrackedMetalBuffer::TypeCount> TrackedMetalBuffer::aliveBuffers = { 0 };
MetalPlatform* TrackedMetalBuffer::platform = nullptr;
MetalPlatform* ScopedAllocationTimer::platform = nullptr;
#if FILAMENT_METAL_BUFFER_TRACKING
std::array<NSHashTable<id<MTLBuffer>>*, 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
@@ -41,9 +45,9 @@ MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType,
// Otherwise, we allocate a private GPU buffer.
{
ScopedAllocationTimer timer("generic");
mBuffer = { [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate],
TrackedMetalBuffer::Type::GENERIC };
mBuffer = [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate];
}
MetalBufferTracking::track(mBuffer, MetalBufferTracking::Type::GENERIC);
ASSERT_POSTCONDITION(mBuffer, "Could not allocate Metal buffer of size %zu.", size);
}
@@ -70,7 +74,7 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
// 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.get().contents, src, size);
memcpy(staging->buffer.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");
@@ -79,9 +83,9 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(&mContext);
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
blitEncoder.label = @"Buffer upload blit";
[blitEncoder copyFromBuffer:staging->buffer.get()
[blitEncoder copyFromBuffer:staging->buffer
sourceOffset:0
toBuffer:mBuffer.get()
toBuffer:mBuffer
destinationOffset:byteOffset
size:size];
[blitEncoder endEncoding];
@@ -102,7 +106,7 @@ id<MTLBuffer> MetalBuffer::getGpuBufferForDraw(id<MTLCommandBuffer> cmdBuffer) n
return nil;
}
assert_invariant(mBuffer);
return mBuffer.get();
return mBuffer;
}
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
+1 -1
View File
@@ -32,7 +32,7 @@ struct MetalContext;
// Immutable POD representing a shared CPU-GPU buffer.
struct MetalBufferPoolEntry {
TrackedMetalBuffer buffer;
id<MTLBuffer> buffer;
size_t capacity;
mutable uint64_t lastAccessed;
mutable uint32_t referenceCount;
@@ -48,9 +48,10 @@ MetalBufferPoolEntry const* MetalBufferPool::acquireBuffer(size_t numBytes) {
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, TrackedMetalBuffer::Type::STAGING },
.buffer = buffer,
.capacity = numBytes,
.lastAccessed = mCurrentFrame,
.referenceCount = 1
-14
View File
@@ -55,18 +55,6 @@ struct MetalVertexBuffer;
constexpr static uint8_t MAX_SAMPLE_COUNT = 8; // Metal devices support at most 8 MSAA samples
class MetalPushConstantBuffer {
public:
void setPushConstant(PushConstantVariant value, uint8_t index);
bool isDirty() const { return mDirty; }
void setBytes(id<MTLCommandEncoder> encoder, ShaderStage stage);
void clear();
private:
std::vector<PushConstantVariant> mPushConstants;
bool mDirty = false;
};
struct MetalContext {
explicit MetalContext(size_t metalFreedTextureListSize)
: texturesToDestroy(metalFreedTextureListSize) {}
@@ -121,8 +109,6 @@ struct MetalContext {
PolygonOffset currentPolygonOffset = {0.0f, 0.0f};
std::array<MetalPushConstantBuffer, Program::SHADER_TYPE_COUNT> currentPushConstants;
MetalSamplerGroup* samplerBindings[Program::SAMPLER_BINDING_COUNT] = {};
// Keeps track of sampler groups we've finalized for the current render pass.
@@ -153,68 +153,5 @@ bool isInRenderPass(MetalContext* context) {
return context->currentRenderPassEncoder != nil;
}
void MetalPushConstantBuffer::setPushConstant(PushConstantVariant value, uint8_t index) {
if (mPushConstants.size() <= index) {
mPushConstants.resize(index + 1);
mDirty = true;
}
if (UTILS_LIKELY(mPushConstants[index] != value)) {
mDirty = true;
mPushConstants[index] = value;
}
}
void MetalPushConstantBuffer::setBytes(id<MTLCommandEncoder> encoder, ShaderStage stage) {
constexpr size_t PUSH_CONSTANT_SIZE_BYTES = 4;
constexpr size_t PUSH_CONSTANT_BUFFER_INDEX = 26;
static char buffer[MAX_PUSH_CONSTANT_COUNT * PUSH_CONSTANT_SIZE_BYTES];
assert_invariant(mPushConstants.size() <= MAX_PUSH_CONSTANT_COUNT);
size_t bufferSize = PUSH_CONSTANT_SIZE_BYTES * mPushConstants.size();
for (size_t i = 0; i < mPushConstants.size(); i++) {
const auto& constant = mPushConstants[i];
std::visit(
[i](auto arg) {
if constexpr (std::is_same_v<decltype(arg), bool>) {
// bool push constants are converted to uints in MSL.
// We must ensure we write all the bytes for boolean values to work
// correctly.
uint32_t boolAsUint = arg ? 0x00000001 : 0x00000000;
*(reinterpret_cast<uint32_t*>(buffer + PUSH_CONSTANT_SIZE_BYTES * i)) =
boolAsUint;
} else {
*(decltype(arg)*)(buffer + PUSH_CONSTANT_SIZE_BYTES * i) = arg;
}
},
constant);
}
switch (stage) {
case ShaderStage::VERTEX:
[(id<MTLRenderCommandEncoder>)encoder setVertexBytes:buffer
length:bufferSize
atIndex:PUSH_CONSTANT_BUFFER_INDEX];
break;
case ShaderStage::FRAGMENT:
[(id<MTLRenderCommandEncoder>)encoder setFragmentBytes:buffer
length:bufferSize
atIndex:PUSH_CONSTANT_BUFFER_INDEX];
break;
case ShaderStage::COMPUTE:
[(id<MTLComputeCommandEncoder>)encoder setBytes:buffer
length:bufferSize
atIndex:PUSH_CONSTANT_BUFFER_INDEX];
break;
}
mDirty = false;
}
void MetalPushConstantBuffer::clear() {
mPushConstants.clear();
mDirty = false;
}
} // namespace backend
} // namespace filament
-2
View File
@@ -17,7 +17,6 @@
#ifndef TNT_FILAMENT_DRIVER_METALDRIVER_H
#define TNT_FILAMENT_DRIVER_METALDRIVER_H
#include <backend/DriverEnums.h>
#include "private/backend/Driver.h"
#include "DriverBase.h"
@@ -141,7 +140,6 @@ private:
void enumerateBoundBuffers(BufferObjectBinding bindingType,
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f);
backend::StereoscopicType const mStereoscopicType;
};
} // namespace backend
+26 -44
View File
@@ -102,12 +102,12 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
mContext(new MetalContext(driverConfig.textureUseAfterFreePoolSize)),
mHandleAllocator("Handles",
driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck),
mStereoscopicType(driverConfig.stereoscopicType) {
driverConfig.disableHandleUseAfterFreeCheck) {
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(MetalPlatform* platform, const Platform::DriverConfig&
}
MetalDriver::~MetalDriver() noexcept {
TrackedMetalBuffer::setPlatform(nullptr);
MetalBufferTracking::setPlatform(nullptr);
ScopedAllocationTimer::setPlatform(nullptr);
mContext->device = nil;
mContext->emptyTexture = nil;
@@ -224,13 +224,16 @@ void MetalDriver::beginFrame(int64_t monotonic_clock_ns,
os_signpost_interval_begin(mContext->log, mContext->signpostId, "Frame encoding", "%{public}d", frameId);
#endif
if (mPlatform.hasDebugUpdateStatFunc()) {
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));
#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
}
}
@@ -240,10 +243,10 @@ void MetalDriver::setFrameScheduledCallback(
swapChain->setFrameScheduledCallback(handler, std::move(callback));
}
void MetalDriver::setFrameCompletedCallback(
Handle<HwSwapChain> sch, CallbackHandler* handler, utils::Invocable<void(void)>&& callback) {
void MetalDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
auto* swapChain = handle_cast<MetalSwapChain>(sch);
swapChain->setFrameCompletedCallback(handler, std::move(callback));
swapChain->setFrameCompletedCallback(handler, callback, user);
}
void MetalDriver::execute(std::function<void(void)> const& fn) noexcept {
@@ -800,15 +803,13 @@ bool MetalDriver::isProtectedContentSupported() {
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;
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;
}
}
@@ -1108,10 +1109,6 @@ void MetalDriver::beginRenderPass(Handle<HwRenderTarget> rth,
mContext->currentPolygonOffset = {0.0f, 0.0f};
mContext->finalizedSamplerGroups.clear();
for (auto& pc : mContext->currentPushConstants) {
pc.clear();
}
}
void MetalDriver::nextSubpass(int dummy) {}
@@ -1260,14 +1257,7 @@ void MetalDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
}
void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
ASSERT_PRECONDITION(
isInRenderPass(mContext), "setPushConstant must be called inside a render pass.");
assert_invariant(static_cast<size_t>(stage) < mContext->currentPushConstants.size());
MetalPushConstantBuffer& pushConstants =
mContext->currentPushConstants[static_cast<size_t>(stage)];
pushConstants.setPushConstant(value, index);
}
backend::PushConstantVariant value) {}
void MetalDriver::insertEventMarker(const char* string, uint32_t len) {
@@ -1647,7 +1637,7 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
}
}
void MetalDriver::bindPipeline(PipelineState const& ps) {
void MetalDriver::bindPipeline(PipelineState ps) {
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
"bindPipeline() without a valid command encoder.");
@@ -1875,14 +1865,6 @@ void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t inst
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
// Update push constants.
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
auto& pushConstants = mContext->currentPushConstants[i];
if (UTILS_UNLIKELY(pushConstants.isDirty())) {
pushConstants.setBytes(mContext->currentRenderPassEncoder, static_cast<ShaderStage>(i));
}
}
auto primitive = handle_cast<MetalRenderPrimitive>(mContext->currentRenderPrimitive);
MetalIndexBuffer* indexBuffer = primitive->indexBuffer;
+4 -3
View File
@@ -75,7 +75,7 @@ public:
void setFrameScheduledCallback(CallbackHandler* handler, FrameScheduledCallback&& callback);
void setFrameCompletedCallback(
CallbackHandler* handler, utils::Invocable<void(void)>&& callback);
CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
// For CAMetalLayer-backed SwapChains, presents the drawable or schedules a
// FrameScheduledCallback.
@@ -119,12 +119,13 @@ private:
// PresentCallable object.
struct {
CallbackHandler* handler = nullptr;
std::shared_ptr<FrameScheduledCallback> callback = nullptr;
FrameScheduledCallback callback = {};
} frameScheduled;
struct {
CallbackHandler* handler = nullptr;
std::shared_ptr<utils::Invocable<void(void)>> callback = nullptr;
CallbackHandler::Callback callback = {};
void* user = nullptr;
} frameCompleted;
};
+16 -27
View File
@@ -224,13 +224,14 @@ void MetalSwapChain::ensureDepthStencilTexture() {
void MetalSwapChain::setFrameScheduledCallback(
CallbackHandler* handler, FrameScheduledCallback&& callback) {
frameScheduled.handler = handler;
frameScheduled.callback = std::make_shared<FrameScheduledCallback>(std::move(callback));
frameScheduled.callback = std::move(callback);
}
void MetalSwapChain::setFrameCompletedCallback(
CallbackHandler* handler, utils::Invocable<void(void)>&& callback) {
void MetalSwapChain::setFrameCompletedCallback(CallbackHandler* handler,
CallbackHandler::Callback callback, void* user) {
frameCompleted.handler = handler;
frameCompleted.callback = std::make_shared<utils::Invocable<void(void)>>(std::move(callback));
frameCompleted.callback = callback;
frameCompleted.user = user;
}
void MetalSwapChain::present() {
@@ -303,17 +304,17 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
assert_invariant(drawable);
struct Callback {
Callback(std::shared_ptr<FrameScheduledCallback> callback, id<CAMetalDrawable> drawable,
Callback(FrameScheduledCallback&& callback, id<CAMetalDrawable> drawable,
MetalDriver* driver)
: f(callback), data(PresentDrawableData::create(drawable, driver)) {}
std::shared_ptr<FrameScheduledCallback> f;
: f(std::move(callback)), data(PresentDrawableData::create(drawable, driver)) {}
FrameScheduledCallback f;
// PresentDrawableData* is destroyed by maybePresentAndDestroyAsync() later.
std::unique_ptr<PresentDrawableData> data;
static void func(void* user) {
auto* const c = reinterpret_cast<Callback*>(user);
PresentDrawableData* presentDrawableData = c->data.release();
PresentCallable presentCallable(presentDrawable, presentDrawableData);
c->f->operator()(presentCallable);
c->f(presentCallable);
delete c;
}
};
@@ -321,7 +322,7 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
// This callback pointer will be captured by the block. Even if the scheduled handler is never
// called, the unique_ptr will still ensure we don't leak memory.
__block auto callback =
std::make_unique<Callback>(frameScheduled.callback, drawable, context.driver);
std::make_unique<Callback>(std::move(frameScheduled.callback), drawable, context.driver);
backend::CallbackHandler* handler = frameScheduled.handler;
MetalDriver* driver = context.driver;
@@ -336,25 +337,13 @@ void MetalSwapChain::scheduleFrameCompletedCallback() {
return;
}
struct Callback {
Callback(std::shared_ptr<utils::Invocable<void(void)>> callback) : f(callback) {}
std::shared_ptr<utils::Invocable<void(void)>> f;
static void func(void* user) {
auto* const c = reinterpret_cast<Callback*>(user);
c->f->operator()();
delete c;
}
};
// This callback pointer will be captured by the block. Even if the completed handler is never
// called, the unique_ptr will still ensure we don't leak memory.
__block auto callback = std::make_unique<Callback>(frameCompleted.callback);
CallbackHandler* handler = frameCompleted.handler;
void* user = frameCompleted.user;
CallbackHandler::Callback callback = frameCompleted.callback;
MetalDriver* driver = context.driver;
[getPendingCommandBuffer(&context) addCompletedHandler:^(id<MTLCommandBuffer> cb) {
Callback* user = callback.release();
driver->scheduleCallback(handler, user, &Callback::func);
driver->scheduleCallback(handler, user, callback);
}];
}
@@ -800,13 +789,13 @@ void MetalTexture::loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion
PixelBufferDescriptor const& data, const PixelBufferShape& shape) {
const size_t stagingBufferSize = shape.totalBytes;
auto entry = context.bufferPool->acquireBuffer(stagingBufferSize);
memcpy(entry->buffer.get().contents,
memcpy(entry->buffer.contents,
static_cast<uint8_t*>(data.buffer) + shape.sourceOffset,
stagingBufferSize);
id<MTLCommandBuffer> blitCommandBuffer = getPendingCommandBuffer(&context);
id<MTLBlitCommandEncoder> blitCommandEncoder = [blitCommandBuffer blitCommandEncoder];
blitCommandEncoder.label = @"Texture upload buffer blit";
[blitCommandEncoder copyFromBuffer:entry->buffer.get()
[blitCommandEncoder copyFromBuffer:entry->buffer
sourceOffset:0
sourceBytesPerRow:shape.bytesPerRow
sourceBytesPerImage:shape.bytesPerSlice
+2 -4
View File
@@ -43,8 +43,7 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) {
// ------------------------------------------------------
// 0 Zero buffer (placeholder vertex buffer) 1
// 1-16 Filament vertex buffers 16 limited by MAX_VERTEX_BUFFER_COUNT
// 17-25 Uniform buffers 9 Program::UNIFORM_BINDING_COUNT
// 26 Push constants 1
// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT
// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT
//
// Total 31
@@ -54,8 +53,7 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) {
// Bindings Buffer name Count
// ------------------------------------------------------
// 0-3 SSBO buffers 4 MAX_SSBO_COUNT
// 17-25 Uniform buffers 9 Program::UNIFORM_BINDING_COUNT
// 26 Push constants 1
// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT
// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT
//
// Total 18
+2 -8
View File
@@ -90,15 +90,9 @@ id<MTLRenderPipelineState> PipelineStateCreator::operator()(id<MTLDevice> device
NSError* error = nullptr;
id<MTLRenderPipelineState> pipeline = [device newRenderPipelineStateWithDescriptor:descriptor
error:&error];
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];
if (error) {
auto description = [error.localizedDescription 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.");
+3 -3
View File
@@ -59,7 +59,7 @@ void NoopDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
}
void NoopDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,
CallbackHandler* handler, utils::Invocable<void(void)>&& callback) {
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
}
@@ -182,7 +182,7 @@ bool NoopDriver::isProtectedContentSupported() {
return false;
}
bool NoopDriver::isStereoSupported() {
bool NoopDriver::isStereoSupported(backend::StereoscopicType) {
return false;
}
@@ -359,7 +359,7 @@ void NoopDriver::blit(
math::uint2 size) {
}
void NoopDriver::bindPipeline(PipelineState const& pipelineState) {
void NoopDriver::bindPipeline(PipelineState pipelineState) {
}
void NoopDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
@@ -66,8 +66,7 @@ bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept {
OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
Platform::DriverConfig const& driverConfig) noexcept
: mPlatform(platform),
mSamplerMap(32),
mDriverConfig(driverConfig) {
mSamplerMap(32) {
state.vao.p = &mDefaultVAO;
@@ -367,8 +366,7 @@ void OpenGLContext::setDefaultState() noexcept {
}
#endif
if (ext.EXT_clip_cull_distance
&& mDriverConfig.stereoscopicType == StereoscopicType::INSTANCED) {
if (ext.EXT_clip_cull_distance) {
glEnable(GL_CLIP_DISTANCE0);
glEnable(GL_CLIP_DISTANCE1);
}
@@ -511,8 +511,6 @@ private:
mutable tsl::robin_map<SamplerParams, GLuint,
SamplerParams::Hasher, SamplerParams::EqualTo> mSamplerMap;
Platform::DriverConfig const mDriverConfig;
void bindFramebufferResolved(GLenum target, GLuint buffer) noexcept;
const std::array<std::tuple<bool const&, char const*, char const*>, sizeof(bugs)> mBugDatabase{{
+25 -54
View File
@@ -241,15 +241,7 @@ OpenGLDriver::~OpenGLDriver() noexcept { // NOLINT(modernize-use-equals-default)
}
Dispatcher OpenGLDriver::getDispatcher() const noexcept {
auto dispatcher = ConcreteDispatcher<OpenGLDriver>::make();
if (mContext.isES2()) {
dispatcher.draw2_ = +[](Driver& driver, CommandBase* base, intptr_t* next){
using Cmd = COMMAND_TYPE(draw2);
OpenGLDriver& concreteDriver = static_cast<OpenGLDriver&>(driver);
Cmd::execute(&OpenGLDriver::draw2GLES2, concreteDriver, base, next);
};
}
return dispatcher;
return ConcreteDispatcher<OpenGLDriver>::make();
}
// ------------------------------------------------------------------------------------------------
@@ -932,18 +924,16 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
}
void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer const* vb) {
// NOTE: this is called often and must be as efficient as possible.
// NOTE: this is called from draw() 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)) {
@@ -959,7 +949,7 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer
// if a buffer is defined it must not be invalid.
assert_invariant(vb->gl.buffers[bi]);
// if we're on ES2, the user shouldn't use FLAG_INTEGER_TARGET
// if w'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]);
@@ -2050,19 +2040,19 @@ bool OpenGLDriver::isProtectedContentSupported() {
return mPlatform.isProtectedContextSupported();
}
bool OpenGLDriver::isStereoSupported() {
bool OpenGLDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
// 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 (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;
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;
}
}
@@ -3469,7 +3459,7 @@ void OpenGLDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
}
void OpenGLDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,
CallbackHandler* handler, utils::Invocable<void(void)>&& callback) {
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
DEBUG_MARKER()
}
@@ -3847,7 +3837,7 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel)
#endif
}
void OpenGLDriver::bindPipeline(PipelineState const& state) {
void OpenGLDriver::bindPipeline(PipelineState state) {
DEBUG_MARKER()
auto& gl = mContext;
setRasterState(state.rasterState);
@@ -3885,35 +3875,20 @@ 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<const void*>(indexOffset * rp->gl.indicesSize));
} else {
assert_invariant(!mContext.isES2());
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
assert_invariant(!mContext.isES2());
glDrawElementsInstanced(GLenum(rp->type), (GLsizei)indexCount,
rp->gl.getIndicesType(),
reinterpret_cast<const void*>(indexOffset * rp->gl.indicesSize),
(GLsizei)instanceCount);
glDrawElementsInstanced(GLenum(rp->type), (GLsizei)indexCount,
rp->gl.getIndicesType(),
reinterpret_cast<const void*>(indexOffset * rp->gl.indicesSize),
(GLsizei)instanceCount);
#endif
#if FILAMENT_ENABLE_MATDBG
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
#else
CHECK_GL_ERROR(utils::slog.e)
#endif
}
void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
return;
}
assert_invariant(mContext.isES2());
assert_invariant(instanceCount == 1);
glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(),
reinterpret_cast<const void*>(indexOffset * rp->gl.indicesSize));
#if FILAMENT_ENABLE_MATDBG
#ifdef FILAMENT_ENABLE_MATDBG
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
#else
CHECK_GL_ERROR(utils::slog.e)
@@ -3932,11 +3907,7 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
state.vertexBufferInfo = rp->vbih;
bindPipeline(state);
bindRenderPrimitive(rph);
if (UTILS_UNLIKELY(mContext.isES2())) {
draw2GLES2(indexOffset, indexCount, instanceCount);
} else {
draw2(indexOffset, indexCount, instanceCount);
}
draw2(indexOffset, indexCount, instanceCount);
}
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
@@ -3963,7 +3934,7 @@ void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGr
glDispatchCompute(workGroupCount.x, workGroupCount.y, workGroupCount.z);
#endif // BACKEND_OPENGL_LEVEL_GLES31
#if FILAMENT_ENABLE_MATDBG
#ifdef FILAMENT_ENABLE_MATDBG
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
#else
CHECK_GL_ERROR(utils::slog.e)
@@ -336,8 +336,6 @@ private:
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;
+1 -6
View File
@@ -120,21 +120,16 @@ public:
}
inline bool isImageCubeArraySupported() const noexcept {
return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE;
return mPhysicalDeviceFeatures.imageCubeArray;
}
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 = {};
+11 -19
View File
@@ -228,8 +228,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
mReadPixels(mPlatform->getDevice()),
mDescriptorSetManager(mPlatform->getDevice(), &mResourceAllocator),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
mStereoscopicType(driverConfig.stereoscopicType) {
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported) {
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
DebugUtils::mSingleton =
@@ -399,7 +398,7 @@ void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
}
void VulkanDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,
CallbackHandler* handler, utils::Invocable<void(void)>&& callback) {
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
}
void VulkanDriver::setPresentationTime(int64_t monotonic_clock_ns) {
@@ -900,14 +899,13 @@ bool VulkanDriver::isProtectedContentSupported() {
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;
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;
}
}
@@ -1765,7 +1763,7 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
FVK_SYSTRACE_END();
}
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
void VulkanDriver::bindPipeline(PipelineState pipelineState) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("draw");
@@ -1875,12 +1873,6 @@ void VulkanDriver::bindPipeline(PipelineState const& 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();
}
@@ -1971,7 +1963,7 @@ void VulkanDriver::scissor(Viewport scissorBox) {
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
rt->transformClientRectToPlatform(&scissor);
vkCmdSetScissor(cmdbuffer, 0, 1, &scissor);
mPipelineCache.bindScissor(cmdbuffer, scissor);
}
void VulkanDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
+1 -2
View File
@@ -28,7 +28,6 @@
#include "VulkanSamplerCache.h"
#include "VulkanStagePool.h"
#include "VulkanUtility.h"
#include "backend/DriverEnums.h"
#include "caching/VulkanDescriptorSetManager.h"
#include "caching/VulkanPipelineLayoutCache.h"
@@ -167,10 +166,10 @@ private:
VkPipelineLayout pipelineLayout;
};
BoundPipeline mBoundPipeline = {};
RenderPassFboBundle mRenderPassFboInfo;
bool const mIsSRGBSwapChainSupported;
backend::StereoscopicType const mStereoscopicType;
};
} // namespace filament::backend
@@ -79,6 +79,10 @@ 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");
@@ -302,6 +306,7 @@ 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.
@@ -120,6 +120,9 @@ public:
// 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;
@@ -260,6 +263,9 @@ private:
// Current bindings for the pipeline and descriptor sets.
PipelineKey mBoundPipeline = {};
// Current state for scissoring.
VkRect2D mCurrentScissor = {};
};
} // namespace filament::backend
@@ -303,7 +303,7 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
VkResult status = vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
// Fence hasn't been reached. Try waiting again.
if (status != VK_SUCCESS) {
utils::slog.e << "Failed to wait for readPixels fence" << utils::io::endl;
utils::slog.e << "Failed to wait for readPixels fence=" << status << utils::io::endl;
return;
}
@@ -16,8 +16,6 @@
#include "backend/platforms/VulkanPlatform.h"
#include <backend/DriverEnums.h>
#include "vulkan/platform/VulkanPlatformSwapChainImpl.h"
#include "vulkan/VulkanConstants.h"
#include "vulkan/VulkanDriver.h"
@@ -46,11 +44,7 @@ namespace {
constexpr uint32_t const INVALID_VK_INDEX = 0xFFFFFFFF;
using ExtensionSet = VulkanPlatform::ExtensionSet;
inline bool setContains(ExtensionSet const& set, utils::CString const& extension) {
return set.find(extension) != set.end();
};
typedef std::unordered_set<std::string_view> ExtensionSet;
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
// These strings need to be allocated outside a function stack
@@ -86,7 +80,7 @@ FixedCapacityVector<const char*> getEnabledLayers() {
void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
// Print some driver or MoltenVK information if it is available.
if (vkGetPhysicalDeviceProperties2) {
if (vkGetPhysicalDeviceProperties2KHR) {
VkPhysicalDeviceDriverProperties driverProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES,
};
@@ -94,7 +88,7 @@ void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
.pNext = &driverProperties,
};
vkGetPhysicalDeviceProperties2(device, &physicalDeviceProperties2);
vkGetPhysicalDeviceProperties2KHR(device, &physicalDeviceProperties2);
utils::slog.i << "Vulkan device driver: " << driverProperties.driverName << " "
<< driverProperties.driverInfo << utils::io::endl;
}
@@ -154,37 +148,38 @@ void printDepthFormats(VkPhysicalDevice device) {
}
#endif
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,
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,
// 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<VkExtensionProperties> const availableExts =
filament::backend::enumerate(vkEnumerateInstanceExtensionProperties,
FixedCapacityVector<VkExtensionProperties> const availableExts
= filament::backend::enumerate(vkEnumerateInstanceExtensionProperties,
static_cast<char const*>(nullptr) /* pLayerName */);
for (auto const& extProps: availableExts) {
utils::CString name { extProps.extensionName };
if (setContains(TARGET_EXTS, name) || setContains(externallyRequiredExts, name)) {
exts.insert(name);
for (auto const& targetExt: TARGET_EXTS) {
if (targetExt == extProps.extensionName) {
exts.insert(targetExt);
}
}
}
return exts;
}
ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
ExtensionSet const TARGET_EXTS = {
std::string_view const TARGET_EXTS[] = {
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
VK_EXT_DEBUG_MARKER_EXTENSION_NAME,
#endif
@@ -199,9 +194,10 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
= filament::backend::enumerate(vkEnumerateDeviceExtensionProperties, device,
static_cast<const char*>(nullptr) /* pLayerName */);
for (auto const& extension: extensions) {
utils::CString name { extension.extensionName };
if (setContains(TARGET_EXTS, name)) {
exts.insert(name);
for (auto const& targetExt: TARGET_EXTS) {
if (targetExt == extension.extensionName) {
exts.insert(targetExt);
}
}
}
return exts;
@@ -249,7 +245,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();
}
@@ -264,7 +260,7 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
instanceCreateInfo.pApplicationInfo = &appInfo;
instanceCreateInfo.enabledExtensionCount = enabledExtensionCount;
instanceCreateInfo.ppEnabledExtensionNames = ppEnabledExtensions;
if (setContains(requiredExts, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) {
if (requiredExts.find(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME) != requiredExts.end()) {
instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
}
@@ -287,8 +283,8 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
}
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures const& features, uint32_t graphicsQueueFamilyIndex,
ExtensionSet const& deviceExtensions) {
const VkPhysicalDeviceFeatures& features, uint32_t graphicsQueueFamilyIndex,
const ExtensionSet& deviceExtensions) {
VkDevice device;
VkDeviceQueueCreateInfo deviceQueueCreateInfo[1] = {};
const float queuePriority[] = {1.0f};
@@ -296,9 +292,9 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
FixedCapacityVector<const char*> 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 const& ext: deviceExtensions) {
for (auto ext: deviceExtensions) {
requestExtensions.push_back(ext.data());
}
deviceQueueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
@@ -315,7 +311,6 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
.samplerAnisotropy = features.samplerAnisotropy,
.textureCompressionETC2 = features.textureCompressionETC2,
.textureCompressionBC = features.textureCompressionBC,
.shaderClipDistance = features.shaderClipDistance,
};
deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
@@ -328,12 +323,12 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
.imageViewFormatSwizzle = VK_TRUE,
.mutableComparisonSamplers = VK_TRUE,
};
if (setContains(deviceExtensions, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) {
if (deviceExtensions.find(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) != deviceExtensions.end()) {
deviceCreateInfo.pNext = &portability;
}
VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateDevice error=%d.", result);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateDevice error.");
return device;
}
@@ -347,16 +342,16 @@ std::tuple<ExtensionSet, ExtensionSet> pruneExtensions(VkPhysicalDevice device,
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
// debugUtils and debugMarkers extensions are used mutually exclusively.
if (setContains(newInstExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) &&
setContains(newInstExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
if (newInstExts.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != newInstExts.end()
&& newDeviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != newDeviceExts.end()) {
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 (setContains(newInstExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME) &&
!setContains(newInstExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
if (newDeviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != newDeviceExts.end()
&& newInstExts.find(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == newInstExts.end()) {
newDeviceExts.erase(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
}
#endif
@@ -562,7 +557,6 @@ struct VulkanPlatformPrivate {
std::unordered_set<SwapChainPtr> mHeadlessSwapChains;
bool mSharedContext = false;
bool mForceXCBSwapchain = false;
};
void VulkanPlatform::terminate() {
@@ -616,25 +610,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
ExtensionSet instExts;
// If using a shared context, we do not assume any extensions.
if (!mImpl->mSharedContext) {
// 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 = getInstanceExtensions();
instExts.merge(getRequiredInstanceExtensions());
}
@@ -668,12 +644,6 @@ 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
@@ -702,8 +672,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
assert_invariant(mImpl->mGraphicsQueue != VK_NULL_HANDLE);
// Store the extension support in the context
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
context.mDebugUtilsSupported
= instExts.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != instExts.end();
context.mDebugMarkersSupported
= deviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != deviceExts.end();
#ifdef NDEBUG
// If we are in release build, we should not have turned on debug extensions
@@ -776,10 +748,6 @@ 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,
@@ -46,7 +46,7 @@
uint32_t height;
} wl;
}// anonymous namespace
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
#elif 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 <dlfcn.h>
@@ -86,23 +86,22 @@ using namespace bluevk;
namespace filament::backend {
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,
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");
#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;
}
@@ -139,7 +138,7 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
VkResult const result = vkCreateWaylandSurfaceKHR(instance, &createInfo, VKALLOC,
(VkSurfaceKHR*) &surface);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateWaylandSurfaceKHR error.");
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
#elif 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.");
@@ -147,11 +146,15 @@ 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)
@@ -164,9 +167,6 @@ 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,9 +179,6 @@ 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,
@@ -52,14 +52,13 @@ using namespace bluevk;
namespace filament::backend {
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
ExtensionSet const ret = {
VulkanPlatform::ExtensionSet VulkanPlatform::getRequiredInstanceExtensions() {
ExtensionSet ret;
#if defined(__APPLE__)
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,
ret.insert("VK_MVK_macos_surface"); // TODO: replace with VK_EXT_metal_surface
#elif defined(IOS)
ret.insert("VK_MVK_ios_surface");
#endif
};
return ret;
}
+13
View File
@@ -22,11 +22,14 @@
#include <spirv_glsl.hpp>
#include <spirv_msl.hpp>
#include <spirv-tools/libspirv.hpp>
#include "../src/GLSLPostProcessor.h"
#include "builtinResource.h"
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <iostream>
#include <utility>
@@ -36,6 +39,7 @@ namespace test {
using namespace glslang;
using namespace spirv_cross;
using namespace spvtools;
using namespace filament::backend;
@@ -128,6 +132,8 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(
bool ok = tShader.parse(&DefaultTBuiltInResource, version, false, msg);
utils::slog.e <<"-----------" << shaderCString << utils::io::endl;
if (!ok) {
std::cerr << "ERROR: Unable to parse " <<
(stage == ShaderStage::VERTEX ? "vertex" : "fragment") << " shader:" << std::endl;
@@ -146,6 +152,13 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(
GlslangToSpv(*program.getIntermediate(language), spirv);
SpirvTools const tools(SPV_ENV_UNIVERSAL_1_3);
std::string disassembly;
bool rr = tools.Disassemble(spirv, &disassembly);
utils::slog.e <<"dissambly=" << rr << "\n" <<
disassembly << utils::io::endl;
std::string result;
assert_invariant(backend == Backend::OPENGL ||
-122
View File
@@ -1,122 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "BackendTest.h"
using namespace filament;
using namespace filament::backend;
namespace test {
TEST_F(BackendTest, FrameScheduledCallback) {
auto& api = getDriverApi();
// Create a SwapChain.
// In order for the frameScheduledCallback to be called, this must be a real SwapChain (not
// headless) so we obtain a drawable.
auto swapChain = createSwapChain();
Handle<HwRenderTarget> renderTarget = api.createDefaultRenderTarget();
int callbackCountA = 0;
api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountA](PresentCallable callable) {
callable();
callbackCountA++;
});
// Render the first frame.
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget, {});
api.endRenderPass(0);
api.commit(swapChain);
api.endFrame(0);
// Render the next frame. The same callback should be called.
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget, {});
api.endRenderPass(0);
api.commit(swapChain);
api.endFrame(0);
// Now switch out the callback.
int callbackCountB = 0;
api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountB](PresentCallable callable) {
callable();
callbackCountB++;
});
// Render one final frame.
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget, {});
api.endRenderPass(0);
api.commit(swapChain);
api.endFrame(0);
api.finish();
executeCommands();
getDriver().purge();
EXPECT_EQ(callbackCountA, 2);
EXPECT_EQ(callbackCountB, 1);
}
TEST_F(BackendTest, FrameCompletedCallback) {
auto& api = getDriverApi();
// Create a SwapChain.
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
int callbackCountA = 0;
api.setFrameCompletedCallback(swapChain, nullptr,
[&callbackCountA]() { callbackCountA++; });
// Render the first frame.
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.commit(swapChain);
api.endFrame(0);
// Render the next frame. The same callback should be called.
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.commit(swapChain);
api.endFrame(0);
// Now switch out the callback.
int callbackCountB = 0;
api.setFrameCompletedCallback(swapChain, nullptr,
[&callbackCountB]() { callbackCountB++; });
// Render one final frame.
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.commit(swapChain);
api.endFrame(0);
api.finish();
executeCommands();
getDriver().purge();
EXPECT_EQ(callbackCountA, 2);
EXPECT_EQ(callbackCountB, 1);
}
} // namespace test
+64 -5
View File
@@ -23,11 +23,22 @@
#include "private/filament/SamplerInterfaceBlock.h"
#include "private/backend/SamplerGroup.h"
#include <utils/Log.h>
#include <utils/Hash.h>
#include <math/half.h>
#include <fstream>
#include <vector>
#ifndef IOS
#include <imageio/ImageEncoder.h>
#include <image/ColorTransform.h>
using namespace image;
#endif
using namespace filament;
using namespace filament::backend;
@@ -56,7 +67,7 @@ layout(location = 0) out vec4 fragColor;
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
// This parameter is ignored for other backends.
layout(location = 0, set = 1) uniform {samplerType} test_tex;
layout(binding = 0, set = 1) uniform {samplerType} test_tex;
void main() {
vec2 fbsize = vec2(512);
@@ -109,6 +120,49 @@ void main() {
)");
}
static void readScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, size_t const readWidth,
size_t const readHeight, std::function<void(void*)> output) {
size_t const size = readWidth * readHeight * 4;
void* buffer = calloc(1, size);
struct ReadPixelUserData {
std::function<void(void*)> onRead;
};
ReadPixelUserData* userdata = new ReadPixelUserData{output};
auto cb = [](void* buffer, size_t size, void* user) {
ReadPixelUserData* data = (ReadPixelUserData*) user;
data->onRead(buffer);
free(buffer);
delete data;
};
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb,
userdata);
dapi.readPixels(rt, 0, 0, readWidth, readHeight, std::move(pb));
}
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, size_t const readWidth,
size_t const readHeight, test::Backend backend, std::string fname = "") {
size_t const size = readWidth * readHeight * 4;
if (fname.empty()) {
std::string backendStr = "gl";
if (backend == test::Backend::METAL) {
backendStr = "mtl";
} else if (backend == test::Backend::VULKAN) {
backendStr = "vk";
}
fname = "feedback_" + backendStr + "_" + ".png";
}
utils::slog.e << "writing: " << fname << utils::io::endl;
readScreenshot(dapi, rt, readWidth, readHeight, [w = readWidth, h = readHeight, size, fname](void* buffer) {
uint32_t const* texels = (uint32_t*) buffer;
auto hashResutl = utils::hash::murmur3(texels, size / 4, 0);
#ifndef IOS
LinearImage image(w, h, 4);
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, (uint8_t*) buffer);
std::ofstream pngstrm(fname.c_str(), std::ios::binary | std::ios::trunc);
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", fname.c_str());
#endif
});
}
namespace test {
@@ -246,21 +300,22 @@ TEST_F(BackendTest, UpdateImage2D) {
std::vector<TestCase> testCases;
// Test basic upload.
testCases.emplace_back("RGBA, UBYTE -> RGBA8", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8);
// Test format conversion.
// TODO: Vulkan crashes with `Texture at colorAttachment[0] has usage (0x01) which doesn't specify MTLTextureUsageRenderTarget (0x04)'
testCases.emplace_back("RGBA, FLOAT -> RGBA16F", PixelDataFormat::RGBA, PixelDataType::FLOAT, TextureFormat::RGBA16F);
/*
// Test texture formats not all backends support natively.
// TODO: Vulkan crashes with "VK_FORMAT_R32G32B32_SFLOAT is not supported"
testCases.emplace_back("RGB, FLOAT -> RGB32F", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB32F);
testCases.emplace_back("RGB, FLOAT -> RGB16F", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB16F);
// Test packed format uploads.
// packed Test format uploads.
// TODO: Vulkan crashes with "Texture at colorAttachment[0] has usage (0x01) which doesn't specify MTLTextureUsageRenderTarget (0x04)"
testCases.emplace_back("RGBA, UINT_2_10_10_10_REV -> RGB10_A2", PixelDataFormat::RGBA, PixelDataType::UINT_2_10_10_10_REV, TextureFormat::RGB10_A2);
testCases.emplace_back("RGBA, UINT_2_10_10_10_REV -> RGB10_A2", PixelDataFormat::RGBA, PixelDataType::UINT_2_10_10_10_REV, TextureFormat::RGB10_A2);gitgg
testCases.emplace_back("RGB, UINT_10F_11F_11F_REV -> R11F_G11F_B10F", PixelDataFormat::RGB, PixelDataType::UINT_10F_11F_11F_REV, TextureFormat::R11F_G11F_B10F);
testCases.emplace_back("RGB, HALF -> R11F_G11F_B10F", PixelDataFormat::RGB, PixelDataType::HALF, TextureFormat::R11F_G11F_B10F);
@@ -281,8 +336,9 @@ TEST_F(BackendTest, UpdateImage2D) {
// TODO: Vulkan crashes with "Offsets not yet supported"
testCases.emplace_back("RGBA, UBYTE -> RGBA8 (subregions)", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8, 0u, true);
testCases.emplace_back("RGBA, FLOAT -> RGBA16F (subregions)", PixelDataFormat::RGBA, PixelDataType::FLOAT, TextureFormat::RGBA16F, 0u, true);
testCases.emplace_back("RGBA, UBYTE -> RGBA8 (subregions, buffer padding)", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8, 64u, true);
testCases.emplace_back("RGBA, UBYTE -> RGBA8 (subregions, buffer padding)", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8, 64u, true);
testCases.emplace_back("RGB, FLOAT -> RGB32F (subregions, buffer padding)", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB32F, 64u, true);
*/
auto& api = getDriverApi();
@@ -305,6 +361,7 @@ TEST_F(BackendTest, UpdateImage2D) {
std::string const fragment = stringReplace("{samplerType}",
getSamplerTypeName(t.textureFormat), fragmentTemplate);
utils::slog.e <<"sampler=" << fragment << utils::io::endl;
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 };
@@ -348,6 +405,8 @@ TEST_F(BackendTest, UpdateImage2D) {
api.commit(swapChain);
api.endFrame(0);
// dumpScreenshot(api, defaultRenderTarget, 512, 512, test::Backend::VULKAN, t.name);
api.destroyProgram(program);
api.destroySwapChain(swapChain);
api.destroyRenderTarget(defaultRenderTarget);
@@ -1,168 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "BackendTest.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
namespace test {
using namespace filament;
using namespace filament::backend;
static constexpr struct {
size_t TRIANGLE_HIDE = 0;
size_t TRIANGLE_SCALE = 1;
size_t TRIANGLE_OFFSET_X = 2;
size_t TRIANGLE_OFFSET_Y = 3;
size_t RED = 0;
size_t GREEN = 2;
size_t BLUE = 3;
} pushConstantIndex;
static const char* const triangleVs = R"(#version 450 core
layout(push_constant) uniform Constants {
bool hideTriangle;
float triangleScale;
float triangleOffsetX;
float triangleOffsetY;
} pushConstants;
layout(location = 0) in vec4 mesh_position;
void main() {
if (pushConstants.hideTriangle) {
// Test that bools are written correctly. All bits must be 0 if the bool is false.
gl_Position = vec4(0.0);
return;
}
gl_Position = vec4(mesh_position.xy * pushConstants.triangleScale +
vec2(pushConstants.triangleOffsetX, pushConstants.triangleOffsetY), 0.0, 1.0);
#if defined(TARGET_VULKAN_ENVIRONMENT)
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
gl_Position.y = -gl_Position.y;
#endif
})";
static const char* const triangleFs = R"(#version 450 core
layout(push_constant) uniform Constants {
float red;
bool padding; // test correct bool padding
float green;
float blue;
} pushConstants;
precision mediump int; precision highp float;
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = vec4(pushConstants.red, pushConstants.green, pushConstants.blue, 1.0);
})";
TEST_F(BackendTest, PushConstants) {
auto& api = getDriverApi();
api.startCapture(0);
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
// Create a SwapChain and make it current.
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
ProgramHandle program = api.createProgram(std::move(p));
Handle<HwRenderTarget> renderTarget = api.createDefaultRenderTarget();
TrianglePrimitive triangle(api);
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::COLOR0;
params.viewport = { 0, 0, 512, 512 };
params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f);
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {};
ps.program = program;
ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false;
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget, params);
// Set the push constants to scale the triangle in half
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_HIDE, false);
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_SCALE, 0.5f);
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_X, 0.0f);
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_Y, 0.0f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.RED, 0.25f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.GREEN, 0.5f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.BLUE, 1.0f);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
// Draw another triangle, transposed to the upper-right.
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_X, 0.5f);
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_Y, 0.5f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.RED, 1.00f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.GREEN, 0.5f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.BLUE, 0.25f);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
// Draw a final triangle, transposed to the lower-left.
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_X, -0.5f);
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_Y, -0.5f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.RED, 0.5f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.GREEN, 0.25f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.BLUE, 1.00f);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
readPixelsAndAssertHash("pushConstants", 512, 512, renderTarget, 1957275826, true);
api.commit(swapChain);
api.endFrame(0);
// Cleanup.
api.destroySwapChain(swapChain);
api.destroyRenderTarget(renderTarget);
}
api.stopCapture(0);
// Wait for the ReadPixels result to come back.
api.finish();
executeCommands();
getDriver().purge();
}
} // namespace test
+1 -1
View File
@@ -315,7 +315,7 @@ public:
*
* @see View::setStereoscopicOptions
*/
StereoscopicType stereoscopicType = StereoscopicType::NONE;
StereoscopicType stereoscopicType = StereoscopicType::INSTANCED;
/*
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
+37 -49
View File
@@ -464,11 +464,16 @@ public:
Builder& boneIndicesAndWeights(size_t primitiveIndex,
utils::FixedCapacityVector<
utils::FixedCapacityVector<math::float2>> indicesAndWeightsVector) noexcept;
/**
* Controls if the renderable has legacy vertex morphing targets, zero by default. This is
* Controls if the renderable has vertex morphing targets, zero by default. This is
* required to enable GPU morphing.
*
* Filament supports two morphing modes: standard (default) and legacy.
*
* For standard morphing, A MorphTargetBuffer must be created and provided via
* RenderableManager::setMorphTargetBufferAt(). Standard morphing supports up to
* \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets.
*
* For legacy morphing, the attached VertexBuffer must provide data in the
* appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only
* supports up to 4 morph targets and will be deprecated in the future. Legacy morphing must
@@ -481,52 +486,26 @@ public:
Builder& morphing(size_t targetCount) noexcept;
/**
* Controls if the renderable has vertex morphing targets, zero by default. This is
* required to enable GPU morphing.
* Specifies the morph target buffer for a primitive.
*
* Filament supports two morphing modes: standard (default) and legacy.
* The morph target buffer must have an associated renderable and geometry. Two conditions
* must be met:
* 1. The number of morph targets in the buffer must equal the renderable's morph target
* count.
* 2. The vertex count of each morph target must equal the geometry's vertex count.
*
* For standard morphing, A MorphTargetBuffer must be provided.
* Standard morphing supports up to \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets.
*
* For legacy morphing, the attached VertexBuffer must provide data in the
* appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only
* supports up to 4 morph targets and will be deprecated in the future. Legacy morphing must
* be enabled on the material definition: either via the legacyMorphing material attribute
* or by calling filamat::MaterialBuilder::useLegacyMorphing().
*
* See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis
* to advance the animation.
*/
Builder& morphing(MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept;
/**
* @deprecated Use morphing(uint8_t level, size_t primitiveIndex, size_t offset, size_t count) instead
* @param level the level of detail (lod), only 0 can be specified
* @param primitiveIndex zero-based index of the primitive, must be less than the count passed to Builder constructor
* @param morphTargetBuffer specifies the morph target buffer
* @param offset specifies where in the morph target buffer to start reading (expressed as a number of vertices)
* @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3)
*/
Builder& morphing(uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer,
size_t offset, size_t count) noexcept;
/**
* @deprecated Use morphing(uint8_t level, size_t primitiveIndex, size_t offset, size_t count) instead
*/
inline Builder& morphing(uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept {
return morphing(level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
/**
* Specifies the the range of the MorphTargetBuffer to use with this primitive.
*
* @param level the level of detail (lod), only 0 can be specified
* @param primitiveIndex zero-based index of the primitive, must be less than the count passed to Builder constructor
* @param offset specifies where in the morph target buffer to start reading (expressed as a number of vertices)
* @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3)
*/
Builder& morphing(uint8_t level, size_t primitiveIndex,
size_t offset, size_t count) noexcept;
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept;
/**
* Sets the drawing order for blended primitives. The drawing order is either global or
@@ -786,19 +765,14 @@ public:
/**
* Associates a MorphTargetBuffer to the given primitive.
*/
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
size_t offset, size_t count);
/** @deprecated */
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, size_t offset, size_t count);
/** @deprecated */
/**
* Utility method to change a MorphTargetBuffer to the given primitive
*/
inline void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) {
setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer);
/**
* Get a MorphTargetBuffer to the given primitive or null if it doesn't exist.
@@ -932,6 +906,20 @@ protected:
~RenderableManager() = default;
};
RenderableManager::Builder& RenderableManager::Builder::morphing(
uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept {
return morphing(level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
void RenderableManager::setMorphTargetBufferAt(
Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) {
setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
template<typename VECTOR, typename INDEX, typename, typename>
Box RenderableManager::computeAABB(
VECTOR const* UTILS_NONNULL vertices,
+1 -1
View File
@@ -355,7 +355,7 @@ const Engine::Config& Engine::getConfig() const noexcept {
}
bool Engine::isStereoSupported(StereoscopicType stereoscopicType) const noexcept {
return downcast(this)->isStereoSupported();
return downcast(this)->isStereoSupported(stereoscopicType);
}
size_t Engine::getMaxStereoscopicEyes() noexcept {
-4
View File
@@ -684,7 +684,6 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
cmd.info.indexCount = primitive.getIndexCount();
cmd.info.type = primitive.getPrimitiveType();
cmd.info.morphTargetBuffer = morphTargets.buffer->getHwHandle();
cmd.info.morphingOffset = morphTargets.offset;
if constexpr (isColorPass) {
RenderPass::setupColorCommand(cmd, renderableVariant, mi, inverseFrontFaces);
@@ -1030,9 +1029,6 @@ void RenderPass::Executor::execute(FEngine& engine,
rebindPipeline = false;
currentPipeline = pipeline;
driver.bindPipeline(pipeline);
driver.setPushConstant(ShaderStage::VERTEX,
+PushConstantIds::MORPHING_BUFFER_OFFSET, int32_t(info.morphingOffset));
}
if (info.rph != currentPrimitiveHandle) {
+1 -2
View File
@@ -251,7 +251,6 @@ public:
uint32_t indexCount; // 4 bytes
uint32_t index = 0; // 4 bytes
backend::SamplerGroupHandle morphTargetBuffer; // 4 bytes
uint32_t morphingOffset = 0; // 4 bytes
backend::RasterState rasterState; // 4 bytes
@@ -262,7 +261,7 @@ public:
bool hasMorphing : 1; // 1 bit
bool hasHybridInstancing : 1; // 1 bit
uint32_t rfu[2]; // 16 bytes
uint32_t rfu[3]; // 16 bytes
};
static_assert(sizeof(PrimitiveInfo) == 56);
-5
View File
@@ -164,11 +164,6 @@ void RenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level,
downcast(morphTargetBuffer), offset, count);
}
void RenderableManager::setMorphTargetBufferAt(
Instance instance, uint8_t level, size_t primitiveIndex, size_t offset, size_t count) {
downcast(this)->setMorphTargetBufferAt(instance, level, primitiveIndex, offset, count);
}
MorphTargetBuffer* RenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level,
size_t primitiveIndex) const noexcept {
return downcast(this)->getMorphTargetBufferAt(instance, level, primitiveIndex);
+6 -32
View File
@@ -86,7 +86,6 @@ struct RenderableManager::BuilderDetails {
RenderableManager::Builder::GeometryType mGeometryType : 2;
size_t mSkinningBoneCount = 0;
size_t mMorphTargetCount = 0;
FMorphTargetBuffer* mMorphTargetBuffer = nullptr;
Bone const* mUserBones = nullptr;
mat4f const* mUserBoneMatrices = nullptr;
FSkinningBuffer* mSkinningBuffer = nullptr;
@@ -274,18 +273,6 @@ RenderableManager::Builder& RenderableManager::Builder::morphing(size_t targetCo
return *this;
}
RenderableManager::Builder& RenderableManager::Builder::morphing(
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept {
mImpl->mMorphTargetBuffer = downcast(morphTargetBuffer);
mImpl->mMorphTargetCount = morphTargetBuffer->getCount();
return *this;
}
RenderableManager::Builder& RenderableManager::Builder::morphing(
uint8_t level, size_t primitiveIndex, size_t offset, size_t count) noexcept {
return morphing(level, primitiveIndex, mImpl->mMorphTargetBuffer, offset, count);
}
RenderableManager::Builder& RenderableManager::Builder::morphing(uint8_t, size_t primitiveIndex,
MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) noexcept {
std::vector<Entry>& entries = mImpl->mEntries;
@@ -659,14 +646,10 @@ void FRenderableManager::create(
// Create and initialize all needed MorphTargets.
// It's required to avoid branches in hot loops.
FMorphTargetBuffer* morphTargetBuffer = builder->mMorphTargetBuffer;
if (morphTargetBuffer == nullptr) {
morphTargetBuffer = mEngine.getDummyMorphTargetBuffer();
}
MorphTargets* const morphTargets = new MorphTargets[entryCount];
std::generate_n(morphTargets, entryCount,
[morphTargetBuffer]() -> MorphTargets {
return { morphTargetBuffer, 0, 0 };
[dummy = mEngine.getDummyMorphTargetBuffer()]() -> MorphTargets {
return { dummy, 0, 0 };
});
mManager[ci].morphTargets = { morphTargets, size_type(entryCount) };
@@ -966,13 +949,15 @@ void FRenderableManager::setMorphWeights(Instance instance, float const* weights
void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level,
size_t primitiveIndex, FMorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) {
assert_invariant(offset == 0 && "Offset not yet supported.");
assert_invariant(count == morphTargetBuffer->getVertexCount() && "Count not yet supported.");
if (instance) {
assert_invariant(morphTargetBuffer);
MorphWeights const& morphWeights = mManager[instance].morphWeights;
ASSERT_PRECONDITION(morphWeights.count == count,
ASSERT_PRECONDITION(morphWeights.count == morphTargetBuffer->getCount(),
"Only %d morph targets can be set (count=%d)",
morphWeights.count, count);
morphWeights.count, morphTargetBuffer->getCount());
Slice<MorphTargets>& morphTargets = getMorphTargets(instance, level);
if (primitiveIndex < morphTargets.size()) {
@@ -982,17 +967,6 @@ void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level
}
}
void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level,
size_t primitiveIndex, size_t offset, size_t count) {
if (instance) {
Slice<MorphTargets>& morphTargets = getMorphTargets(instance, level);
if (primitiveIndex < morphTargets.size()) {
setMorphTargetBufferAt(instance, level,
primitiveIndex, morphTargets[primitiveIndex].buffer, offset, count);
}
}
}
MorphTargetBuffer* FRenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level,
size_t primitiveIndex) const noexcept {
if (instance) {
@@ -157,8 +157,6 @@ public:
void setMorphWeights(Instance instance, float const* weights, size_t count, size_t offset);
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
FMorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count);
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
size_t offset, size_t count);
MorphTargetBuffer* getMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex) const noexcept;
size_t getMorphTargetCount(Instance instance) const noexcept;
+2 -5
View File
@@ -103,8 +103,7 @@ Engine* FEngine::create(Engine::Builder const& builder) {
.textureUseAfterFreePoolSize = instance->getConfig().textureUseAfterFreePoolSize,
.disableParallelShaderCompile = instance->getConfig().disableParallelShaderCompile,
.disableHandleUseAfterFreeCheck = instance->getConfig().disableHandleUseAfterFreeCheck,
.forceGLES2Context = instance->getConfig().forceGLES2Context,
.stereoscopicType = instance->getConfig().stereoscopicType,
.forceGLES2Context = instance->getConfig().forceGLES2Context
};
instance->mDriver = platform->createDriver(sharedContext, driverConfig);
@@ -353,7 +352,6 @@ void FEngine::init() {
FMaterial::DefaultMaterialBuilder defaultMaterialBuilder;
switch (mConfig.stereoscopicType) {
case StereoscopicType::NONE:
case StereoscopicType::INSTANCED:
defaultMaterialBuilder.package(
MATERIALS_DEFAULTMATERIAL_DATA, MATERIALS_DEFAULTMATERIAL_SIZE);
@@ -677,8 +675,7 @@ int FEngine::loop() {
.textureUseAfterFreePoolSize = mConfig.textureUseAfterFreePoolSize,
.disableParallelShaderCompile = mConfig.disableParallelShaderCompile,
.disableHandleUseAfterFreeCheck = mConfig.disableHandleUseAfterFreeCheck,
.forceGLES2Context = mConfig.forceGLES2Context,
.stereoscopicType = mConfig.stereoscopicType,
.forceGLES2Context = mConfig.forceGLES2Context
};
mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig);
+3 -3
View File
@@ -184,8 +184,8 @@ public:
return CONFIG_MAX_INSTANCES;
}
bool isStereoSupported() const noexcept {
return getDriver().isStereoSupported();
bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept {
return getDriver().isStereoSupported(stereoscopicType);
}
static size_t getMaxStereoscopicEyes() noexcept {
@@ -600,7 +600,7 @@ public:
bool debug_froxel_visualization = false;
} lighting;
struct {
bool combine_multiview_images = false;
bool combine_multiview_images = true;
} stereo;
matdbg::DebugServer* server = nullptr;
} debug;
+2 -1
View File
@@ -385,7 +385,8 @@ void FMaterial::compile(CompilerPriorityQueue priority,
utils::Invocable<void(Material*)>&& callback) noexcept {
// Turn off the STE variant if stereo is not supported.
if (!mEngine.getDriverApi().isStereoSupported()) {
const StereoscopicType stereoscopicType = mEngine.getConfig().stereoscopicType;
if (!mEngine.getDriverApi().isStereoSupported(stereoscopicType)) {
variantSpec &= ~UserVariantFilterMask(UserVariantFilterBit::STE);
}
+5 -5
View File
@@ -153,7 +153,7 @@ void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t targetIndex,
"MorphTargetBuffer (size=%lu) overflow (count=%u, offset=%u)",
(unsigned)mVertexCount, (unsigned)count, (unsigned)offset);
auto size = getSize<VertexAttribute::POSITION>(count);
auto size = getSize<VertexAttribute::POSITION>(mVertexCount);
ASSERT_PRECONDITION(targetIndex < mCount,
"%d target index must be < %d", targetIndex, mCount);
@@ -177,7 +177,7 @@ void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t targetIndex,
"MorphTargetBuffer (size=%lu) overflow (count=%u, offset=%u)",
(unsigned)mVertexCount, (unsigned)count, (unsigned)offset);
auto size = getSize<VertexAttribute::POSITION>(count);
auto size = getSize<VertexAttribute::POSITION>(mVertexCount);
ASSERT_PRECONDITION(targetIndex < mCount,
"%d target index must be < %d", targetIndex, mCount);
@@ -200,7 +200,7 @@ void FMorphTargetBuffer::setTangentsAt(FEngine& engine, size_t targetIndex,
"MorphTargetBuffer (size=%lu) overflow (count=%u, offset=%u)",
(unsigned)mVertexCount, (unsigned)count, (unsigned)offset);
const auto size = getSize<VertexAttribute::TANGENTS>(count);
const auto size = getSize<VertexAttribute::TANGENTS>(mVertexCount);
ASSERT_PRECONDITION(targetIndex < mCount,
"%d target index must be < %d", targetIndex, mCount);
@@ -226,8 +226,8 @@ void FMorphTargetBuffer::updateDataAt(backend::DriverApi& driver,
size_t const xoffset = offset % MAX_MORPH_TARGET_BUFFER_WIDTH;
size_t const textureWidth = getWidth(mVertexCount);
size_t const alignment = ((textureWidth - xoffset) % textureWidth);
size_t const lineCount = (count > alignment) ? (count - alignment) / textureWidth : 0;
size_t const lastLineCount = (count > alignment) ? (count - alignment) % textureWidth : 0;
size_t const lineCount = (count - alignment) / textureWidth;
size_t const lastLineCount = (count - alignment) % textureWidth;
// 'out' buffer is going to be used up to 3 times, so for simplicity we use a shared_buffer
// to manage its lifetime. One side effect of this is that the callbacks below will allocate
+1 -3
View File
@@ -132,7 +132,6 @@ 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;
@@ -140,8 +139,7 @@ FMaterial const* FSkybox::createMaterial(FEngine& engine) {
#ifdef FILAMENT_ENABLE_MULTIVIEW
builder.package(MATERIALS_SKYBOX_MULTIVIEW_DATA, MATERIALS_SKYBOX_MULTIVIEW_SIZE);
#else
PANIC_POSTCONDITION("Multiview is enabled in the Engine, but this build has not "
"been compiled for multiview.");
assert_invariant(false);
#endif
break;
}
+18 -5
View File
@@ -79,11 +79,24 @@ bool FSwapChain::isFrameScheduledCallbackSet() const noexcept {
return mFrameScheduledCallbackIsSet;
}
void FSwapChain::setFrameCompletedCallback(
backend::CallbackHandler* handler, FrameCompletedCallback&& callback) noexcept {
using namespace std::placeholders;
auto boundCallback = std::bind(std::move(callback), this);
mEngine.getDriverApi().setFrameCompletedCallback(mHwSwapChain, handler, std::move(boundCallback));
void FSwapChain::setFrameCompletedCallback(backend::CallbackHandler* handler,
utils::Invocable<void(SwapChain*)>&& callback) noexcept {
struct Callback {
utils::Invocable<void(SwapChain*)> f;
SwapChain* s;
static void func(void* user) {
auto* const c = reinterpret_cast<Callback*>(user);
c->f(c->s);
delete c;
}
};
if (callback) {
auto* const user = new(std::nothrow) Callback{ std::move(callback), this };
mEngine.getDriverApi().setFrameCompletedCallback(
mHwSwapChain, handler, &Callback::func, static_cast<void*>(user));
} else {
mEngine.getDriverApi().setFrameCompletedCallback(mHwSwapChain, nullptr, nullptr, nullptr);
}
}
bool FSwapChain::isSRGBSwapChainSupported(FEngine& engine) noexcept {
+1 -1
View File
@@ -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()),
mIsStereoSupported(engine.getDriverApi().isStereoSupported(engine.getConfig().stereoscopicType)),
mPerViewUniforms(engine) {
DriverApi& driver = engine.getDriverApi();
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.52.1"
spec.version = "1.51.8"
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.1/filament-v1.52.1-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.8/filament-v1.51.8-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 52;
static constexpr size_t MATERIAL_VERSION = 51;
/**
* Supported shading models
@@ -72,7 +72,7 @@ enum class ReservedSpecializationConstants : uint8_t {
CONFIG_STEREO_EYE_COUNT = 8, // don't change (hardcoded in ShaderCompilerService.cpp)
};
enum class PushConstantIds : uint8_t {
enum class PushConstantIds {
MORPHING_BUFFER_OFFSET = 0,
};
@@ -143,8 +143,6 @@ struct utils::EnableIntegerOperators<filament::SamplerBindingPoints> : public st
template<>
struct utils::EnableIntegerOperators<filament::ReservedSpecializationConstants> : public std::true_type {};
template<>
struct utils::EnableIntegerOperators<filament::PushConstantIds> : public std::true_type {};
template<>
struct utils::EnableIntegerOperators<filament::PostProcessVariant> : public std::true_type {};
template<>
+3 -15
View File
@@ -255,17 +255,6 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob *spirv, std::string *outMsl,
mslCompiler.add_msl_resource_binding(argBufferBinding);
}
// Bind push constants to [buffer(26)]
MSLResourceBinding pushConstantBinding;
// the baseType doesn't matter, but can't be UNKNOWN
pushConstantBinding.basetype = SPIRType::BaseType::Struct;
pushConstantBinding.stage = executionModel;
pushConstantBinding.desc_set = kPushConstDescSet;
pushConstantBinding.binding = kPushConstBinding;
pushConstantBinding.count = 1;
pushConstantBinding.msl_buffer = 26;
mslCompiler.add_msl_resource_binding(pushConstantBinding);
auto updateResourceBindingDefault = [executionModel, &mslCompiler](const auto& resource) {
auto set = mslCompiler.get_decoration(resource.id, spv::DecorationDescriptorSet);
auto binding = mslCompiler.get_decoration(resource.id, spv::DecorationBinding);
@@ -550,16 +539,15 @@ 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;
}
}
@@ -78,8 +78,6 @@ 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;
@@ -101,8 +99,6 @@ 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;
@@ -124,8 +120,6 @@ 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;
}
}
@@ -223,8 +217,6 @@ 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) {
-2
View File
@@ -619,8 +619,6 @@ 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) {
-1
View File
@@ -39,7 +39,6 @@ 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
)
@@ -299,16 +299,6 @@ public:
}
}
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 };
+6 -9
View File
@@ -286,11 +286,6 @@ public:
*/
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.
@@ -340,7 +335,6 @@ public:
// 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;
@@ -380,6 +374,11 @@ public:
}
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.
@@ -388,8 +387,7 @@ protected:
* @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, std::string reasonLiteral);
TPanic(char const* function, char const* file, int line, std::string reason);
~TPanic() override;
@@ -398,7 +396,6 @@ private:
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;
+1 -11
View File
@@ -60,11 +60,6 @@ public:
std::fill(std::begin(storage), std::end(storage), 0);
}
template<typename U, typename = typename std::enable_if_t<N == 1, U>>
explicit bitset(U value) noexcept {
storage[0] = value;
}
T getBitsAt(size_t n) const noexcept {
assert_invariant(n<N);
return storage[n];
@@ -99,8 +94,6 @@ public:
size_t size() const noexcept { return N * BITS_PER_WORD; }
bool empty() const noexcept { return none(); }
bool test(size_t bit) const noexcept { return operator[](bit); }
void set(size_t b) noexcept {
@@ -124,14 +117,11 @@ public:
storage[b / BITS_PER_WORD] ^= T(1) << (b % BITS_PER_WORD);
}
void reset() noexcept {
std::fill(std::begin(storage), std::end(storage), 0);
}
void clear() noexcept {
reset();
}
bool operator[](size_t b) const noexcept {
assert_invariant(b / BITS_PER_WORD < N);
return bool(storage[b / BITS_PER_WORD] & (T(1) << (b % BITS_PER_WORD)));
+13 -12
View File
@@ -123,16 +123,22 @@ void Panic::setPanicHandler(PanicHandlerCallback handler, void* user) noexcept {
// ------------------------------------------------------------------------------------------------
template<typename T>
TPanic<T>::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) {
TPanic<T>::TPanic(std::string reason) :
m_reason(std::move(reason)) {
m_callstack.update(1);
buildMessage();
}
template<typename T>
TPanic<T>::~TPanic() = default;
TPanic<T>::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<typename T>
TPanic<T>::~TPanic() {
}
template<typename T>
const char* TPanic<T>::what() const noexcept {
@@ -144,11 +150,6 @@ const char* TPanic<T>::getReason() const noexcept {
return m_reason.c_str();
}
template<typename T>
const char* TPanic<T>::getReasonLiteral() const noexcept {
return m_reason_literal.c_str();
}
template<typename T>
const char* TPanic<T>::getFunction() const noexcept {
return m_function;
@@ -196,9 +197,9 @@ template<typename T>
void TPanic<T>::panic(char const* function, char const* file, int line, const char* format, ...) {
va_list args;
va_start(args, format);
std::string reason(formatString(format, args));
std::string const reason(formatString(format, args));
va_end(args);
T e(function, formatFile(file), line, std::move(reason), format);
T e(function, formatFile(file), line, reason);
// always log the Panic at the point it is detected
e.log();
-6
View File
@@ -34,12 +34,6 @@ 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
# ==================================================================================================
+3 -15
View File
@@ -387,9 +387,6 @@ 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<bool>("d.stereo.combine_multiview_images") = true;
using namespace filament;
LightManager::Builder(LightManager::Type::SUN)
.color(mSettings.lighting.sunlightColor)
@@ -1097,18 +1094,9 @@ void ViewerGui::updateUserInterface() {
ImGui::ListBox("Cameras", &mCurrentCamera, cstrings.data(), cstrings.size());
}
#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<bool>("d.stereo.combine_multiview_images"));
ImGui::Unindent();
#endif
#endif
ImGui::SliderFloat("Ocular distance", &mSettings.viewer.cameraEyeOcularDistance, 0.0f,
1.0f);
ImGui::Checkbox("Instanced stereo", &mSettings.view.stereoscopicOptions.enabled);
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°");
+5
View File
@@ -893,6 +893,11 @@ 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<bool>("d.stereo.combine_multiview_images"));
#endif
bool debugFroxelVisualization;
if (debug.getProperty("d.lighting.debug_froxel_visualization",
&debugFroxelVisualization)) {
+16 -9
View File
@@ -54,6 +54,7 @@ struct App {
Skybox* skybox;
Entity renderable;
MorphTargetBuffer *mt1;
MorphTargetBuffer *mt2;
};
struct Vertex {
@@ -173,7 +174,12 @@ int main(int argc, char** argv) {
.build(*engine);
app.mt1 = MorphTargetBuffer::Builder()
.vertexCount(9 * 2)
.vertexCount(9)
.count(3)
.build(*engine);
app.mt2 = MorphTargetBuffer::Builder()
.vertexCount(9)
.count(3)
.build(*engine);
@@ -184,12 +190,12 @@ int main(int argc, char** argv) {
app.mt1->setTangentsAt(*engine,1, targets_tan+3, 3, 0);
app.mt1->setTangentsAt(*engine,2, targets_tan+6, 3, 0);
app.mt1->setPositionsAt(*engine,0, targets_pos2, 3, 9);
app.mt1->setPositionsAt(*engine,1, targets_pos2+3, 3, 9);
app.mt1->setPositionsAt(*engine,2, targets_pos2+6, 3, 9);
app.mt1->setTangentsAt(*engine,0, targets_tan, 3, 9);
app.mt1->setTangentsAt(*engine,1, targets_tan+3, 3, 9);
app.mt1->setTangentsAt(*engine,2, targets_tan+6, 3, 9);
app.mt2->setPositionsAt(*engine,0, targets_pos2, 3, 0);
app.mt2->setPositionsAt(*engine,1, targets_pos2+3, 3, 0);
app.mt2->setPositionsAt(*engine,2, targets_pos2+6, 3, 0);
app.mt2->setTangentsAt(*engine,0, targets_tan, 3, 0);
app.mt2->setTangentsAt(*engine,1, targets_tan+3, 3, 0);
app.mt2->setTangentsAt(*engine,2, targets_tan+6, 3, 0);
app.renderable = EntityManager::get().create();
@@ -203,8 +209,8 @@ int main(int argc, char** argv) {
.receiveShadows(false)
.castShadows(false)
.morphing(3)
.morphing(0,0,app.mt1, 0, app.mt1->getCount())
.morphing(0,1,app.mt1, 9, app.mt1->getCount())
.morphing(0,0,app.mt1)
.morphing(0,1,app.mt2)
.build(*engine, app.renderable);
scene->addEntity(app.renderable);
@@ -220,6 +226,7 @@ int main(int argc, char** argv) {
engine->destroy(app.vb);
engine->destroy(app.ib);
engine->destroy(app.mt1);
engine->destroy(app.mt2);
engine->destroyCameraComponent(app.camera);
utils::EntityManager::get().destroy(app.camera);
};
+4 -2
View File
@@ -24,11 +24,13 @@ highp mat4 getViewFromClipMatrix() {
/** @public-api */
highp mat4 getClipFromWorldMatrix() {
#if defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_INSTANCED)
#if defined(VARIANT_HAS_STEREO)
#if defined(FILAMENT_STEREO_INSTANCED)
int eye = instance_index % CONFIG_STEREO_EYE_COUNT;
return frameUniforms.clipFromWorldMatrix[eye];
#elif defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_MULTIVIEW)
#elif defined(FILAMENT_STEREO_MULTIVIEW)
return frameUniforms.clipFromWorldMatrix[gl_ViewID_OVR];
#endif
#else
return frameUniforms.clipFromWorldMatrix[0];
#endif
+2 -4
View File
@@ -146,8 +146,7 @@ void skinNormalTangent(inout vec3 n, inout vec3 t, const uvec4 ids, const vec4 w
#define MAX_MORPH_TARGET_BUFFER_WIDTH 2048
void morphPosition(inout vec4 p) {
int index = getVertexIndex() + pushConstants.morphingBufferOffset;
ivec3 texcoord = ivec3(index % MAX_MORPH_TARGET_BUFFER_WIDTH, index / MAX_MORPH_TARGET_BUFFER_WIDTH, 0);
ivec3 texcoord = ivec3(getVertexIndex() % MAX_MORPH_TARGET_BUFFER_WIDTH, getVertexIndex() / MAX_MORPH_TARGET_BUFFER_WIDTH, 0);
int c = object_uniforms_morphTargetCount;
for (int i = 0; i < c; ++i) {
float w = morphingUniforms.weights[i][0];
@@ -160,8 +159,7 @@ void morphPosition(inout vec4 p) {
void morphNormal(inout vec3 n) {
vec3 baseNormal = n;
int index = getVertexIndex() + pushConstants.morphingBufferOffset;
ivec3 texcoord = ivec3(index % MAX_MORPH_TARGET_BUFFER_WIDTH, index / MAX_MORPH_TARGET_BUFFER_WIDTH, 0);
ivec3 texcoord = ivec3(getVertexIndex() % MAX_MORPH_TARGET_BUFFER_WIDTH, getVertexIndex() / MAX_MORPH_TARGET_BUFFER_WIDTH, 0);
int c = object_uniforms_morphTargetCount;
for (int i = 0; i < c; ++i) {
float w = morphingUniforms.weights[i][0];
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.52.1",
"version": "1.51.8",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",