Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e32e6cf2a |
+5
-40
@@ -437,11 +437,10 @@ Finally simply open `docs/html/index.html` in your web browser.
|
||||
|
||||
## Software Rasterization
|
||||
|
||||
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.
|
||||
We have tested swiftshader for running software rasterization on the Vulkan backend. To use this,
|
||||
please first make sure that the [Vulkan SDK](https://www.lunarg.com/vulkan-sdk/) is installed on
|
||||
your machine. If you are doing a manual installation of the SDK on Linux, you will have to source
|
||||
`setup-env.sh` in the SDK's root folder to make sure the Vulkan loader is the first lib loaded.
|
||||
|
||||
### Swiftshader (Vulkan) [tested on macOS and Linux]
|
||||
|
||||
@@ -458,39 +457,5 @@ and then set `VK_ICD_FILENAMES` to the ICD json produced in the build. For examp
|
||||
export VK_ICD_FILENAMES=/Users/user/swiftshader/build/Darwin/vk_swiftshader_icd.json
|
||||
```
|
||||
|
||||
Build and run Filament as usual and specify the Vulkan backend when creating the Engine.
|
||||
Build Filament as normal and use the vulkan backend.
|
||||
|
||||
### Mesa's LLVMPipe (GL) and Lavapipe (Vulkan) [tested on Linux]
|
||||
|
||||
We will only cover steps that build Mesa from source. The official documentation of Mesa mentioned
|
||||
that in general precompiled libraries [are **not** made available](https://docs.mesa3d.org/precompiled.html).
|
||||
|
||||
Download the repo and make sure you have the build depedencies. For example (assuming an Ubuntu/Debian distro),
|
||||
```shell
|
||||
git clone https://gitlab.freedesktop.org/mesa/mesa.git
|
||||
sudo apt-get build-dep mesa
|
||||
```
|
||||
|
||||
To build both the GL and Vulkan rasterizers,
|
||||
|
||||
```shell
|
||||
cd mesa
|
||||
mkdir -p out
|
||||
meson setup builddir/ -Dprefix=$(pwd)/out -Dglx=xlib -Dgallium-drivers=swrast -Dvulkan-drivers=swrast
|
||||
meson install -C builddir/
|
||||
```
|
||||
|
||||
For GL, we need to ensure that we load the GL lib from the mesa output directory. For example, to run
|
||||
the debug `gltf_viewer`, we would execute
|
||||
```shell
|
||||
LD_LIBRARY_PATH=/Users/user/mesa/out/lib/x86_64-linux-gnu \
|
||||
./out/cmake-debug/samples/gltf_viewer -a opengl
|
||||
```
|
||||
|
||||
For Vulkan, we need to set the path to the ICD json, which tells the loader where to find the driver
|
||||
library. To run `gltf_viewer`, we would execute
|
||||
```shell
|
||||
VK_ICD_FILENAMES=/Users/user/mesa/out/share/vulkan/icd.d/lvp_icd.x86_64.json \
|
||||
./out/cmake-debug/samples/gltf_viewer -a vulkan
|
||||
|
||||
```
|
||||
|
||||
+4
-6
@@ -524,15 +524,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.
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.52.2'
|
||||
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.2'
|
||||
pod 'Filament', '~> 1.51.8'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -7,13 +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.2
|
||||
|
||||
|
||||
## v1.52.1
|
||||
|
||||
- Add instructions for using Mesa for software rasterization
|
||||
|
||||
## v1.51.9
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+16
-43
@@ -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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.52.2
|
||||
VERSION_NAME=1.51.8
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -64,9 +64,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"
|
||||
@@ -178,8 +175,6 @@ ASAN_UBSAN_OPTION=""
|
||||
|
||||
BACKEND_DEBUG_FLAG_OPTION=""
|
||||
|
||||
STEREOSCOPIC_OPTION=""
|
||||
|
||||
IOS_BUILD_SIMULATOR=false
|
||||
BUILD_UNIVERSAL_LIBRARIES=false
|
||||
|
||||
@@ -239,7 +234,6 @@ function build_desktop_target {
|
||||
${MATOPT_OPTION} \
|
||||
${ASAN_UBSAN_OPTION} \
|
||||
${BACKEND_DEBUG_FLAG_OPTION} \
|
||||
${STEREOSCOPIC_OPTION} \
|
||||
${architectures} \
|
||||
../..
|
||||
ln -sf "out/cmake-${lc_target}/compile_commands.json" \
|
||||
@@ -374,7 +368,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 +602,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 +789,7 @@ function check_debug_release_build {
|
||||
|
||||
pushd "$(dirname "$0")" > /dev/null
|
||||
|
||||
while getopts ":hacCfgijmp:q:uvslwedk:bx:S:" opt; do
|
||||
while getopts ":hacCfgijmp:q:uvslwedk:bx:" opt; do
|
||||
case ${opt} in
|
||||
h)
|
||||
print_help
|
||||
@@ -936,20 +929,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 ""
|
||||
|
||||
@@ -419,7 +419,6 @@ if (APPLE OR LINUX)
|
||||
test/test_BufferUpdates.cpp
|
||||
test/test_Callbacks.cpp
|
||||
test/test_MRT.cpp
|
||||
test/test_PushConstants.cpp
|
||||
test/test_LoadImage.cpp
|
||||
test/test_StencilBuffer.cpp
|
||||
test/test_Scissor.cpp
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -300,7 +300,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameTimeSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAutoDepthResolveSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isSRGBSwapChainSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedContentSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_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)
|
||||
|
||||
@@ -173,8 +173,8 @@ public:
|
||||
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
|
||||
auto const pNode = static_cast<typename Allocator::Node*>(p);
|
||||
uint8_t const expectedAge = pNode[-1].age;
|
||||
FILAMENT_CHECK_POSTCONDITION(expectedAge == age) <<
|
||||
"use-after-free of Handle with id=" << handle.getId();
|
||||
ASSERT_POSTCONDITION(expectedAge == age,
|
||||
"use-after-free of Handle with id=%d", handle.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,8 +240,8 @@ private:
|
||||
Node* const pNode = static_cast<Node*>(p);
|
||||
uint8_t& expectedAge = pNode[-1].age;
|
||||
if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) {
|
||||
FILAMENT_CHECK_POSTCONDITION(expectedAge == age) <<
|
||||
"double-free of Handle of size " << size << " at " << p;
|
||||
ASSERT_POSTCONDITION(expectedAge == age,
|
||||
"double-free of Handle of size %d at %p", size, p);
|
||||
}
|
||||
expectedAge = (expectedAge + 1) & 0xF; // fixme
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ PresentCallable::PresentCallable(PresentFn fn, void* user) noexcept
|
||||
}
|
||||
|
||||
void PresentCallable::operator()(bool presentFrame) noexcept {
|
||||
FILAMENT_CHECK_PRECONDITION(mPresentFn) << "This PresentCallable was already called. "
|
||||
"PresentCallables should be called exactly once.";
|
||||
ASSERT_PRECONDITION(mPresentFn, "This PresentCallable was already called. " \
|
||||
"PresentCallables should be called exactly once.");
|
||||
mPresentFn(presentFrame, mUser);
|
||||
// Set mPresentFn to nullptr to denote that the callable has been called.
|
||||
mPresentFn = nullptr;
|
||||
|
||||
@@ -119,9 +119,9 @@ void* CircularBuffer::alloc(size_t size) noexcept {
|
||||
data = mmap(nullptr, size * 2 + BLOCK_SIZE,
|
||||
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(data) <<
|
||||
"couldn't allocate " << (size * 2 / 1024) <<
|
||||
" KiB of virtual address space for the command buffer";
|
||||
ASSERT_POSTCONDITION(data,
|
||||
"couldn't allocate %u KiB of virtual address space for the command buffer",
|
||||
(size * 2 / 1024));
|
||||
|
||||
slog.d << "WARNING: Using soft CircularBuffer (" << (size * 2 / 1024) << " KiB)"
|
||||
<< io::endl;
|
||||
|
||||
@@ -74,6 +74,8 @@ void CommandBufferQueue::setPaused(bool paused) {
|
||||
|
||||
bool CommandBufferQueue::isExitRequested() const {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
ASSERT_PRECONDITION( mExitRequested == 0 || mExitRequested == EXIT_REQUESTED,
|
||||
"mExitRequested is corrupted (value = 0x%08x)!", mExitRequested);
|
||||
return (bool)mExitRequested;
|
||||
}
|
||||
|
||||
@@ -106,11 +108,11 @@ void CommandBufferQueue::flush() noexcept {
|
||||
mCondition.notify_one();
|
||||
|
||||
// circular buffer is too small, we corrupted the stream
|
||||
FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) <<
|
||||
ASSERT_POSTCONDITION(used <= mFreeSpace,
|
||||
"Backend CommandStream overflow. Commands are corrupted and unrecoverable.\n"
|
||||
"Please increase minCommandBufferSizeMB inside the Config passed to Engine::create.\n"
|
||||
"Space used at this time: " << used <<
|
||||
" bytes, overflow: " << used - mFreeSpace << " bytes";
|
||||
"Space used at this time: %u bytes, overflow: %u bytes",
|
||||
(unsigned)used, unsigned(used - mFreeSpace));
|
||||
|
||||
// wait until there is enough space in the buffer
|
||||
mFreeSpace -= used;
|
||||
@@ -129,11 +131,9 @@ void CommandBufferQueue::flush() noexcept {
|
||||
#endif
|
||||
|
||||
SYSTRACE_NAME("waiting: CircularBuffer::flush()");
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(!mPaused) <<
|
||||
ASSERT_POSTCONDITION(!mPaused,
|
||||
"CommandStream is full, but since the rendering thread is paused, "
|
||||
"the buffer cannot flush and we will deadlock. Instead, abort.";
|
||||
|
||||
"the buffer cannot flush and we will deadlock. Instead, abort.");
|
||||
mCondition.wait(lock, [this, requiredSize]() -> bool {
|
||||
// TODO: on macOS, we need to call pumpEvents from time to time
|
||||
return mFreeSpace >= requiredSize;
|
||||
@@ -149,6 +149,10 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
|
||||
while ((mCommandBuffersToExecute.empty() || mPaused) && !mExitRequested) {
|
||||
mCondition.wait(lock);
|
||||
}
|
||||
|
||||
ASSERT_PRECONDITION( mExitRequested == 0 || mExitRequested == EXIT_REQUESTED,
|
||||
"mExitRequested is corrupted (value = 0x%08x)!", mExitRequested);
|
||||
|
||||
return std::move(mCommandBuffersToExecute);
|
||||
}
|
||||
|
||||
|
||||
@@ -113,9 +113,9 @@ HandleBase::HandleId HandleAllocator<P0, P1, P2>::allocateHandleSlow(size_t size
|
||||
|
||||
HandleBase::HandleId id = (++mId) | HANDLE_HEAP_FLAG;
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(mId < HANDLE_HEAP_FLAG) <<
|
||||
ASSERT_POSTCONDITION(mId < HANDLE_HEAP_FLAG,
|
||||
"No more Handle ids available! This can happen if HandleAllocator arena has been full"
|
||||
" for a while. Please increase FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB";
|
||||
" for a while. Please increase FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB");
|
||||
|
||||
mOverflowMap.emplace(id, p);
|
||||
lock.unlock();
|
||||
|
||||
@@ -109,8 +109,9 @@ inline bool MTLSizeEqual(T a, T b) noexcept {
|
||||
MetalBlitter::MetalBlitter(MetalContext& context) noexcept : mContext(context) { }
|
||||
|
||||
void MetalBlitter::blit(id<MTLCommandBuffer> cmdBuffer, const BlitArgs& args, const char* label) {
|
||||
FILAMENT_CHECK_PRECONDITION(args.source.region.size.depth == args.destination.region.size.depth)
|
||||
<< "Blitting requires the source and destination regions to have the same depth.";
|
||||
|
||||
ASSERT_PRECONDITION(args.source.region.size.depth == args.destination.region.size.depth,
|
||||
"Blitting requires the source and destination regions to have the same depth.");
|
||||
|
||||
// Determine if the blit for color or depth are eligible to use a MTLBlitCommandEncoder.
|
||||
// blitFastPath returns true upon success.
|
||||
@@ -326,8 +327,7 @@ id<MTLFunction> MetalBlitter::compileFragmentFunction(BlitFunctionKey key) const
|
||||
utils::slog.e << description << utils::io::endl;
|
||||
}
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(library && function)
|
||||
<< "Unable to compile fragment shader for MetalBlitter.";
|
||||
ASSERT_POSTCONDITION(library && function, "Unable to compile fragment shader for MetalBlitter.");
|
||||
|
||||
return function;
|
||||
}
|
||||
@@ -352,8 +352,7 @@ id<MTLFunction> MetalBlitter::getBlitVertexFunction() {
|
||||
utils::slog.e << description << utils::io::endl;
|
||||
}
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(library && function)
|
||||
<< "Unable to compile vertex shader for MetalBlitter.";
|
||||
ASSERT_POSTCONDITION(library && function, "Unable to compile vertex shader for MetalBlitter.");
|
||||
|
||||
mVertexFunction = function;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,11 +45,10 @@ 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];
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(mBuffer)
|
||||
<< "Could not allocate Metal buffer of size " << size << ".";
|
||||
MetalBufferTracking::track(mBuffer, MetalBufferTracking::Type::GENERIC);
|
||||
ASSERT_POSTCONDITION(mBuffer, "Could not allocate Metal buffer of size %zu.", size);
|
||||
}
|
||||
|
||||
MetalBuffer::~MetalBuffer() {
|
||||
@@ -58,9 +61,9 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
|
||||
if (size <= 0) {
|
||||
return;
|
||||
}
|
||||
FILAMENT_CHECK_PRECONDITION(size + byteOffset <= mBufferSize)
|
||||
<< "Attempting to copy " << size << " bytes into a buffer of size " << mBufferSize
|
||||
<< " at offset " << byteOffset;
|
||||
ASSERT_PRECONDITION(size + byteOffset <= mBufferSize,
|
||||
"Attempting to copy %zu bytes into a buffer of size %zu at offset %zu",
|
||||
size, mBufferSize, byteOffset);
|
||||
|
||||
// Either copy into the Metal buffer or into our cpu buffer.
|
||||
if (mCpuBuffer) {
|
||||
@@ -71,18 +74,18 @@ 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.
|
||||
FILAMENT_CHECK_PRECONDITION(!(byteOffset & 0x3u)) << "byteOffset must be a multiple of 4";
|
||||
ASSERT_PRECONDITION(!(byteOffset & 0x3u), "byteOffset must be a multiple of 4");
|
||||
|
||||
// Encode a blit from the staging buffer into the private GPU buffer.
|
||||
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];
|
||||
@@ -103,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,
|
||||
|
||||
@@ -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,10 +48,10 @@ MetalBufferPoolEntry const* MetalBufferPool::acquireBuffer(size_t numBytes) {
|
||||
buffer = [mContext.device newBufferWithLength:numBytes
|
||||
options:MTLResourceStorageModeShared];
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(buffer)
|
||||
<< "Could not allocate Metal staging buffer of size " << numBytes << ".";
|
||||
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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -113,8 +113,7 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
|
||||
}
|
||||
}
|
||||
}];
|
||||
FILAMENT_CHECK_POSTCONDITION(context->pendingCommandBuffer)
|
||||
<< "Could not obtain command buffer.";
|
||||
ASSERT_POSTCONDITION(context->pendingCommandBuffer, "Could not obtain command buffer.");
|
||||
return context->pendingCommandBuffer;
|
||||
}
|
||||
|
||||
@@ -154,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
@@ -181,8 +181,7 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
|
||||
|
||||
CVReturn success = CVMetalTextureCacheCreate(kCFAllocatorDefault, nullptr, mContext->device,
|
||||
nullptr, &mContext->textureCache);
|
||||
FILAMENT_CHECK_POSTCONDITION(success == kCVReturnSuccess)
|
||||
<< "Could not create Metal texture cache.";
|
||||
ASSERT_POSTCONDITION(success == kCVReturnSuccess, "Could not create Metal texture cache.");
|
||||
|
||||
if (@available(iOS 12, *)) {
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);
|
||||
@@ -203,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;
|
||||
@@ -225,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,14 +292,14 @@ void MetalDriver::endFrame(uint32_t frameId) {
|
||||
}
|
||||
|
||||
void MetalDriver::flush(int) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "flush must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"flush must be called outside of a render pass.");
|
||||
submitPendingCommands(mContext);
|
||||
}
|
||||
|
||||
void MetalDriver::finish(int) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "finish must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"finish must be called outside of a render pass.");
|
||||
// Wait for all frames to finish by submitting and waiting on a dummy command buffer.
|
||||
submitPendingCommands(mContext);
|
||||
id<MTLCommandBuffer> oneOffBuffer = [mContext->commandQueue commandBuffer];
|
||||
@@ -358,19 +360,19 @@ void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
|
||||
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height,
|
||||
uint32_t depth, TextureUsage usage) {
|
||||
id<MTLTexture> metalTexture = (id<MTLTexture>) CFBridgingRelease((void*) i);
|
||||
FILAMENT_CHECK_PRECONDITION(metalTexture.width == width)
|
||||
<< "Imported id<MTLTexture> width (" << metalTexture.width
|
||||
<< ") != Filament texture width (" << width << ")";
|
||||
FILAMENT_CHECK_PRECONDITION(metalTexture.height == height)
|
||||
<< "Imported id<MTLTexture> height (" << metalTexture.height
|
||||
<< ") != Filament texture height (" << height << ")";
|
||||
FILAMENT_CHECK_PRECONDITION(metalTexture.mipmapLevelCount == levels)
|
||||
<< "Imported id<MTLTexture> levels (" << metalTexture.mipmapLevelCount
|
||||
<< ") != Filament texture levels (" << levels << ")";
|
||||
ASSERT_PRECONDITION(metalTexture.width == width,
|
||||
"Imported id<MTLTexture> width (%d) != Filament texture width (%d)",
|
||||
metalTexture.width, width);
|
||||
ASSERT_PRECONDITION(metalTexture.height == height,
|
||||
"Imported id<MTLTexture> height (%d) != Filament texture height (%d)",
|
||||
metalTexture.height, height);
|
||||
ASSERT_PRECONDITION(metalTexture.mipmapLevelCount == levels,
|
||||
"Imported id<MTLTexture> levels (%d) != Filament texture levels (%d)",
|
||||
metalTexture.mipmapLevelCount, levels);
|
||||
MTLTextureType filamentMetalType = getMetalType(target);
|
||||
FILAMENT_CHECK_PRECONDITION(metalTexture.textureType == filamentMetalType)
|
||||
<< "Imported id<MTLTexture> type (" << metalTexture.textureType
|
||||
<< ") != Filament texture type (" << filamentMetalType << ")";
|
||||
ASSERT_PRECONDITION(metalTexture.textureType == filamentMetalType,
|
||||
"Imported id<MTLTexture> type (%d) != Filament texture type (%d)",
|
||||
metalTexture.textureType, filamentMetalType);
|
||||
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
|
||||
target, levels, format, samples, width, height, depth, usage, metalTexture));
|
||||
}
|
||||
@@ -399,8 +401,8 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
|
||||
uint8_t samples, uint8_t layerCount, MRT color,
|
||||
TargetBufferInfo depth, TargetBufferInfo stencil) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "createRenderTarget must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"createRenderTarget must be called outside of a render pass.");
|
||||
// Clamp sample count to what the device supports.
|
||||
auto& sc = mContext->sampleCountLookup;
|
||||
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
|
||||
@@ -411,33 +413,33 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
continue;
|
||||
}
|
||||
const auto& buffer = color[i];
|
||||
FILAMENT_CHECK_PRECONDITION(buffer.handle)
|
||||
<< "The COLOR" << i << " flag was specified, but invalid color handle provided.";
|
||||
ASSERT_PRECONDITION(buffer.handle,
|
||||
"The COLOR%u flag was specified, but invalid color handle provided.", i);
|
||||
auto colorTexture = handle_cast<MetalTexture>(buffer.handle);
|
||||
FILAMENT_CHECK_PRECONDITION(colorTexture->getMtlTextureForWrite())
|
||||
<< "Color texture passed to render target has no texture allocation";
|
||||
ASSERT_PRECONDITION(colorTexture->getMtlTextureForWrite(),
|
||||
"Color texture passed to render target has no texture allocation");
|
||||
colorTexture->extendLodRangeTo(buffer.level);
|
||||
colorAttachments[i] = { colorTexture, color[i].level, color[i].layer };
|
||||
}
|
||||
|
||||
MetalRenderTarget::Attachment depthAttachment = {};
|
||||
if (any(targetBufferFlags & TargetBufferFlags::DEPTH)) {
|
||||
FILAMENT_CHECK_PRECONDITION(depth.handle)
|
||||
<< "The DEPTH flag was specified, but invalid depth handle provided.";
|
||||
ASSERT_PRECONDITION(depth.handle,
|
||||
"The DEPTH flag was specified, but invalid depth handle provided.");
|
||||
auto depthTexture = handle_cast<MetalTexture>(depth.handle);
|
||||
FILAMENT_CHECK_PRECONDITION(depthTexture->getMtlTextureForWrite())
|
||||
<< "Depth texture passed to render target has no texture allocation.";
|
||||
ASSERT_PRECONDITION(depthTexture->getMtlTextureForWrite(),
|
||||
"Depth texture passed to render target has no texture allocation.");
|
||||
depthTexture->extendLodRangeTo(depth.level);
|
||||
depthAttachment = { depthTexture, depth.level, depth.layer };
|
||||
}
|
||||
|
||||
MetalRenderTarget::Attachment stencilAttachment = {};
|
||||
if (any(targetBufferFlags & TargetBufferFlags::STENCIL)) {
|
||||
FILAMENT_CHECK_PRECONDITION(stencil.handle)
|
||||
<< "The STENCIL flag was specified, but invalid stencil handle provided.";
|
||||
ASSERT_PRECONDITION(stencil.handle,
|
||||
"The STENCIL flag was specified, but invalid stencil handle provided.");
|
||||
auto stencilTexture = handle_cast<MetalTexture>(stencil.handle);
|
||||
FILAMENT_CHECK_PRECONDITION(stencilTexture->getMtlTextureForWrite())
|
||||
<< "Stencil texture passed to render target has no texture allocation.";
|
||||
ASSERT_PRECONDITION(stencilTexture->getMtlTextureForWrite(),
|
||||
"Stencil texture passed to render target has no texture allocation.");
|
||||
stencilTexture->extendLodRangeTo(stencil.level);
|
||||
stencilAttachment = { stencilTexture, stencil.level, stencil.layer };
|
||||
}
|
||||
@@ -801,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,8 +882,8 @@ void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&
|
||||
|
||||
void MetalDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& data,
|
||||
uint32_t byteOffset) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "updateBufferObject must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"updateBufferObject must be called outside of a render pass.");
|
||||
auto* bo = handle_cast<MetalBufferObject>(boh);
|
||||
bo->updateBuffer(data.buffer, data.size, byteOffset);
|
||||
scheduleDestroy(std::move(data));
|
||||
@@ -922,8 +922,8 @@ void MetalDriver::update3DImage(Handle<HwTexture> th, uint32_t level,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
|
||||
uint32_t width, uint32_t height, uint32_t depth,
|
||||
PixelBufferDescriptor&& data) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "update3DImage must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"update3DImage must be called outside of a render pass.");
|
||||
auto tex = handle_cast<MetalTexture>(th);
|
||||
tex->loadImage(level, MTLRegionMake3D(xoffset, yoffset, zoffset, width, height, depth), data);
|
||||
scheduleDestroy(std::move(data));
|
||||
@@ -939,15 +939,15 @@ void MetalDriver::setupExternalImage(void* image) {
|
||||
}
|
||||
|
||||
void MetalDriver::setExternalImage(Handle<HwTexture> th, void* image) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "setExternalImage must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"setExternalImage must be called outside of a render pass.");
|
||||
auto texture = handle_cast<MetalTexture>(th);
|
||||
texture->externalImage.set((CVPixelBufferRef) image);
|
||||
}
|
||||
|
||||
void MetalDriver::setExternalImagePlane(Handle<HwTexture> th, void* image, uint32_t plane) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "setExternalImagePlane must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"setExternalImagePlane must be called outside of a render pass.");
|
||||
auto texture = handle_cast<MetalTexture>(th);
|
||||
texture->externalImage.set((CVPixelBufferRef) image, plane);
|
||||
}
|
||||
@@ -962,15 +962,15 @@ TimerQueryResult MetalDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint6
|
||||
}
|
||||
|
||||
void MetalDriver::generateMipmaps(Handle<HwTexture> th) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "generateMipmaps must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"generateMipmaps must be called outside of a render pass.");
|
||||
auto tex = handle_cast<MetalTexture>(th);
|
||||
tex->generateMipmaps();
|
||||
}
|
||||
|
||||
void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescriptor&& data) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "updateSamplerGroup must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"updateSamplerGroup must be called outside of a render pass.");
|
||||
|
||||
auto sb = handle_cast<MetalSamplerGroup>(sbh);
|
||||
assert_invariant(sb->size == data.size / sizeof(SamplerDescriptor));
|
||||
@@ -1109,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) {}
|
||||
@@ -1261,14 +1257,7 @@ void MetalDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
|
||||
}
|
||||
|
||||
void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
backend::PushConstantVariant value) {
|
||||
FILAMENT_CHECK_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) {
|
||||
|
||||
@@ -1320,8 +1309,8 @@ void MetalDriver::stopCapture(int) {
|
||||
|
||||
void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y, uint32_t width,
|
||||
uint32_t height, PixelBufferDescriptor&& data) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "readPixels must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"readPixels must be called outside of a render pass.");
|
||||
|
||||
auto srcTarget = handle_cast<MetalRenderTarget>(src);
|
||||
// We always readPixels from the COLOR0 attachment.
|
||||
@@ -1335,19 +1324,17 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
|
||||
width = std::min(static_cast<uint32_t>(srcTextureSize.width), width);
|
||||
|
||||
const MTLPixelFormat format = getMetalFormat(data.format, data.type);
|
||||
FILAMENT_CHECK_PRECONDITION(format != MTLPixelFormatInvalid)
|
||||
<< "The chosen combination of PixelDataFormat (" << (int)data.format
|
||||
<< ") and PixelDataType (" << (int)data.type
|
||||
<< ") is not supported for "
|
||||
"readPixels.";
|
||||
ASSERT_PRECONDITION(format != MTLPixelFormatInvalid,
|
||||
"The chosen combination of PixelDataFormat (%d) and PixelDataType (%d) is not supported for "
|
||||
"readPixels.", (int) data.format, (int) data.type);
|
||||
|
||||
const bool formatConversionNecessary = srcTexture.pixelFormat != format;
|
||||
|
||||
// TODO: MetalBlitter does not currently support format conversions to integer types.
|
||||
// The format and type must match the source pixel format exactly.
|
||||
FILAMENT_CHECK_PRECONDITION(!formatConversionNecessary || !isMetalFormatInteger(format))
|
||||
<< "readPixels does not support integer format conversions from MTLPixelFormat ("
|
||||
<< (int)srcTexture.pixelFormat << ") to (" << (int)format << ").";
|
||||
ASSERT_PRECONDITION(!formatConversionNecessary || !isMetalFormatInteger(format),
|
||||
"readPixels does not support integer format conversions from MTLPixelFormat (%d) to (%d).",
|
||||
(int) srcTexture.pixelFormat, (int) format);
|
||||
|
||||
MTLTextureDescriptor* textureDescriptor =
|
||||
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
|
||||
@@ -1413,31 +1400,31 @@ void MetalDriver::resolve(
|
||||
assert_invariant(srcTexture);
|
||||
assert_invariant(dstTexture);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder == nil)
|
||||
<< "resolve() cannot be invoked inside a render pass.";
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
|
||||
"resolve() cannot be invoked inside a render pass.");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
dstTexture->width == srcTexture->width && dstTexture->height == srcTexture->height)
|
||||
<< "invalid resolve: src and dst sizes don't match";
|
||||
ASSERT_PRECONDITION(
|
||||
dstTexture->width == srcTexture->width && dstTexture->height == srcTexture->height,
|
||||
"invalid resolve: src and dst sizes don't match");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(srcTexture->samples > 1 && dstTexture->samples == 1)
|
||||
<< "invalid resolve: src.samples=" << +srcTexture->samples
|
||||
<< ", dst.samples=" << +dstTexture->samples;
|
||||
ASSERT_PRECONDITION(srcTexture->samples > 1 && dstTexture->samples == 1,
|
||||
"invalid resolve: src.samples=%u, dst.samples=%u",
|
||||
+srcTexture->samples, +dstTexture->samples);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(srcTexture->format == dstTexture->format)
|
||||
<< "src and dst texture format don't match";
|
||||
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!isDepthFormat(srcTexture->format))
|
||||
<< "can't resolve depth formats";
|
||||
ASSERT_PRECONDITION(!isDepthFormat(srcTexture->format),
|
||||
"can't resolve depth formats");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!isStencilFormat(srcTexture->format))
|
||||
<< "can't resolve stencil formats";
|
||||
ASSERT_PRECONDITION(!isStencilFormat(srcTexture->format),
|
||||
"can't resolve stencil formats");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST))
|
||||
<< "texture doesn't have BLIT_DST";
|
||||
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC))
|
||||
<< "texture doesn't have BLIT_SRC";
|
||||
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
// FIXME: on metal the blit() call below always take the slow path (using a shader)
|
||||
|
||||
@@ -1462,22 +1449,21 @@ void MetalDriver::blit(
|
||||
assert_invariant(srcTexture);
|
||||
assert_invariant(dstTexture);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder == nil)
|
||||
<< "blit() cannot be invoked inside a render pass.";
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
|
||||
"blit() cannot be invoked inside a render pass.");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST))
|
||||
<< "texture doesn't have BLIT_DST";
|
||||
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC))
|
||||
<< "texture doesn't have BLIT_SRC";
|
||||
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(srcTexture->format == dstTexture->format)
|
||||
<< "src and dst texture format don't match";
|
||||
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
isBlitableTextureType(srcTexture->getMtlTextureForRead().textureType) &&
|
||||
isBlitableTextureType(dstTexture->getMtlTextureForWrite().textureType))
|
||||
<< "Metal does not support blitting to/from non-2D textures.";
|
||||
ASSERT_PRECONDITION(isBlitableTextureType(srcTexture->getMtlTextureForRead().textureType) &&
|
||||
isBlitableTextureType(dstTexture->getMtlTextureForWrite().textureType),
|
||||
"Metal does not support blitting to/from non-2D textures.");
|
||||
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = SamplerMagFilter::NEAREST;
|
||||
@@ -1515,18 +1501,18 @@ void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
// It is called between beginFrame and endFrame, but should never be called in the middle of
|
||||
// a render pass.
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder == nil)
|
||||
<< "blitDEPRECATED() cannot be invoked inside a render pass.";
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
|
||||
"blitDEPRECATED() cannot be invoked inside a render pass.");
|
||||
|
||||
auto srcTarget = handle_cast<MetalRenderTarget>(src);
|
||||
auto dstTarget = handle_cast<MetalRenderTarget>(dst);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(buffers == TargetBufferFlags::COLOR0)
|
||||
<< "blitDEPRECATED only supports COLOR0";
|
||||
ASSERT_PRECONDITION(buffers == TargetBufferFlags::COLOR0,
|
||||
"blitDEPRECATED only supports COLOR0");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
srcRect.left >= 0 && srcRect.bottom >= 0 && dstRect.left >= 0 && dstRect.bottom >= 0)
|
||||
<< "Source and destination rects must be positive.";
|
||||
ASSERT_PRECONDITION(srcRect.left >= 0 && srcRect.bottom >= 0 &&
|
||||
dstRect.left >= 0 && dstRect.bottom >= 0,
|
||||
"Source and destination rects must be positive.");
|
||||
|
||||
auto isBlitableTextureType = [](MTLTextureType t) {
|
||||
return t == MTLTextureType2D || t == MTLTextureType2DMultisample ||
|
||||
@@ -1538,10 +1524,9 @@ void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
MetalRenderTarget::Attachment const dstColorAttachment = dstTarget->getDrawColorAttachment(0);
|
||||
|
||||
if (srcColorAttachment && dstColorAttachment) {
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
isBlitableTextureType(srcColorAttachment.getTexture().textureType) &&
|
||||
isBlitableTextureType(dstColorAttachment.getTexture().textureType))
|
||||
<< "Metal does not support blitting to/from non-2D textures.";
|
||||
ASSERT_PRECONDITION(isBlitableTextureType(srcColorAttachment.getTexture().textureType) &&
|
||||
isBlitableTextureType(dstColorAttachment.getTexture().textureType),
|
||||
"Metal does not support blitting to/from non-2D textures.");
|
||||
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = filter;
|
||||
@@ -1653,8 +1638,8 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
|
||||
}
|
||||
|
||||
void MetalDriver::bindPipeline(PipelineState const& ps) {
|
||||
FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr)
|
||||
<< "bindPipeline() without a valid command encoder.";
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
|
||||
"bindPipeline() without a valid command encoder.");
|
||||
|
||||
MetalVertexBufferInfo const* const vbi =
|
||||
handle_cast<MetalVertexBufferInfo>(ps.vertexBufferInfo);
|
||||
@@ -1826,8 +1811,8 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
|
||||
}
|
||||
|
||||
void MetalDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr)
|
||||
<< "bindRenderPrimitive() without a valid command encoder.";
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
|
||||
"bindRenderPrimitive() without a valid command encoder.");
|
||||
|
||||
// Bind the user vertex buffers.
|
||||
MetalBuffer* vertexBuffers[MAX_VERTEX_BUFFER_COUNT] = {};
|
||||
@@ -1863,8 +1848,8 @@ void MetalDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
}
|
||||
|
||||
void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr)
|
||||
<< "draw() without a valid command encoder.";
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
|
||||
"draw() without a valid command encoder.");
|
||||
|
||||
// Bind uniform buffers.
|
||||
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
|
||||
@@ -1880,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;
|
||||
@@ -1913,8 +1890,8 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
|
||||
}
|
||||
|
||||
void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "dispatchCompute must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"dispatchCompute must be called outside of a render pass.");
|
||||
|
||||
auto mtlProgram = handle_cast<MetalProgram>(program);
|
||||
|
||||
@@ -2014,15 +1991,15 @@ void MetalDriver::scissor(Viewport scissorBox) {
|
||||
}
|
||||
|
||||
void MetalDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "beginTimerQuery must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"beginTimerQuery must be called outside of a render pass.");
|
||||
auto* tq = handle_cast<MetalTimerQuery>(tqh);
|
||||
mContext->timerQueryImpl->beginTimeElapsedQuery(tq);
|
||||
}
|
||||
|
||||
void MetalDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "endTimerQuery must be called outside of a render pass.";
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"endTimerQuery must be called outside of a render pass.");
|
||||
auto* tq = handle_cast<MetalTimerQuery>(tqh);
|
||||
mContext->timerQueryImpl->endTimeElapsedQuery(tq);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ constexpr inline MTLIndexType getIndexType(size_t elementSize) noexcept {
|
||||
} else if (elementSize == 4) {
|
||||
return MTLIndexTypeUInt32;
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(false) << "Index element size not supported.";
|
||||
ASSERT_POSTCONDITION(false, "Index element size not supported.");
|
||||
}
|
||||
|
||||
constexpr inline MTLVertexFormat getMetalFormat(ElementType type, bool normalized) noexcept {
|
||||
@@ -100,7 +100,7 @@ constexpr inline MTLVertexFormat getMetalFormat(ElementType type, bool normalize
|
||||
case ElementType::SHORT4: return MTLVertexFormatShort4Normalized;
|
||||
case ElementType::USHORT4: return MTLVertexFormatUShort4Normalized;
|
||||
default:
|
||||
FILAMENT_CHECK_POSTCONDITION(false) << "Normalized format does not exist.";
|
||||
ASSERT_POSTCONDITION(false, "Normalized format does not exist.");
|
||||
return MTLVertexFormatInvalid;
|
||||
}
|
||||
}
|
||||
@@ -326,8 +326,7 @@ constexpr inline MTLCullMode getMetalCullMode(CullingMode cullMode) noexcept {
|
||||
case CullingMode::FRONT: return MTLCullModeFront;
|
||||
case CullingMode::BACK: return MTLCullModeBack;
|
||||
case CullingMode::FRONT_AND_BACK:
|
||||
FILAMENT_CHECK_POSTCONDITION(false)
|
||||
<< "FRONT_AND_BACK culling is not supported in Metal.";
|
||||
ASSERT_POSTCONDITION(false, "FRONT_AND_BACK culling is not supported in Metal.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding]; \
|
||||
utils::slog.e << description << utils::io::endl; \
|
||||
} \
|
||||
FILAMENT_CHECK_POSTCONDITION(error == nil) << message;
|
||||
ASSERT_POSTCONDITION(error == nil, message);
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
@@ -86,14 +86,14 @@ void MetalExternalImage::set(CVPixelBufferRef image) noexcept {
|
||||
}
|
||||
|
||||
OSType formatType = CVPixelBufferGetPixelFormatType(image);
|
||||
FILAMENT_CHECK_POSTCONDITION(formatType == kCVPixelFormatType_32BGRA ||
|
||||
formatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)
|
||||
<< "Metal external images must be in either 32BGRA or 420f format.";
|
||||
ASSERT_POSTCONDITION(formatType == kCVPixelFormatType_32BGRA ||
|
||||
formatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
|
||||
"Metal external images must be in either 32BGRA or 420f format.");
|
||||
|
||||
size_t planeCount = CVPixelBufferGetPlaneCount(image);
|
||||
FILAMENT_CHECK_POSTCONDITION(planeCount == 0 || planeCount == 2)
|
||||
<< "The Metal backend does not support images with plane counts of " << planeCount
|
||||
<< ".";
|
||||
ASSERT_POSTCONDITION(planeCount == 0 || planeCount == 2,
|
||||
"The Metal backend does not support images with plane counts of %d.", planeCount);
|
||||
|
||||
|
||||
if (planeCount == 0) {
|
||||
mImage = image;
|
||||
@@ -138,8 +138,8 @@ void MetalExternalImage::set(CVPixelBufferRef image, size_t plane) noexcept {
|
||||
}
|
||||
|
||||
const OSType formatType = CVPixelBufferGetPixelFormatType(image);
|
||||
FILAMENT_CHECK_POSTCONDITION(formatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)
|
||||
<< "Metal planar external images must be in the 420f format.";
|
||||
ASSERT_POSTCONDITION(formatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
|
||||
"Metal planar external images must be in the 420f format.");
|
||||
|
||||
mImage = image;
|
||||
|
||||
@@ -191,8 +191,8 @@ CVMetalTextureRef MetalExternalImage::createTextureFromImage(CVPixelBufferRef im
|
||||
CVMetalTextureRef texture;
|
||||
CVReturn result = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
|
||||
mContext.textureCache, image, nullptr, format, width, height, plane, &texture);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == kCVReturnSuccess)
|
||||
<< "Could not create a CVMetalTexture from CVPixelBuffer.";
|
||||
ASSERT_POSTCONDITION(result == kCVReturnSuccess,
|
||||
"Could not create a CVMetalTexture from CVPixelBuffer.");
|
||||
|
||||
return texture;
|
||||
}
|
||||
@@ -203,8 +203,8 @@ void MetalExternalImage::shutdown(MetalContext& context) noexcept {
|
||||
|
||||
void MetalExternalImage::assertWritableImage(CVPixelBufferRef image) {
|
||||
OSType formatType = CVPixelBufferGetPixelFormatType(image);
|
||||
FILAMENT_CHECK_PRECONDITION(formatType == kCVPixelFormatType_32BGRA)
|
||||
<< "Metal SwapChain images must be in the 32BGRA format.";
|
||||
ASSERT_PRECONDITION(formatType == kCVPixelFormatType_32BGRA,
|
||||
"Metal SwapChain images must be in the 32BGRA format.");
|
||||
}
|
||||
|
||||
void MetalExternalImage::unset() {
|
||||
|
||||
@@ -176,7 +176,7 @@ id<MTLTexture> MetalSwapChain::acquireDrawable() {
|
||||
assert_invariant(isCaMetalLayer());
|
||||
drawable = [layer nextDrawable];
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(drawable != nil) << "Could not obtain drawable.";
|
||||
ASSERT_POSTCONDITION(drawable != nil, "Could not obtain drawable.");
|
||||
return drawable.texture;
|
||||
}
|
||||
|
||||
@@ -493,16 +493,15 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
externalImage(context, r, g, b, a) {
|
||||
|
||||
devicePixelFormat = decidePixelFormat(&context, format);
|
||||
FILAMENT_CHECK_POSTCONDITION(devicePixelFormat != MTLPixelFormatInvalid)
|
||||
<< "Texture format not supported.";
|
||||
ASSERT_POSTCONDITION(devicePixelFormat != MTLPixelFormatInvalid, "Texture format not supported.");
|
||||
|
||||
const BOOL mipmapped = levels > 1;
|
||||
const BOOL multisampled = samples > 1;
|
||||
|
||||
#if defined(IOS)
|
||||
const BOOL textureArray = target == SamplerType::SAMPLER_2D_ARRAY;
|
||||
FILAMENT_CHECK_PRECONDITION(!textureArray || !multisampled)
|
||||
<< "iOS does not support multisampled texture arrays.";
|
||||
ASSERT_PRECONDITION(!textureArray || !multisampled,
|
||||
"iOS does not support multisampled texture arrays.");
|
||||
#endif
|
||||
|
||||
const auto get2DTextureType = [](SamplerType target, bool isMultisampled) {
|
||||
@@ -537,14 +536,12 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
descriptor.usage = getMetalTextureUsage(usage);
|
||||
descriptor.storageMode = MTLStorageModePrivate;
|
||||
texture = [context.device newTextureWithDescriptor:descriptor];
|
||||
FILAMENT_CHECK_POSTCONDITION(texture != nil)
|
||||
<< "Could not create Metal texture. Out of memory?";
|
||||
ASSERT_POSTCONDITION(texture != nil, "Could not create Metal texture. Out of memory?");
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
FILAMENT_CHECK_POSTCONDITION(!multisampled)
|
||||
<< "Multisampled cubemap faces not supported.";
|
||||
FILAMENT_CHECK_POSTCONDITION(width == height) << "Cubemap faces must be square.";
|
||||
ASSERT_POSTCONDITION(!multisampled, "Multisampled cubemap faces not supported.");
|
||||
ASSERT_POSTCONDITION(width == height, "Cubemap faces must be square.");
|
||||
descriptor = [MTLTextureDescriptor textureCubeDescriptorWithPixelFormat:devicePixelFormat
|
||||
size:width
|
||||
mipmapped:mipmapped];
|
||||
@@ -553,8 +550,7 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
descriptor.usage = getMetalTextureUsage(usage);
|
||||
descriptor.storageMode = MTLStorageModePrivate;
|
||||
texture = [context.device newTextureWithDescriptor:descriptor];
|
||||
FILAMENT_CHECK_POSTCONDITION(texture != nil)
|
||||
<< "Could not create Metal texture. Out of memory?";
|
||||
ASSERT_POSTCONDITION(texture != nil, "Could not create Metal texture. Out of memory?");
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
descriptor = [MTLTextureDescriptor new];
|
||||
@@ -567,8 +563,7 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
descriptor.usage = getMetalTextureUsage(usage);
|
||||
descriptor.storageMode = MTLStorageModePrivate;
|
||||
texture = [context.device newTextureWithDescriptor:descriptor];
|
||||
FILAMENT_CHECK_POSTCONDITION(texture != nil)
|
||||
<< "Could not create Metal texture. Out of memory?";
|
||||
ASSERT_POSTCONDITION(texture != nil, "Could not create Metal texture. Out of memory?");
|
||||
break;
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
// If we're using external textures (CVPixelBufferRefs), we don't need to make any
|
||||
@@ -770,9 +765,9 @@ void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffs
|
||||
PixelBufferDescriptor const& data) noexcept {
|
||||
const PixelBufferShape shape = PixelBufferShape::compute(data, format, region.size, byteOffset);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(data.size >= shape.totalBytes)
|
||||
<< "Expected buffer size of at least " << shape.totalBytes
|
||||
<< " but received PixelBufferDescriptor with size " << data.size << ".";
|
||||
ASSERT_PRECONDITION(data.size >= shape.totalBytes,
|
||||
"Expected buffer size of at least %d but "
|
||||
"received PixelBufferDescriptor with size %d.", shape.totalBytes, data.size);
|
||||
|
||||
// Earlier versions of iOS don't have the maxBufferLength query, but 256 MB is a safe bet.
|
||||
NSUInteger deviceMaxBufferLength = 256 * 1024 * 1024; // 256 MB
|
||||
@@ -805,13 +800,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
|
||||
@@ -993,9 +988,9 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
}
|
||||
color[i] = colorAttachments[i];
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(color[i].getSampleCount() <= samples)
|
||||
<< "MetalRenderTarget was initialized with a MSAA COLOR" << i
|
||||
<< " texture, but sample count is " << samples << ".";
|
||||
ASSERT_PRECONDITION(color[i].getSampleCount() <= samples,
|
||||
"MetalRenderTarget was initialized with a MSAA COLOR%d texture, but sample count is %d.",
|
||||
i, samples);
|
||||
|
||||
auto t = color[i].metalTexture;
|
||||
const auto twidth = std::max(1u, t->width >> color[i].level);
|
||||
@@ -1018,10 +1013,9 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
if (depthAttachment) {
|
||||
depth = depthAttachment;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(depth.getSampleCount() <= samples)
|
||||
<< "MetalRenderTarget was initialized with a MSAA DEPTH texture, but sample count "
|
||||
"is "
|
||||
<< samples << ".";
|
||||
ASSERT_PRECONDITION(depth.getSampleCount() <= samples,
|
||||
"MetalRenderTarget was initialized with a MSAA DEPTH texture, but sample count is %d.",
|
||||
samples);
|
||||
|
||||
auto t = depth.metalTexture;
|
||||
const auto twidth = std::max(1u, t->width >> depth.level);
|
||||
@@ -1044,10 +1038,9 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
if (stencilAttachment) {
|
||||
stencil = stencilAttachment;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(stencil.getSampleCount() <= samples)
|
||||
<< "MetalRenderTarget was initialized with a MSAA STENCIL texture, but sample "
|
||||
"count is "
|
||||
<< samples << ".";
|
||||
ASSERT_PRECONDITION(stencil.getSampleCount() <= samples,
|
||||
"MetalRenderTarget was initialized with a MSAA STENCIL texture, but sample count is %d.",
|
||||
samples);
|
||||
|
||||
auto t = stencil.metalTexture;
|
||||
const auto twidth = std::max(1u, t->width >> stencil.level);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -90,17 +90,11 @@ 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];
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(error == nil) << "Could not create Metal pipeline state.";
|
||||
ASSERT_POSTCONDITION(error == nil, "Could not create Metal pipeline state.");
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ bool NoopDriver::isProtectedContentSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isStereoSupported() {
|
||||
bool NoopDriver::isStereoSupported(backend::StereoscopicType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -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{{
|
||||
|
||||
@@ -1494,8 +1494,9 @@ void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
// note: in practice this should never happen on Android
|
||||
FILAMENT_CHECK_POSTCONDITION(sc->swapChain) << "createSwapChain(" << nativeWindow << ", "
|
||||
<< flags << ") failed. See logs for details.";
|
||||
ASSERT_POSTCONDITION(sc->swapChain,
|
||||
"createSwapChain(%p, 0x%lx) failed. See logs for details.",
|
||||
nativeWindow, flags);
|
||||
#endif
|
||||
|
||||
// See if we need the emulated rec709 output conversion
|
||||
@@ -1514,9 +1515,9 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
// note: in practice this should never happen on Android
|
||||
FILAMENT_CHECK_POSTCONDITION(sc->swapChain)
|
||||
<< "createSwapChainHeadless(" << width << ", " << height << ", " << flags
|
||||
<< ") failed. See logs for details.";
|
||||
ASSERT_POSTCONDITION(sc->swapChain,
|
||||
"createSwapChainHeadless(%u, %u, 0x%lx) failed. See logs for details.",
|
||||
width, height, flags);
|
||||
#endif
|
||||
|
||||
// See if we need the emulated rec709 output conversion
|
||||
@@ -2049,19 +2050,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3605,11 +3606,13 @@ void OpenGLDriver::resolve(
|
||||
assert_invariant(s);
|
||||
assert_invariant(d);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(d->width == s->width && d->height == s->height)
|
||||
<< "invalid resolve: src and dst sizes don't match";
|
||||
ASSERT_PRECONDITION(
|
||||
d->width == s->width && d->height == s->height,
|
||||
"invalid resolve: src and dst sizes don't match");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(s->samples > 1 && d->samples == 1)
|
||||
<< "invalid resolve: src.samples=" << +s->samples << ", dst.samples=" << +d->samples;
|
||||
ASSERT_PRECONDITION(s->samples > 1 && d->samples == 1,
|
||||
"invalid resolve: src.samples=%u, dst.samples=%u",
|
||||
+s->samples, +d->samples);
|
||||
|
||||
blit( dst, dstLevel, dstLayer, {},
|
||||
src, srcLevel, srcLayer, {},
|
||||
@@ -3765,12 +3768,12 @@ void OpenGLDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
UTILS_UNUSED_IN_RELEASE auto& gl = mContext;
|
||||
assert_invariant(!gl.isES2());
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(buffers == TargetBufferFlags::COLOR0)
|
||||
<< "blitDEPRECATED only supports COLOR0";
|
||||
ASSERT_PRECONDITION(buffers == TargetBufferFlags::COLOR0,
|
||||
"blitDEPRECATED only supports COLOR0");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
srcRect.left >= 0 && srcRect.bottom >= 0 && dstRect.left >= 0 && dstRect.bottom >= 0)
|
||||
<< "Source and destination rects must be positive.";
|
||||
ASSERT_PRECONDITION(srcRect.left >= 0 && srcRect.bottom >= 0 &&
|
||||
dstRect.left >= 0 && dstRect.bottom >= 0,
|
||||
"Source and destination rects must be positive.");
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
|
||||
|
||||
@@ -111,8 +111,8 @@ bool CocoaExternalImage::set(CVPixelBufferRef image) noexcept {
|
||||
}
|
||||
|
||||
OSType formatType = CVPixelBufferGetPixelFormatType(image);
|
||||
FILAMENT_CHECK_POSTCONDITION(formatType == kCVPixelFormatType_32BGRA)
|
||||
<< "macOS external images must be 32BGRA format.";
|
||||
ASSERT_POSTCONDITION(formatType == kCVPixelFormatType_32BGRA,
|
||||
"macOS external images must be 32BGRA format.");
|
||||
|
||||
// The pixel buffer must be locked whenever we do rendering with it. We'll unlock it before
|
||||
// releasing.
|
||||
|
||||
@@ -135,14 +135,13 @@ bool CocoaTouchExternalImage::set(CVPixelBufferRef image) noexcept {
|
||||
}
|
||||
|
||||
OSType formatType = CVPixelBufferGetPixelFormatType(image);
|
||||
FILAMENT_CHECK_POSTCONDITION(formatType == kCVPixelFormatType_32BGRA ||
|
||||
formatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)
|
||||
<< "iOS external images must be in either 32BGRA or 420f format.";
|
||||
ASSERT_POSTCONDITION(formatType == kCVPixelFormatType_32BGRA ||
|
||||
formatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
|
||||
"iOS external images must be in either 32BGRA or 420f format.");
|
||||
|
||||
size_t planeCount = CVPixelBufferGetPlaneCount(image);
|
||||
FILAMENT_CHECK_POSTCONDITION(planeCount == 0 || planeCount == 2)
|
||||
<< "The OpenGL backend does not support images with plane counts of " << planeCount
|
||||
<< ".";
|
||||
ASSERT_POSTCONDITION(planeCount == 0 || planeCount == 2,
|
||||
"The OpenGL backend does not support images with plane counts of %d.", planeCount);
|
||||
|
||||
// The pixel buffer must be locked whenever we do rendering with it. We'll unlock it before
|
||||
// releasing.
|
||||
|
||||
@@ -162,7 +162,7 @@ Driver* PlatformCocoaGL::createDriver(void* sharedContext, const Platform::Drive
|
||||
pImpl->mGLContext = nsOpenGLContext;
|
||||
|
||||
int result = bluegl::bind();
|
||||
FILAMENT_CHECK_POSTCONDITION(!result) << "Unable to load OpenGL entry points.";
|
||||
ASSERT_POSTCONDITION(!result, "Unable to load OpenGL entry points.");
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE CVReturn success = CVOpenGLTextureCacheCreate(kCFAllocatorDefault, nullptr,
|
||||
[pImpl->mGLContext CGLContextObj], [pImpl->mGLContext.pixelFormat CGLPixelFormatObj], nullptr,
|
||||
|
||||
@@ -61,7 +61,7 @@ Driver* PlatformCocoaTouchGL::createDriver(void* const sharedGLContext, const Pl
|
||||
EAGLSharegroup* sharegroup = (__bridge EAGLSharegroup*) sharedGLContext;
|
||||
|
||||
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 sharegroup:sharegroup];
|
||||
FILAMENT_CHECK_POSTCONDITION(context) << "Unable to create OpenGL ES context.";
|
||||
ASSERT_POSTCONDITION(context, "Unable to create OpenGL ES context.");
|
||||
|
||||
[EAGLContext setCurrentContext:context];
|
||||
|
||||
@@ -103,7 +103,7 @@ void PlatformCocoaTouchGL::createContext(bool shared) {
|
||||
EAGLContext* const context = [[EAGLContext alloc]
|
||||
initWithAPI:kEAGLRenderingAPIOpenGLES3
|
||||
sharegroup:sharegroup];
|
||||
FILAMENT_CHECK_POSTCONDITION(context) << "Unable to create extra OpenGL ES context.";
|
||||
ASSERT_POSTCONDITION(context, "Unable to create extra OpenGL ES context.");
|
||||
[EAGLContext setCurrentContext:context];
|
||||
pImpl->mAdditionalContexts.push_back(context);
|
||||
}
|
||||
@@ -180,8 +180,7 @@ bool PlatformCocoaTouchGL::makeCurrent(ContextType type, SwapChain* drawSwapChai
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFramebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, pImpl->mDefaultFramebuffer);
|
||||
GLenum const status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
FILAMENT_CHECK_POSTCONDITION(status == GL_FRAMEBUFFER_COMPLETE)
|
||||
<< "Incomplete framebuffer.";
|
||||
ASSERT_POSTCONDITION(status == GL_FRAMEBUFFER_COMPLETE, "Incomplete framebuffer.");
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, oldFramebuffer);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -226,7 +226,7 @@ Driver* PlatformGLX::createDriver(void* const sharedGLContext,
|
||||
g_glx.setCurrentContext(mGLXDisplay, mDummySurface, mDummySurface, mGLXContext);
|
||||
|
||||
int result = bluegl::bind();
|
||||
FILAMENT_CHECK_POSTCONDITION(!result) << "Unable to load OpenGL entry points.";
|
||||
ASSERT_POSTCONDITION(!result, "Unable to load OpenGL entry points.");
|
||||
|
||||
return OpenGLPlatform::createDefaultDriver(this, sharedGLContext, driverConfig);
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ Driver* PlatformWGL::createDriver(void* const sharedGLContext,
|
||||
}
|
||||
|
||||
result = bluegl::bind();
|
||||
FILAMENT_CHECK_POSTCONDITION(!result) << "Unable to load OpenGL entry points.";
|
||||
ASSERT_POSTCONDITION(!result, "Unable to load OpenGL entry points.");
|
||||
|
||||
return OpenGLPlatform::createDefaultDriver(this, sharedGLContext, driverConfig);
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ VulkanTimestamps::VulkanTimestamps(VkDevice device) : mDevice(device) {
|
||||
std::unique_lock<utils::Mutex> lock(mMutex);
|
||||
tqpCreateInfo.queryCount = mUsed.size() * 2;
|
||||
VkResult result = vkCreateQueryPool(mDevice, &tqpCreateInfo, VKALLOC, &mPool);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateQueryPool error.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateQueryPool error.");
|
||||
mUsed.reset();
|
||||
}
|
||||
|
||||
@@ -134,8 +134,8 @@ VulkanTimestamps::QueryResult VulkanTimestamps::getResult(VulkanTimerQuery const
|
||||
VkResult vkresult =
|
||||
vkGetQueryPoolResults(mDevice, mPool, index, 2, dataSize, (void*) result.data(),
|
||||
stride, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
|
||||
FILAMENT_CHECK_POSTCONDITION(vkresult == VK_SUCCESS || vkresult == VK_NOT_READY)
|
||||
<< "vkGetQueryPoolResults error: " << static_cast<int32_t>(vkresult);
|
||||
ASSERT_POSTCONDITION(vkresult == VK_SUCCESS || vkresult == VK_NOT_READY,
|
||||
"vkGetQueryPoolResults error: %d", static_cast<int32_t>(vkresult));
|
||||
if (vkresult == VK_NOT_READY) {
|
||||
return {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
@@ -173,8 +173,7 @@ DebugUtils::DebugUtils(VkInstance instance, VkDevice device, VulkanContext const
|
||||
};
|
||||
VkResult result = vkCreateDebugUtilsMessengerEXT(instance, &createInfo,
|
||||
VKALLOC, &mDebugMessenger);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create Vulkan debug messenger.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug messenger.");
|
||||
}
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
|
||||
}
|
||||
@@ -229,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 =
|
||||
@@ -248,8 +246,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
||||
};
|
||||
VkResult result = createDebugReportCallback(mPlatform->getInstance(), &cbinfo, VKALLOC,
|
||||
&mDebugCallback);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create Vulkan debug callback.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug callback.");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -902,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1092,8 +1088,7 @@ TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint
|
||||
return TimerQueryResult::NOT_READY;
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(timestamp1 >= timestamp0)
|
||||
<< "Timestamps are not monotonically increasing.";
|
||||
ASSERT_POSTCONDITION(timestamp1 >= timestamp0, "Timestamps are not monotonically increasing.");
|
||||
|
||||
// NOTE: MoltenVK currently writes system time so the following delta will always be zero.
|
||||
// However there are plans for implementing this properly. See the following GitHub ticket.
|
||||
@@ -1498,8 +1493,8 @@ void VulkanDriver::endRenderPass(int) {
|
||||
}
|
||||
|
||||
void VulkanDriver::nextSubpass(int) {
|
||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.currentSubpass == 0)
|
||||
<< "Only two subpasses are currently supported.";
|
||||
ASSERT_PRECONDITION(mCurrentRenderPass.currentSubpass == 0,
|
||||
"Only two subpasses are currently supported.");
|
||||
|
||||
VulkanRenderTarget* renderTarget = mCurrentRenderPass.renderTarget;
|
||||
assert_invariant(renderTarget);
|
||||
@@ -1639,36 +1634,36 @@ void VulkanDriver::resolve(
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("resolve");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
|
||||
<< "resolve() cannot be invoked inside a render pass.";
|
||||
ASSERT_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE,
|
||||
"resolve() cannot be invoked inside a render pass.");
|
||||
|
||||
auto* const srcTexture = mResourceAllocator.handle_cast<VulkanTexture*>(src);
|
||||
auto* const dstTexture = mResourceAllocator.handle_cast<VulkanTexture*>(dst);
|
||||
assert_invariant(srcTexture);
|
||||
assert_invariant(dstTexture);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
dstTexture->width == srcTexture->width && dstTexture->height == srcTexture->height)
|
||||
<< "invalid resolve: src and dst sizes don't match";
|
||||
ASSERT_PRECONDITION(
|
||||
dstTexture->width == srcTexture->width && dstTexture->height == srcTexture->height,
|
||||
"invalid resolve: src and dst sizes don't match");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(srcTexture->samples > 1 && dstTexture->samples == 1)
|
||||
<< "invalid resolve: src.samples=" << +srcTexture->samples
|
||||
<< ", dst.samples=" << +dstTexture->samples;
|
||||
ASSERT_PRECONDITION(srcTexture->samples > 1 && dstTexture->samples == 1,
|
||||
"invalid resolve: src.samples=%u, dst.samples=%u",
|
||||
+srcTexture->samples, +dstTexture->samples);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(srcTexture->format == dstTexture->format)
|
||||
<< "src and dst texture format don't match";
|
||||
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!isDepthFormat(srcTexture->format))
|
||||
<< "can't resolve depth formats";
|
||||
ASSERT_PRECONDITION(!isDepthFormat(srcTexture->format),
|
||||
"can't resolve depth formats");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!isStencilFormat(srcTexture->format))
|
||||
<< "can't resolve stencil formats";
|
||||
ASSERT_PRECONDITION(!isStencilFormat(srcTexture->format),
|
||||
"can't resolve stencil formats");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST))
|
||||
<< "texture doesn't have BLIT_DST";
|
||||
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC))
|
||||
<< "texture doesn't have BLIT_SRC";
|
||||
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
mBlitter.resolve(
|
||||
{ .texture = dstTexture, .level = dstLevel, .layer = dstLayer },
|
||||
@@ -1684,20 +1679,20 @@ void VulkanDriver::blit(
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("blit");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
|
||||
<< "blit() cannot be invoked inside a render pass.";
|
||||
ASSERT_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE,
|
||||
"blit() cannot be invoked inside a render pass.");
|
||||
|
||||
auto* const srcTexture = mResourceAllocator.handle_cast<VulkanTexture*>(src);
|
||||
auto* const dstTexture = mResourceAllocator.handle_cast<VulkanTexture*>(dst);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST))
|
||||
<< "texture doesn't have BLIT_DST";
|
||||
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC))
|
||||
<< "texture doesn't have BLIT_SRC";
|
||||
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(srcTexture->format == dstTexture->format)
|
||||
<< "src and dst texture format don't match";
|
||||
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
// The Y inversion below makes it so that Vk matches GL and Metal.
|
||||
|
||||
@@ -1729,15 +1724,15 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
|
||||
// Note: blitDEPRECATED is only used for Renderer::copyFrame()
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
|
||||
<< "blitDEPRECATED() cannot be invoked inside a render pass.";
|
||||
ASSERT_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE,
|
||||
"blitDEPRECATED() cannot be invoked inside a render pass.");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(buffers == TargetBufferFlags::COLOR0)
|
||||
<< "blitDEPRECATED only supports COLOR0";
|
||||
ASSERT_PRECONDITION(buffers == TargetBufferFlags::COLOR0,
|
||||
"blitDEPRECATED only supports COLOR0");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
srcRect.left >= 0 && srcRect.bottom >= 0 && dstRect.left >= 0 && dstRect.bottom >= 0)
|
||||
<< "Source and destination rects must be positive.";
|
||||
ASSERT_PRECONDITION(srcRect.left >= 0 && srcRect.bottom >= 0 &&
|
||||
dstRect.left >= 0 && dstRect.bottom >= 0,
|
||||
"Source and destination rects must be positive.");
|
||||
|
||||
VulkanRenderTarget* dstTarget = mResourceAllocator.handle_cast<VulkanRenderTarget*>(dst);
|
||||
VulkanRenderTarget* srcTarget = mResourceAllocator.handle_cast<VulkanRenderTarget*>(src);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -64,8 +64,8 @@ VulkanFboCache::VulkanFboCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
VulkanFboCache::~VulkanFboCache() {
|
||||
FILAMENT_CHECK_POSTCONDITION(mFramebufferCache.empty() && mRenderPassCache.empty())
|
||||
<< "Please explicitly call terminate() while the VkDevice is still alive.";
|
||||
ASSERT_POSTCONDITION(mFramebufferCache.empty() && mRenderPassCache.empty(),
|
||||
"Please explicitly call terminate() while the VkDevice is still alive.");
|
||||
}
|
||||
|
||||
VkFramebuffer VulkanFboCache::getFramebuffer(FboKey config) noexcept {
|
||||
@@ -115,7 +115,7 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey config) noexcept {
|
||||
mRenderPassRefCount[info.renderPass]++;
|
||||
VkFramebuffer framebuffer;
|
||||
VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer);
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to create framebuffer.";
|
||||
ASSERT_POSTCONDITION(!error, "Unable to create framebuffer.");
|
||||
mFramebufferCache[config] = {framebuffer, mCurrentTime};
|
||||
return framebuffer;
|
||||
}
|
||||
@@ -306,7 +306,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
// Finally, create the VkRenderPass.
|
||||
VkRenderPass renderPass;
|
||||
VkResult error = vkCreateRenderPass(mDevice, &renderPassInfo, VKALLOC, &renderPass);
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to create render pass.";
|
||||
ASSERT_POSTCONDITION(!error, "Unable to create render pass.");
|
||||
mRenderPassCache[config] = {renderPass, mCurrentTime};
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
|
||||
|
||||
@@ -237,7 +237,7 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
.pCode = data,
|
||||
};
|
||||
VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create shader module.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create shader module.");
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
std::string name{ builder.getName().c_str(), builder.getName().size() };
|
||||
|
||||
@@ -71,8 +71,8 @@ void TaskHandler::shutdown() {
|
||||
}
|
||||
mHasTaskCondition.notify_one();
|
||||
mThread.join();
|
||||
FILAMENT_CHECK_POSTCONDITION(mTaskQueue.empty())
|
||||
<< "ReadPixels handler has tasks in the queue after shutdown";
|
||||
ASSERT_POSTCONDITION(mTaskQueue.empty(),
|
||||
"ReadPixels handler has tasks in the queue after shutdown");
|
||||
}
|
||||
|
||||
void TaskHandler::loop() {
|
||||
@@ -190,8 +190,8 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
|
||||
<< utils::io::endl;
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES)
|
||||
<< "VulkanReadPixels: unable to find a memory type that meets requirements.";
|
||||
ASSERT_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES,
|
||||
"VulkanReadPixels: unable to find a memory type that meets requirements.");
|
||||
|
||||
VkMemoryAllocateInfo const allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
|
||||
@@ -122,7 +122,7 @@ VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
|
||||
};
|
||||
VkSampler sampler;
|
||||
VkResult error = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler);
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to create sampler.";
|
||||
ASSERT_POSTCONDITION(!error, "Unable to create sampler.");
|
||||
mCache.insert({params, sampler});
|
||||
return sampler;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
|
||||
mAcquired(false),
|
||||
mIsFirstRenderPass(true) {
|
||||
swapChain = mPlatform->createSwapChain(nativeWindow, flags, extent);
|
||||
FILAMENT_CHECK_POSTCONDITION(swapChain) << "Unable to create swapchain";
|
||||
ASSERT_POSTCONDITION(swapChain, "Unable to create swapchain");
|
||||
|
||||
VkSemaphoreCreateInfo const createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
@@ -53,9 +53,8 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
|
||||
for (uint32_t i = 0; i < IMAGE_READY_SEMAPHORE_COUNT; ++i) {
|
||||
VkResult result =
|
||||
vkCreateSemaphore(mPlatform->getDevice(), &createInfo, nullptr, mImageReady + i);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Failed to create semaphore";
|
||||
}
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Failed to create semaphore");
|
||||
}
|
||||
}
|
||||
|
||||
update();
|
||||
@@ -113,9 +112,9 @@ void VulkanSwapChain::present() {
|
||||
if (!mHeadless) {
|
||||
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
|
||||
VkResult const result = mPlatform->present(swapChain, mCurrentSwapIndex, finishedDrawing);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR ||
|
||||
result == VK_ERROR_OUT_OF_DATE_KHR)
|
||||
<< "Cannot present in swapchain.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR ||
|
||||
result == VK_ERROR_OUT_OF_DATE_KHR,
|
||||
"Cannot present in swapchain.");
|
||||
}
|
||||
|
||||
// We presented the last acquired buffer.
|
||||
@@ -142,8 +141,8 @@ void VulkanSwapChain::acquire(bool& resized) {
|
||||
mCurrentImageReadyIndex = (mCurrentImageReadyIndex + 1) % IMAGE_READY_SEMAPHORE_COUNT;
|
||||
const VkSemaphore imageReady = mImageReady[mCurrentImageReadyIndex];
|
||||
VkResult const result = mPlatform->acquire(swapChain, imageReady, &mCurrentSwapIndex);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
|
||||
<< "Cannot acquire in swapchain.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR,
|
||||
"Cannot acquire in swapchain.");
|
||||
if (imageReady != VK_NULL_HANDLE) {
|
||||
mCommands->injectDependency(imageReady);
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
<< "target = " << static_cast<int>(target) <<", "
|
||||
<< "format = " << mVkFormat << utils::io::endl;
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to create image.";
|
||||
ASSERT_POSTCONDITION(!error, "Unable to create image.");
|
||||
|
||||
// Allocate memory for the VkImage and bind it.
|
||||
VkMemoryRequirements memReqs = {};
|
||||
@@ -186,8 +186,8 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
uint32_t memoryTypeIndex
|
||||
= context.selectMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES)
|
||||
<< "VulkanTexture: unable to find a memory type that meets requirements.";
|
||||
ASSERT_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES,
|
||||
"VulkanTexture: unable to find a memory type that meets requirements.");
|
||||
|
||||
VkMemoryAllocateInfo allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
@@ -195,9 +195,9 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
.memoryTypeIndex = memoryTypeIndex,
|
||||
};
|
||||
error = vkAllocateMemory(mDevice, &allocInfo, nullptr, &mTextureImageMemory);
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to allocate image memory.";
|
||||
ASSERT_POSTCONDITION(!error, "Unable to allocate image memory.");
|
||||
error = vkBindImageMemory(mDevice, mTextureImage, mTextureImageMemory, 0);
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to bind image.";
|
||||
ASSERT_POSTCONDITION(!error, "Unable to bind image.");
|
||||
|
||||
uint32_t layerCount = 0;
|
||||
if (target == SamplerType::SAMPLER_CUBEMAP) {
|
||||
|
||||
@@ -51,7 +51,7 @@ VkFormat getVkFormat(ElementType type, bool normalized, bool integer) {
|
||||
case ElementType::SHORT4: return VK_FORMAT_R16G16B16A16_SNORM;
|
||||
case ElementType::USHORT4: return VK_FORMAT_R16G16B16A16_UNORM;
|
||||
default:
|
||||
FILAMENT_CHECK_POSTCONDITION(false) << "Normalized format does not exist.";
|
||||
ASSERT_POSTCONDITION(false, "Normalized format does not exist.");
|
||||
return VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,13 +56,13 @@ uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask);
|
||||
// considered, but because the "variadic" part of the vk methods (i.e. the inputs) are before the
|
||||
// non-variadic parts, this breaks the template type matching logic. Hence, we use a macro approach
|
||||
// here.
|
||||
#define EXPAND_ENUM(...) \
|
||||
uint32_t size = 0; \
|
||||
VkResult result = func(__VA_ARGS__, nullptr); \
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "enumerate size error"; \
|
||||
utils::FixedCapacityVector<OutType> ret(size); \
|
||||
result = func(__VA_ARGS__, ret.data()); \
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "enumerate error"; \
|
||||
#define EXPAND_ENUM(...)\
|
||||
uint32_t size = 0;\
|
||||
VkResult result = func(__VA_ARGS__, nullptr);\
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "enumerate size error");\
|
||||
utils::FixedCapacityVector<OutType> ret(size);\
|
||||
result = func(__VA_ARGS__, ret.data());\
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "enumerate error");\
|
||||
return std::move(ret);
|
||||
|
||||
#define EXPAND_ENUM_NO_ARGS() EXPAND_ENUM(&size)
|
||||
|
||||
@@ -170,9 +170,9 @@ public:
|
||||
};
|
||||
VkDescriptorSet vkSet;
|
||||
UTILS_UNUSED VkResult result = vkAllocateDescriptorSets(mDevice, &allocInfo, &vkSet);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Failed to allocate descriptor set code=" << result << " size=" << mSize
|
||||
<< " capacity=" << mCapacity << " count=" << "%s";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS,
|
||||
"Failed to allocate descriptor set code=%d size=%d capacity=%d count=%s", result,
|
||||
mSize, mCapacity);
|
||||
mSize++;
|
||||
return createSet(layout->bitmask, vkSet);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -281,8 +279,8 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
|
||||
}
|
||||
|
||||
VkResult result = vkCreateInstance(&instanceCreateInfo, VKALLOC, &instance);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create Vulkan instance. Result=" << result;
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan instance. Result=%d",
|
||||
result);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -315,7 +313,6 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
||||
.samplerAnisotropy = features.samplerAnisotropy,
|
||||
.textureCompressionETC2 = features.textureCompressionETC2,
|
||||
.textureCompressionBC = features.textureCompressionBC,
|
||||
.shaderClipDistance = features.shaderClipDistance,
|
||||
};
|
||||
|
||||
deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
|
||||
@@ -333,7 +330,7 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
||||
}
|
||||
|
||||
VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateDevice error=" << result << ".";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateDevice error=%d.", result);
|
||||
|
||||
return device;
|
||||
}
|
||||
@@ -467,9 +464,9 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance,
|
||||
deviceList[deviceInd].name = targetDeviceProperties.deviceName;
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(gpuPreference.index < static_cast<int32_t>(deviceList.size()))
|
||||
<< "Provided GPU index=" << gpuPreference.index
|
||||
<< " >= the number of GPUs=" << static_cast<int32_t>(deviceList.size());
|
||||
ASSERT_PRECONDITION(gpuPreference.index < static_cast<int32_t>(deviceList.size()),
|
||||
"Provided GPU index=%d >= the number of GPUs=%d", gpuPreference.index,
|
||||
static_cast<int32_t>(deviceList.size()));
|
||||
|
||||
// Sort the found devices
|
||||
std::sort(deviceList.begin(), deviceList.end(),
|
||||
@@ -497,7 +494,7 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance,
|
||||
return deviceTypeOrder(a.deviceType) < deviceTypeOrder(b.deviceType);
|
||||
});
|
||||
auto device = deviceList.back().device;
|
||||
FILAMENT_CHECK_POSTCONDITION(device != VK_NULL_HANDLE) << "Unable to find suitable device.";
|
||||
ASSERT_POSTCONDITION(device != VK_NULL_HANDLE, "Unable to find suitable device.");
|
||||
return device;
|
||||
}
|
||||
|
||||
@@ -586,21 +583,21 @@ void VulkanPlatform::terminate() {
|
||||
Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept {
|
||||
// Load Vulkan entry points.
|
||||
FILAMENT_CHECK_POSTCONDITION(bluevk::initialize()) << "BlueVK is unable to load entry points.";
|
||||
ASSERT_POSTCONDITION(bluevk::initialize(), "BlueVK is unable to load entry points.");
|
||||
|
||||
if (sharedContext) {
|
||||
VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext;
|
||||
// All fields of VulkanSharedContext should be present.
|
||||
FILAMENT_CHECK_PRECONDITION(scontext->instance != VK_NULL_HANDLE)
|
||||
<< "Client needs to provide VkInstance";
|
||||
FILAMENT_CHECK_PRECONDITION(scontext->physicalDevice != VK_NULL_HANDLE)
|
||||
<< "Client needs to provide VkPhysicalDevice";
|
||||
FILAMENT_CHECK_PRECONDITION(scontext->logicalDevice != VK_NULL_HANDLE)
|
||||
<< "Client needs to provide VkDevice";
|
||||
FILAMENT_CHECK_PRECONDITION(scontext->graphicsQueueFamilyIndex != INVALID_VK_INDEX)
|
||||
<< "Client needs to provide graphics queue family index";
|
||||
FILAMENT_CHECK_PRECONDITION(scontext->graphicsQueueIndex != INVALID_VK_INDEX)
|
||||
<< "Client needs to provide graphics queue index";
|
||||
ASSERT_PRECONDITION(scontext->instance != VK_NULL_HANDLE,
|
||||
"Client needs to provide VkInstance");
|
||||
ASSERT_PRECONDITION(scontext->physicalDevice != VK_NULL_HANDLE,
|
||||
"Client needs to provide VkPhysicalDevice");
|
||||
ASSERT_PRECONDITION(scontext->logicalDevice != VK_NULL_HANDLE,
|
||||
"Client needs to provide VkDevice");
|
||||
ASSERT_PRECONDITION(scontext->graphicsQueueFamilyIndex != INVALID_VK_INDEX,
|
||||
"Client needs to provide graphics queue family index");
|
||||
ASSERT_PRECONDITION(scontext->graphicsQueueIndex != INVALID_VK_INDEX,
|
||||
"Client needs to provide graphics queue index");
|
||||
|
||||
mImpl->mInstance = scontext->instance;
|
||||
mImpl->mPhysicalDevice = scontext->physicalDevice;
|
||||
@@ -646,8 +643,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
|
||||
VulkanPlatform::Customization::GPUPreference const pref = getCustomization().gpu;
|
||||
bool const hasGPUPreference = pref.index >= 0 || !pref.deviceName.empty();
|
||||
FILAMENT_CHECK_PRECONDITION(!(hasGPUPreference && sharedContext))
|
||||
<< "Cannot both share context and indicate GPU preference";
|
||||
ASSERT_PRECONDITION(!(hasGPUPreference && sharedContext),
|
||||
"Cannot both share context and indicate GPU preference");
|
||||
|
||||
mImpl->mPhysicalDevice = mImpl->mPhysicalDevice == VK_NULL_HANDLE
|
||||
? selectPhysicalDevice(mImpl->mInstance, pref)
|
||||
@@ -668,12 +665,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
|
||||
@@ -707,8 +698,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
|
||||
#ifdef NDEBUG
|
||||
// If we are in release build, we should not have turned on debug extensions
|
||||
FILAMENT_CHECK_POSTCONDITION(!context.mDebugUtilsSupported && !context.mDebugMarkersSupported)
|
||||
<< "Debug utils should not be enabled in release build.";
|
||||
ASSERT_POSTCONDITION(!context.mDebugUtilsSupported && !context.mDebugMarkersSupported,
|
||||
"Debug utils should not be enabled in release build.");
|
||||
#endif
|
||||
|
||||
context.mDepthStencilFormats = findAttachmentDepthStencilFormats(mImpl->mPhysicalDevice);
|
||||
|
||||
@@ -123,7 +123,7 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
|
||||
};
|
||||
VkResult const result = vkCreateAndroidSurfaceKHR(instance, &createInfo, VKALLOC,
|
||||
(VkSurfaceKHR*) &surface);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateAndroidSurfaceKHR error.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateAndroidSurfaceKHR error.");
|
||||
#elif defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
wl* ptrval = reinterpret_cast<wl*>(nativeWindow);
|
||||
extent.width = ptrval->width;
|
||||
@@ -138,11 +138,11 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
|
||||
};
|
||||
VkResult const result = vkCreateWaylandSurfaceKHR(instance, &createInfo, VKALLOC,
|
||||
(VkSurfaceKHR*) &surface);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateWaylandSurfaceKHR error.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateWaylandSurfaceKHR error.");
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
|
||||
if (g_x11_vk.library == nullptr) {
|
||||
g_x11_vk.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11_vk.library) << "Unable to open X11 library.";
|
||||
ASSERT_PRECONDITION(g_x11_vk.library, "Unable to open X11 library.");
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
g_x11_vk.xcbConnect = (XCB_CONNECT) dlsym(g_x11_vk.library, "xcb_connect");
|
||||
int screen;
|
||||
@@ -151,7 +151,7 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
|
||||
#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);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11_vk.display) << "Unable to open X11 display.";
|
||||
ASSERT_PRECONDITION(g_x11_vk.display, "Unable to open X11 display.");
|
||||
#endif
|
||||
}
|
||||
#if defined(FILAMENT_SUPPORTS_XCB) || defined(FILAMENT_SUPPORTS_XLIB)
|
||||
@@ -164,8 +164,8 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
|
||||
useXcb = true;
|
||||
#endif
|
||||
if (useXcb) {
|
||||
FILAMENT_CHECK_POSTCONDITION(vkCreateXcbSurfaceKHR)
|
||||
<< "Unable to load vkCreateXcbSurfaceKHR function.";
|
||||
ASSERT_POSTCONDITION(vkCreateXcbSurfaceKHR,
|
||||
"Unable to load vkCreateXcbSurfaceKHR function.");
|
||||
|
||||
VkXcbSurfaceCreateInfoKHR const createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR,
|
||||
@@ -174,14 +174,13 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
|
||||
};
|
||||
VkResult const result = vkCreateXcbSurfaceKHR(instance, &createInfo, VKALLOC,
|
||||
(VkSurfaceKHR*) &surface);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkCreateXcbSurfaceKHR error.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateXcbSurfaceKHR error.");
|
||||
}
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
if (!useXcb) {
|
||||
FILAMENT_CHECK_POSTCONDITION(vkCreateXlibSurfaceKHR)
|
||||
<< "Unable to load vkCreateXlibSurfaceKHR function.";
|
||||
ASSERT_POSTCONDITION(vkCreateXlibSurfaceKHR,
|
||||
"Unable to load vkCreateXlibSurfaceKHR function.");
|
||||
|
||||
VkXlibSurfaceCreateInfoKHR const createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
|
||||
@@ -190,8 +189,7 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
|
||||
};
|
||||
VkResult const result = vkCreateXlibSurfaceKHR(instance, &createInfo, VKALLOC,
|
||||
(VkSurfaceKHR*) &surface);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkCreateXlibSurfaceKHR error.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateXlibSurfaceKHR error.");
|
||||
}
|
||||
#endif
|
||||
#elif defined(WIN32)
|
||||
@@ -202,7 +200,7 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
|
||||
};
|
||||
VkResult const result = vkCreateWin32SurfaceKHR(instance, &createInfo, nullptr,
|
||||
(VkSurfaceKHR*) &surface);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateWin32SurfaceKHR error.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateWin32SurfaceKHR error.");
|
||||
#endif
|
||||
return std::make_tuple(surface, extent);
|
||||
}
|
||||
|
||||
@@ -68,22 +68,20 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
|
||||
VkSurfaceKHR surface;
|
||||
#if defined(__APPLE__)
|
||||
NSView* nsview = (__bridge NSView*) nativeWindow;
|
||||
FILAMENT_CHECK_POSTCONDITION(nsview) << "Unable to obtain Metal-backed NSView.";
|
||||
ASSERT_POSTCONDITION(nsview, "Unable to obtain Metal-backed NSView.");
|
||||
|
||||
// Create the VkSurface.
|
||||
FILAMENT_CHECK_POSTCONDITION(vkCreateMacOSSurfaceMVK)
|
||||
<< "Unable to load vkCreateMacOSSurfaceMVK.";
|
||||
ASSERT_POSTCONDITION(vkCreateMacOSSurfaceMVK, "Unable to load vkCreateMacOSSurfaceMVK.");
|
||||
VkMacOSSurfaceCreateInfoMVK createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
|
||||
createInfo.pView = (__bridge void*) nsview;
|
||||
VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
|
||||
(VkSurfaceKHR*) &surface);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateMacOSSurfaceMVK error.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateMacOSSurfaceMVK error.");
|
||||
#elif defined(IOS) && defined(METAL_AVAILABLE)
|
||||
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
|
||||
// Create the VkSurface.
|
||||
FILAMENT_CHECK_POSTCONDITION(vkCreateIOSSurfaceMVK)
|
||||
<< "Unable to load vkCreateIOSSurfaceMVK function.";
|
||||
ASSERT_POSTCONDITION(vkCreateIOSSurfaceMVK, "Unable to load vkCreateIOSSurfaceMVK function.");
|
||||
VkIOSSurfaceCreateInfoMVK createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
|
||||
createInfo.pNext = NULL;
|
||||
@@ -91,7 +89,7 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
|
||||
createInfo.pView = metalLayer;
|
||||
VkResult result = vkCreateIOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
|
||||
(VkSurfaceKHR*) &surface);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateIOSSurfaceMVK error.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateIOSSurfaceMVK error.");
|
||||
#endif
|
||||
return std::make_tuple(surface, VkExtent2D {});
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& co
|
||||
};
|
||||
VkImage image;
|
||||
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &image);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create image: " << static_cast<int32_t>(result);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS,
|
||||
"Unable to create image: ", static_cast<int32_t>(result));
|
||||
|
||||
// Allocate memory for the VkImage and bind it.
|
||||
VkDeviceMemory imageMemory;
|
||||
@@ -61,8 +61,8 @@ std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& co
|
||||
uint32_t memoryTypeIndex
|
||||
= context.selectMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES)
|
||||
<< "VulkanPlatformSwapChainImpl: unable to find a memory type that meets requirements.";
|
||||
ASSERT_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES,
|
||||
"VulkanPlatformSwapChainImpl: unable to find a memory type that meets requirements.");
|
||||
|
||||
VkMemoryAllocateInfo allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
@@ -70,9 +70,9 @@ std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& co
|
||||
.memoryTypeIndex = memoryTypeIndex,
|
||||
};
|
||||
result = vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to allocate image memory.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to allocate image memory.");
|
||||
result = vkBindImageMemory(device, image, imageMemory, 0);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to bind image.";
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to bind image.");
|
||||
return std::tuple(image, imageMemory);
|
||||
}
|
||||
|
||||
@@ -178,8 +178,8 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceFormat.format != VK_FORMAT_UNDEFINED)
|
||||
<< "Cannot find suitable swapchain format";
|
||||
ASSERT_POSTCONDITION(surfaceFormat.format != VK_FORMAT_UNDEFINED,
|
||||
"Cannot find suitable swapchain format");
|
||||
|
||||
// Verify that our chosen present mode is supported. In practice all devices support the FIFO
|
||||
// mode, but we check for it anyway for completeness. (and to avoid validation warnings)
|
||||
@@ -193,8 +193,8 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(foundSuitablePresentMode)
|
||||
<< "Desired present mode is not supported by this device.";
|
||||
ASSERT_POSTCONDITION(foundSuitablePresentMode,
|
||||
"Desired present mode is not supported by this device.");
|
||||
|
||||
// Create the low-level swap chain.
|
||||
if (caps.currentExtent.width == VULKAN_UNDEFINED_EXTENT
|
||||
@@ -237,8 +237,8 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
|
||||
.oldSwapchain = mSwapchain,
|
||||
};
|
||||
VkResult result = vkCreateSwapchainKHR(mDevice, &createInfo, VKALLOC, &mSwapchain);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkGetPhysicalDeviceSurfaceFormatsKHR error: " << static_cast<int32_t>(result);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkGetPhysicalDeviceSurfaceFormatsKHR error: %d",
|
||||
static_cast<int32_t>(result));
|
||||
|
||||
mSwapChainBundle.colors = enumerate(vkGetSwapchainImagesKHR, mDevice, mSwapchain);
|
||||
mSwapChainBundle.colorFormat = surfaceFormat.format;
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <fstream>
|
||||
@@ -149,50 +150,59 @@ void BackendTest::renderTriangle(Handle<HwRenderTarget> renderTarget,
|
||||
}
|
||||
|
||||
void BackendTest::readPixelsAndAssertHash(const char* testName, size_t width, size_t height,
|
||||
Handle<HwRenderTarget> rt, uint32_t expectedHash, bool exportScreenshot) {
|
||||
void* buffer = calloc(1, width * height * 4);
|
||||
Handle<HwRenderTarget> rt, uint32_t const expectedHash, bool const exportScreenshot) {
|
||||
auto img = getRenderTargetRGB(rt, width, height);
|
||||
uint32_t hash = computeImageHash(img);
|
||||
|
||||
struct Capture {
|
||||
uint32_t expectedHash;
|
||||
char* name;
|
||||
bool exportScreenshot;
|
||||
size_t width, height;
|
||||
};
|
||||
auto* c = new Capture();
|
||||
c->expectedHash = expectedHash;
|
||||
c->name = strdup(testName);
|
||||
c->exportScreenshot = exportScreenshot;
|
||||
c->width = width;
|
||||
c->height = height;
|
||||
|
||||
PixelBufferDescriptor pbd(buffer, width * height * 4, PixelDataFormat::RGBA, PixelDataType::UBYTE,
|
||||
1, 0, 0, width, [](void* buffer, size_t size, void* user) {
|
||||
auto* c = (Capture*)user;
|
||||
|
||||
// Export a screenshot, if requested.
|
||||
if (c->exportScreenshot) {
|
||||
#ifndef IOS
|
||||
LinearImage image(c->width, c->height, 4);
|
||||
image = toLinearWithAlpha<uint8_t>(c->width, c->height, c->width * 4,
|
||||
(uint8_t*) buffer);
|
||||
const std::string png = std::string(c->name) + ".png";
|
||||
std::ofstream outputStream(png.c_str(), std::ios::binary | std::ios::trunc);
|
||||
ImageEncoder::encode(outputStream, ImageEncoder::Format::PNG, image, "",
|
||||
png);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Hash the contents of the buffer and check that they match.
|
||||
uint32_t hash = utils::hash::murmur3((const uint32_t*) buffer, size / 4, 0);
|
||||
ASSERT_EQ(hash, c->expectedHash) << c->name << " failed: hashes do not match." << std::endl;
|
||||
|
||||
free(buffer);
|
||||
free(c->name);
|
||||
free(c);
|
||||
}, (void*)c);
|
||||
getDriverApi().readPixels(rt, 0, 0, width, height, std::move(pbd));
|
||||
if (exportScreenshot) {
|
||||
writeImage(img, width, height, utils::CString(testName));
|
||||
}
|
||||
ASSERT_EQ(hash, expectedHash) << testName << " failed: hashes do not match." << std::endl;
|
||||
}
|
||||
|
||||
void BackendTest::writeImage(utils::FixedCapacityVector<uint8_t> img, size_t w, size_t h,
|
||||
utils::CString const& fname) {
|
||||
constexpr size_t MAX_FNAME_LEN = 50;
|
||||
constexpr size_t FNAME_EXT_LEN = 4;
|
||||
constexpr size_t TOTAL_LEN = MAX_FNAME_LEN + FNAME_EXT_LEN + 1;
|
||||
char buf[TOTAL_LEN];
|
||||
|
||||
ASSERT_PRECONDITION(fname.size() <= MAX_FNAME_LEN, "File name %s is too long", fname.c_str());
|
||||
|
||||
snprintf(buf, TOTAL_LEN, "%s.png", fname.c_str());
|
||||
|
||||
#ifndef IOS
|
||||
LinearImage image(w, h, 4);
|
||||
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, img.data());
|
||||
std::ofstream pngstrm(buf, std::ios::binary | std::ios::trunc);
|
||||
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<uint8_t> BackendTest::getRenderTarget(
|
||||
Handle<HwRenderTarget> rt, size_t width, size_t height, PixelDataFormat format,
|
||||
PixelDataType dataType) {
|
||||
auto& api = getDriverApi();
|
||||
size_t const size = width * height * 4;
|
||||
utils::FixedCapacityVector<uint8_t> result(size);
|
||||
PixelBufferDescriptor pb(result.data(), size, format, dataType);
|
||||
api.readPixels(rt, 0, 0, width, height, std::move(pb));
|
||||
flushAndWait();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t BackendTest::computeImageHash(utils::FixedCapacityVector<uint8_t> const& img) {
|
||||
return utils::hash::murmur3((uint32_t*) img.data(), img.size() / 4, 0);
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<uint8_t> BackendTest::getRenderTargetRGB(
|
||||
Handle<HwRenderTarget> rt, size_t width, size_t height) {
|
||||
return getRenderTarget(rt, width, height, PixelDataFormat::RGBA,
|
||||
PixelDataType::UBYTE);
|
||||
}
|
||||
|
||||
|
||||
class Environment : public ::testing::Environment {
|
||||
public:
|
||||
virtual void SetUp() override {
|
||||
|
||||
@@ -67,6 +67,17 @@ protected:
|
||||
filament::backend::Driver& getDriver() { return *driver; }
|
||||
|
||||
private:
|
||||
void writeImage(utils::FixedCapacityVector<uint8_t> img, size_t w, size_t h,
|
||||
utils::CString const& fname);
|
||||
utils::FixedCapacityVector<uint8_t> getRenderTarget(
|
||||
filament::backend::Handle<filament::backend::HwRenderTarget> rt, size_t width,
|
||||
size_t height, filament::backend::PixelDataFormat format,
|
||||
filament::backend::PixelDataType dataType);
|
||||
uint32_t computeImageHash(utils::FixedCapacityVector<uint8_t> const& img);
|
||||
|
||||
utils::FixedCapacityVector<uint8_t> getRenderTargetRGB(
|
||||
filament::backend::Handle<filament::backend::HwRenderTarget> rt, size_t width,
|
||||
size_t height);
|
||||
|
||||
filament::backend::Driver* driver = nullptr;
|
||||
filament::backend::CommandBufferQueue commandBufferQueue;
|
||||
|
||||
@@ -60,36 +60,6 @@ struct MaterialParams {
|
||||
float4 scale;
|
||||
};
|
||||
|
||||
struct ScreenshotParams {
|
||||
int width;
|
||||
int height;
|
||||
const char* filename;
|
||||
uint32_t pixelHashResult;
|
||||
};
|
||||
|
||||
#ifdef IOS
|
||||
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, ScreenshotParams* params) {}
|
||||
#else
|
||||
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, ScreenshotParams* params) {
|
||||
using namespace image;
|
||||
const size_t size = params->width * params->height * 4;
|
||||
void* buffer = calloc(1, size);
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
ScreenshotParams* params = (ScreenshotParams*) user;
|
||||
int w = params->width, h = params->height;
|
||||
const uint32_t* texels = (uint32_t*) buffer;
|
||||
params->pixelHashResult = utils::hash::murmur3(texels, size / 4, 0);
|
||||
LinearImage image(w, h, 4);
|
||||
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, (uint8_t*) buffer);
|
||||
std::ofstream pngstrm(params->filename, std::ios::binary | std::ios::trunc);
|
||||
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", params->filename);
|
||||
};
|
||||
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb,
|
||||
(void*) params);
|
||||
dapi.readPixels(rt, 0, 0, params->width, params->height, std::move(pb));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void uploadUniforms(DriverApi& dapi, Handle<HwBufferObject> ubh, MaterialParams params) {
|
||||
MaterialParams* tmp = new MaterialParams(params);
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
@@ -243,21 +213,12 @@ TEST_F(BackendTest, ColorMagnify) {
|
||||
api.endFrame(0);
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMagnify.png" };
|
||||
api.beginFrame(0, 0, 0);
|
||||
dumpScreenshot(api, dstRenderTargets[0], ¶ms);
|
||||
api.commit(swapChain);
|
||||
constexpr uint32_t expected = 0x410bdd31;
|
||||
readPixelsAndAssertHash("ColorMagnify", kDstTexWidth, kDstTexHeight, dstRenderTargets[0],
|
||||
expected, true);
|
||||
api.endFrame(0);
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0x410bdd31;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
flushAndWait();
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
@@ -315,17 +276,15 @@ TEST_F(BackendTest, ColorMinify) {
|
||||
SamplerMagFilter::LINEAR);
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMinify.png" };
|
||||
dumpScreenshot(api, dstRenderTargets[0], ¶ms);
|
||||
api.beginFrame(0, 0, 0);
|
||||
constexpr uint32_t expected = 0xf3d9c53f;
|
||||
readPixelsAndAssertHash("ColorMinify", kDstTexWidth, kDstTexHeight, dstRenderTargets[0],
|
||||
expected, true);
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
flushAndWait();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0xf3d9c53f;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
api.destroyTexture(dstTexture);
|
||||
@@ -416,17 +375,13 @@ TEST_F(BackendTest, ColorResolve) {
|
||||
SamplerMagFilter::NEAREST);
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams sparams{ kDstTexWidth, kDstTexHeight, "ColorResolve.png" };
|
||||
dumpScreenshot(api, dstRenderTarget, &sparams);
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.beginFrame(0, 0, 0);
|
||||
constexpr uint32_t expected = 0xebfac2ef;
|
||||
readPixelsAndAssertHash("ColorResolve", kDstTexWidth, kDstTexHeight, dstRenderTarget,
|
||||
expected, true);
|
||||
api.endFrame(0);
|
||||
flushAndWait();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0xebfac2ef;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", sparams.pixelHashResult, expected);
|
||||
EXPECT_TRUE(sparams.pixelHashResult == expected);
|
||||
|
||||
// Cleanup.
|
||||
api.destroyBufferObject(ubuffer);
|
||||
api.destroyProgram(program);
|
||||
@@ -489,21 +444,12 @@ TEST_F(BackendTest, Blit2DTextureArray) {
|
||||
api.endFrame(0);
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "Blit2DTextureArray.png" };
|
||||
api.beginFrame(0, 0, 0);
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
api.commit(swapChain);
|
||||
constexpr uint32_t expected = 0x8de7d55b;
|
||||
readPixelsAndAssertHash("Blit2DTextureArray", kDstTexWidth, kDstTexHeight, dstRenderTarget,
|
||||
expected, true);
|
||||
api.endFrame(0);
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0x8de7d55b;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
flushAndWait();
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
@@ -578,28 +524,17 @@ TEST_F(BackendTest, BlitRegion) {
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegion.png" };
|
||||
api.beginFrame(0, 0, 0);
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
//
|
||||
// TODO: for some reason, this test has very, very slight (as in one pixel) differences between
|
||||
// OpenGL and Metal. So disable golden checking for now.
|
||||
// Use the compare tool from ImageMagick to see visual differences:
|
||||
// compare -verbose -metric mae BlitRegion_Metal.png BlitRegion_OpenGL.png difference.png
|
||||
//
|
||||
// const uint32_t expected = 0x74fa34ed;
|
||||
// printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
// EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
// api.beginFrame(0, 0, 0);
|
||||
// constexpr uint32_t expected = 0x74fa34ed;
|
||||
// readPixelsAndAssertHash("BlitRegion", kDstTexWidth, kDstTexHeight, dstRenderTarget,
|
||||
// expected, true);
|
||||
// api.endFrame(0);
|
||||
// flushAndWait();
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
@@ -655,23 +590,22 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
|
||||
.height = kDstTexHeight - 10,
|
||||
};
|
||||
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
|
||||
dstRect, srcRenderTargets[srcLevel],
|
||||
srcRect, SamplerMagFilter::LINEAR);
|
||||
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain.png" };
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
|
||||
// Push through an empty frame to allow the texture to upload and the blit to execute.
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.commit(swapChain);
|
||||
|
||||
api.endFrame(0);
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
// Grab a screenshot.
|
||||
api.beginFrame(0, 0, 0);
|
||||
constexpr uint32_t expected = 0xebfac2ef;
|
||||
readPixelsAndAssertHash("BlitRegionToSwapChain", kDstTexWidth, kDstTexHeight, dstRenderTarget,
|
||||
expected, true);
|
||||
api.endFrame(0);
|
||||
flushAndWait();
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "BackendTestUtils.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
@@ -64,8 +65,6 @@ void main() {
|
||||
fragColor = textureLod(test_tex, uv, params.sourceLevel);
|
||||
})";
|
||||
|
||||
static uint32_t sPixelHashResult = 0;
|
||||
|
||||
// Selecting a NPOT texture size seems to exacerbate the bug seen with Intel GPU's.
|
||||
// Note that Filament uses a higher precision format (R11F_G11F_B10F) but this does not seem
|
||||
// necessary to trigger the bug.
|
||||
@@ -99,30 +98,13 @@ static void uploadUniforms(DriverApi& dapi, Handle<HwBufferObject> ubh, Material
|
||||
dapi.updateBufferObject(ubh, std::move(bd), 0);
|
||||
}
|
||||
|
||||
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt) {
|
||||
const size_t size = kTexWidth * kTexHeight * 4;
|
||||
void* buffer = calloc(1, size);
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
int w = kTexWidth, h = kTexHeight;
|
||||
const uint32_t* texels = (uint32_t*) buffer;
|
||||
sPixelHashResult = 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("feedback.png", std::ios::binary | std::ios::trunc);
|
||||
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", "feedback.png");
|
||||
#endif
|
||||
free(buffer);
|
||||
};
|
||||
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
|
||||
dapi.readPixels(rt, 0, 0, kTexWidth, kTexHeight, std::move(pb));
|
||||
}
|
||||
|
||||
// TODO: This test needs work to get Metal and OpenGL to agree on results.
|
||||
// The problems are caused by both uploading and rendering into the same texture, since the OpenGL
|
||||
// backend's readPixels does not work correctly with textures that have image data uploaded.
|
||||
TEST_F(BackendTest, FeedbackLoops) {
|
||||
auto& api = getDriverApi();
|
||||
auto& driver = getDriver();
|
||||
uint32_t pixelHashResult = 0;
|
||||
|
||||
// The test is executed within this block scope to force destructors to run before
|
||||
// executeCommands().
|
||||
@@ -147,7 +129,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
program = api.createProgram(std::move(prog));
|
||||
}
|
||||
|
||||
TrianglePrimitive const triangle(getDriverApi());
|
||||
TrianglePrimitive const triangle(api);
|
||||
|
||||
// Create a texture.
|
||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
||||
@@ -211,8 +193,8 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
const uint32_t sourceLevel = targetLevel - 1;
|
||||
params.viewport.width = kTexWidth >> targetLevel;
|
||||
params.viewport.height = kTexHeight >> targetLevel;
|
||||
getDriverApi().setMinMaxLevels(texture, sourceLevel, sourceLevel);
|
||||
uploadUniforms(getDriverApi(), ubuffer, {
|
||||
api.setMinMaxLevels(texture, sourceLevel, sourceLevel);
|
||||
uploadUniforms(api, ubuffer, {
|
||||
.fbWidth = float(params.viewport.width),
|
||||
.fbHeight = float(params.viewport.height),
|
||||
.sourceLevel = float(sourceLevel),
|
||||
@@ -230,8 +212,8 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
const uint32_t sourceLevel = targetLevel + 1;
|
||||
params.viewport.width = kTexWidth >> targetLevel;
|
||||
params.viewport.height = kTexHeight >> targetLevel;
|
||||
getDriverApi().setMinMaxLevels(texture, sourceLevel, sourceLevel);
|
||||
uploadUniforms(getDriverApi(), ubuffer, {
|
||||
api.setMinMaxLevels(texture, sourceLevel, sourceLevel);
|
||||
uploadUniforms(api, ubuffer, {
|
||||
.fbWidth = float(params.viewport.width),
|
||||
.fbHeight = float(params.viewport.height),
|
||||
.sourceLevel = float(sourceLevel),
|
||||
@@ -241,14 +223,16 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
api.endRenderPass();
|
||||
}
|
||||
|
||||
getDriverApi().setMinMaxLevels(texture, 0, 0x7f);
|
||||
api.setMinMaxLevels(texture, 0, 0x7f);
|
||||
|
||||
// Read back the render target corresponding to the base level.
|
||||
//
|
||||
// NOTE: Calling glReadPixels on any miplevel other than the base level
|
||||
// seems to be un-reliable on some GPU's.
|
||||
if (frame == kNumFrames - 1) {
|
||||
dumpScreenshot(api, renderTargets[0]);
|
||||
constexpr uint32_t expected = 0x70695aa1;
|
||||
readPixelsAndAssertHash("feedback", kTexWidth, kTexHeight, renderTargets[0],
|
||||
expected, true);
|
||||
}
|
||||
|
||||
api.flush();
|
||||
@@ -256,7 +240,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
api.endFrame(0);
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
driver.purge();
|
||||
}
|
||||
|
||||
api.destroyProgram(program);
|
||||
@@ -264,10 +248,6 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
api.destroyTexture(texture);
|
||||
for (auto rt : renderTargets) api.destroyRenderTarget(rt);
|
||||
}
|
||||
|
||||
const uint32_t expected = 0x70695aa1;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", sPixelHashResult, expected);
|
||||
EXPECT_TRUE(sPixelHashResult == expected);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -116,7 +116,7 @@ class Engine;
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* SDL_SysWMinfo wmi;
|
||||
* SDL_VERSION(&wmi.version);
|
||||
* FILAMENT_CHECK_POSTCONDITION(SDL_GetWindowWMInfo(sdlWindow, &wmi)) << "SDL version unsupported!";
|
||||
* ASSERT_POSTCONDITION(SDL_GetWindowWMInfo(sdlWindow, &wmi), "SDL version unsupported!");
|
||||
* HDC nativeWindow = (HDC) wmi.info.win.hdc;
|
||||
*
|
||||
* using namespace filament;
|
||||
|
||||
@@ -299,8 +299,7 @@ void* Engine::streamAlloc(size_t size, size_t alignment) noexcept {
|
||||
// The external-facing execute does a flush, and is meant only for single-threaded environments.
|
||||
// It also discards the boolean return value, which would otherwise indicate a thread exit.
|
||||
void Engine::execute() {
|
||||
FILAMENT_CHECK_PRECONDITION(!UTILS_HAS_THREADING)
|
||||
<< "Execute is meant for single-threaded platforms.";
|
||||
ASSERT_PRECONDITION(!UTILS_HAS_THREADING, "Execute is meant for single-threaded platforms.");
|
||||
downcast(this)->flush();
|
||||
downcast(this)->execute();
|
||||
}
|
||||
@@ -310,14 +309,12 @@ utils::JobSystem& Engine::getJobSystem() noexcept {
|
||||
}
|
||||
|
||||
bool Engine::isPaused() const noexcept {
|
||||
FILAMENT_CHECK_PRECONDITION(UTILS_HAS_THREADING)
|
||||
<< "Pause is meant for multi-threaded platforms.";
|
||||
ASSERT_PRECONDITION(UTILS_HAS_THREADING, "Pause is meant for multi-threaded platforms.");
|
||||
return downcast(this)->isPaused();
|
||||
}
|
||||
|
||||
void Engine::setPaused(bool paused) {
|
||||
FILAMENT_CHECK_PRECONDITION(UTILS_HAS_THREADING)
|
||||
<< "Pause is meant for multi-threaded platforms.";
|
||||
ASSERT_PRECONDITION(UTILS_HAS_THREADING, "Pause is meant for multi-threaded platforms.");
|
||||
downcast(this)->setPaused(paused);
|
||||
}
|
||||
|
||||
@@ -358,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 {
|
||||
|
||||
@@ -80,8 +80,7 @@ RenderPassBuilder& RenderPassBuilder::customCommand(
|
||||
}
|
||||
|
||||
RenderPass RenderPassBuilder::build(FEngine& engine) {
|
||||
FILAMENT_CHECK_POSTCONDITION(mRenderableSoa)
|
||||
<< "RenderPassBuilder::geometry() hasn't been called";
|
||||
ASSERT_POSTCONDITION(mRenderableSoa, "RenderPassBuilder::geometry() hasn't been called");
|
||||
assert_invariant(mScissorViewport.width <= std::numeric_limits<int32_t>::max());
|
||||
assert_invariant(mScissorViewport.height <= std::numeric_limits<int32_t>::max());
|
||||
return RenderPass{ engine, *this };
|
||||
@@ -685,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);
|
||||
@@ -1031,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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -353,12 +353,14 @@ UTILS_NOINLINE
|
||||
void RendererUtils::readPixels(backend::DriverApi& driver, Handle<HwRenderTarget> renderTargetHandle,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
backend::PixelBufferDescriptor&& buffer) {
|
||||
FILAMENT_CHECK_PRECONDITION(buffer.type != PixelDataType::COMPRESSED)
|
||||
<< "buffer.format cannot be COMPRESSED";
|
||||
ASSERT_PRECONDITION(
|
||||
buffer.type != PixelDataType::COMPRESSED,
|
||||
"buffer.format cannot be COMPRESSED");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(buffer.alignment > 0 && buffer.alignment <= 8 &&
|
||||
!(buffer.alignment & (buffer.alignment - 1u)))
|
||||
<< "buffer.alignment must be 1, 2, 4 or 8";
|
||||
ASSERT_PRECONDITION(
|
||||
buffer.alignment > 0 && buffer.alignment <= 8 &&
|
||||
!(buffer.alignment & (buffer.alignment - 1u)),
|
||||
"buffer.alignment must be 1, 2, 4 or 8");
|
||||
|
||||
// It's not really possible to know here which formats will be supported because
|
||||
// it can vary depending on the RenderTarget, in GL the following are ALWAYS supported though:
|
||||
@@ -371,9 +373,8 @@ void RendererUtils::readPixels(backend::DriverApi& driver, Handle<HwRenderTarget
|
||||
buffer.top + height,
|
||||
buffer.alignment);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(buffer.size >= sizeNeeded)
|
||||
<< "Pixel buffer too small: has " << buffer.size << " bytes, needs " << sizeNeeded
|
||||
<< " bytes";
|
||||
ASSERT_PRECONDITION(buffer.size >= sizeNeeded,
|
||||
"Pixel buffer too small: has %u bytes, needs %u bytes", buffer.size, sizeNeeded);
|
||||
|
||||
driver.readPixels(renderTargetHandle, xoffset, yoffset, width, height, std::move(buffer));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -322,21 +309,19 @@ void RenderableManager::BuilderDetails::processBoneIndicesAndWights(Engine& engi
|
||||
for (auto& bonePair: mBonePairs) {
|
||||
auto primitiveIndex = bonePair.first;
|
||||
auto entries = mEntries;
|
||||
FILAMENT_CHECK_PRECONDITION(primitiveIndex < entries.size() && primitiveIndex >= 0)
|
||||
<< "[primitive @ " << primitiveIndex << "] primitiveindex is out of size ("
|
||||
<< entries.size() << ")";
|
||||
ASSERT_PRECONDITION(primitiveIndex < entries.size() && primitiveIndex >= 0,
|
||||
"[primitive @ %u] primitiveindex is out of size (%u)", primitiveIndex, entries.size());
|
||||
auto entry = mEntries[primitiveIndex];
|
||||
auto bonePairsForPrimitive = bonePair.second;
|
||||
auto vertexCount = entry.vertices->getVertexCount();
|
||||
FILAMENT_CHECK_PRECONDITION(bonePairsForPrimitive.size() == vertexCount)
|
||||
<< "[primitive @ " << primitiveIndex << "] bone indices and weights pairs count ("
|
||||
<< bonePairsForPrimitive.size() << ") must be equal to vertex count ("
|
||||
<< vertexCount << ")";
|
||||
ASSERT_PRECONDITION(bonePairsForPrimitive.size() == vertexCount,
|
||||
"[primitive @ %u] bone indices and weights pairs count (%u) must be equal to vertex count (%u)",
|
||||
primitiveIndex, bonePairsForPrimitive.size(), vertexCount);
|
||||
auto const& declaredAttributes = downcast(entry.vertices)->getDeclaredAttributes();
|
||||
FILAMENT_CHECK_PRECONDITION(declaredAttributes[VertexAttribute::BONE_INDICES] ||
|
||||
declaredAttributes[VertexAttribute::BONE_WEIGHTS])
|
||||
<< "[entity=" << entity.getId() << ", primitive @ " << primitiveIndex
|
||||
<< "] for advanced skinning set VertexBuffer::Builder::advancedSkinning()";
|
||||
ASSERT_PRECONDITION(declaredAttributes[VertexAttribute::BONE_INDICES]
|
||||
|| declaredAttributes[VertexAttribute::BONE_WEIGHTS],
|
||||
"[entity=%u, primitive @ %u] for advanced skinning set VertexBuffer::Builder::advancedSkinning()",
|
||||
entity.getId(), primitiveIndex);
|
||||
for (size_t iVertex = 0; iVertex < vertexCount; iVertex++) {
|
||||
size_t const bonesPerVertex = bonePairsForPrimitive[iVertex].size();
|
||||
maxPairsCount += bonesPerVertex;
|
||||
@@ -367,20 +352,16 @@ void RenderableManager::BuilderDetails::processBoneIndicesAndWights(Engine& engi
|
||||
for (size_t k = 0; k < bonePairsForPrimitive[iVertex].size(); k++) {
|
||||
auto boneWeight = bonePairsForPrimitive[iVertex][k][1];
|
||||
auto boneIndex = bonePairsForPrimitive[iVertex][k][0];
|
||||
FILAMENT_CHECK_PRECONDITION(boneWeight >= 0)
|
||||
<< "[entity=" << entity.getId() << ", primitive @ " << primitiveIndex
|
||||
<< "] bone weight (" << boneWeight << ") of vertex=" << iVertex
|
||||
<< " is negative";
|
||||
ASSERT_PRECONDITION(boneWeight >= 0,
|
||||
"[entity=%u, primitive @ %u] bone weight (%f) of vertex=%u is negative ",
|
||||
entity.getId(), primitiveIndex, boneWeight, iVertex);
|
||||
if (boneWeight > 0.0f) {
|
||||
FILAMENT_CHECK_PRECONDITION(boneIndex >= 0)
|
||||
<< "[entity=" << entity.getId() << ", primitive @ "
|
||||
<< primitiveIndex << "] bone index (" << (int)boneIndex
|
||||
<< ") of vertex=" << iVertex << " is negative";
|
||||
FILAMENT_CHECK_PRECONDITION(boneIndex < mSkinningBoneCount)
|
||||
<< "[entity=" << entity.getId() << ", primitive @ "
|
||||
<< primitiveIndex << "] bone index (" << (int)boneIndex
|
||||
<< ") of vertex=" << iVertex << " is bigger then bone count ("
|
||||
<< mSkinningBoneCount << ")";
|
||||
ASSERT_PRECONDITION(boneIndex >= 0,
|
||||
"[entity=%u, primitive @ %u] bone index (%i) of vertex=%u is negative ",
|
||||
entity.getId(), primitiveIndex, (int) boneIndex, iVertex);
|
||||
ASSERT_PRECONDITION(boneIndex < mSkinningBoneCount,
|
||||
"[entity=%u, primitive @ %u] bone index (%i) of vertex=%u is bigger then bone count (%u) ",
|
||||
entity.getId(), primitiveIndex, (int) boneIndex, iVertex, mSkinningBoneCount);
|
||||
boneWeightsSum += boneWeight;
|
||||
tempPairs[tempPairCount][0] = boneIndex;
|
||||
tempPairs[tempPairCount][1] = boneWeight;
|
||||
@@ -388,10 +369,9 @@ void RenderableManager::BuilderDetails::processBoneIndicesAndWights(Engine& engi
|
||||
}
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(boneWeightsSum > 0)
|
||||
<< "[entity=" << entity.getId() << ", primitive @ " << primitiveIndex
|
||||
<< "] sum of bone weights of vertex=" << iVertex << " is " << boneWeightsSum
|
||||
<< ", it should be positive.";
|
||||
ASSERT_PRECONDITION(boneWeightsSum > 0,
|
||||
"[entity=%u, primitive @ %u] sum of bone weights of vertex=%u is %f, it should be positive.",
|
||||
entity.getId(), primitiveIndex, iVertex, boneWeightsSum);
|
||||
|
||||
// see https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#skinned-mesh-attributes
|
||||
double const epsilon = 2e-7 * double(tempPairCount);
|
||||
@@ -439,29 +419,30 @@ void RenderableManager::BuilderDetails::processBoneIndicesAndWights(Engine& engi
|
||||
RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& engine, Entity entity) {
|
||||
bool isEmpty = true;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mSkinningBoneCount <= CONFIG_MAX_BONE_COUNT)
|
||||
<< "bone count > " << CONFIG_MAX_BONE_COUNT;
|
||||
ASSERT_PRECONDITION(mImpl->mSkinningBoneCount <= CONFIG_MAX_BONE_COUNT,
|
||||
"bone count > %u", CONFIG_MAX_BONE_COUNT);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
mImpl->mInstanceCount <= CONFIG_MAX_INSTANCES || !mImpl->mInstanceBuffer)
|
||||
<< "instance count is " << mImpl->mInstanceCount
|
||||
<< ", but instance count is limited to CONFIG_MAX_INSTANCES (" << CONFIG_MAX_INSTANCES
|
||||
<< ") instances when supplying transforms via an InstanceBuffer.";
|
||||
ASSERT_PRECONDITION(mImpl->mInstanceCount <= CONFIG_MAX_INSTANCES || !mImpl->mInstanceBuffer,
|
||||
"instance count is %zu, but instance count is limited to CONFIG_MAX_INSTANCES (%zu) "
|
||||
"instances when supplying transforms via an InstanceBuffer.",
|
||||
mImpl->mInstanceCount,
|
||||
CONFIG_MAX_INSTANCES);
|
||||
|
||||
if (mImpl->mGeometryType == GeometryType::STATIC) {
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mSkinningBoneCount > 0)
|
||||
<< "Skinning can't be used with STATIC geometry";
|
||||
ASSERT_PRECONDITION(mImpl->mSkinningBoneCount > 0,
|
||||
"Skinning can't be used with STATIC geometry");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mMorphTargetCount > 0)
|
||||
<< "Morphing can't be used with STATIC geometry";
|
||||
ASSERT_PRECONDITION(mImpl->mMorphTargetCount > 0,
|
||||
"Morphing can't be used with STATIC geometry");
|
||||
}
|
||||
|
||||
if (mImpl->mInstanceBuffer) {
|
||||
size_t const bufferInstanceCount = mImpl->mInstanceBuffer->mInstanceCount;
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mInstanceCount <= bufferInstanceCount)
|
||||
<< "instance count (" << mImpl->mInstanceCount
|
||||
<< ") must be less than or equal to the InstanceBuffer's instance "
|
||||
"count (" << bufferInstanceCount << ").";
|
||||
ASSERT_PRECONDITION(mImpl->mInstanceCount <= bufferInstanceCount,
|
||||
"instance count (%zu) must be less than or equal to the InstanceBuffer's instance "
|
||||
"count "
|
||||
"(%zu).",
|
||||
mImpl->mInstanceCount, bufferInstanceCount);
|
||||
}
|
||||
|
||||
if (UTILS_LIKELY(mImpl->mSkinningBoneCount || mImpl->mSkinningBufferMode)) {
|
||||
@@ -486,15 +467,15 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
|
||||
}
|
||||
|
||||
// we want a feature level violation to be a hard error (exception if enabled, or crash)
|
||||
FILAMENT_CHECK_PRECONDITION(downcast(engine).hasFeatureLevel(material->getFeatureLevel()))
|
||||
<< "Material \"" << material->getName().c_str_safe() << "\" has feature level "
|
||||
<< (uint8_t)material->getFeatureLevel() << " which is not supported by this Engine";
|
||||
ASSERT_PRECONDITION(downcast(engine).hasFeatureLevel(material->getFeatureLevel()),
|
||||
"Material \"%s\" has feature level %u which is not supported by this Engine",
|
||||
material->getName().c_str_safe(), (uint8_t)material->getFeatureLevel());
|
||||
|
||||
// reject invalid geometry parameters
|
||||
FILAMENT_CHECK_PRECONDITION(entry.offset + entry.count <= entry.indices->getIndexCount())
|
||||
<< "[entity=" << entity.getId() << ", primitive @ " << i << "] offset ("
|
||||
<< entry.offset << ") + count (" << entry.count << ") > indexCount ("
|
||||
<< entry.indices->getIndexCount() << ")";
|
||||
ASSERT_PRECONDITION(entry.offset + entry.count <= entry.indices->getIndexCount(),
|
||||
"[entity=%u, primitive @ %u] offset (%u) + count (%u) > indexCount (%u)",
|
||||
entity.getId(), i,
|
||||
entry.offset, entry.count, entry.indices->getIndexCount());
|
||||
|
||||
// this can't be an error because (1) those values are not immutable, so the caller
|
||||
// could fix later, and (2) the material's shader will work (i.e. compile), and
|
||||
@@ -511,11 +492,12 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
|
||||
isEmpty = false;
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!mImpl->mAABB.isEmpty() ||
|
||||
(!mImpl->mCulling && (!(mImpl->mReceiveShadows || mImpl->mCastShadows)) || isEmpty))
|
||||
<< "[entity=" << entity.getId()
|
||||
<< "] AABB can't be empty, unless culling is disabled and "
|
||||
"the object is not a shadow caster/receiver";
|
||||
ASSERT_PRECONDITION(
|
||||
!mImpl->mAABB.isEmpty() ||
|
||||
(!mImpl->mCulling && (!(mImpl->mReceiveShadows || mImpl->mCastShadows)) ||
|
||||
isEmpty),
|
||||
"[entity=%u] AABB can't be empty, unless culling is disabled and "
|
||||
"the object is not a shadow caster/receiver", entity.getId());
|
||||
|
||||
downcast(engine).createRenderable(*this, entity);
|
||||
return Success;
|
||||
@@ -664,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) };
|
||||
@@ -809,10 +787,9 @@ void FRenderableManager::setMaterialInstanceAt(Instance instance, uint8_t level,
|
||||
FMaterial const* material = mi->getMaterial();
|
||||
|
||||
// we want a feature level violation to be a hard error (exception if enabled, or crash)
|
||||
FILAMENT_CHECK_PRECONDITION(mEngine.hasFeatureLevel(material->getFeatureLevel()))
|
||||
<< "Material \"" << material->getName().c_str_safe() << "\" has feature level "
|
||||
<< (uint8_t)material->getFeatureLevel()
|
||||
<< " which is not supported by this Engine";
|
||||
ASSERT_PRECONDITION(mEngine.hasFeatureLevel(material->getFeatureLevel()),
|
||||
"Material \"%s\" has feature level %u which is not supported by this Engine",
|
||||
material->getName().c_str_safe(), (uint8_t)material->getFeatureLevel());
|
||||
|
||||
primitives[primitiveIndex].setMaterialInstance(mi);
|
||||
AttributeBitset const required = material->getRequiredAttributes();
|
||||
@@ -887,8 +864,8 @@ void FRenderableManager::setBones(Instance ci,
|
||||
if (ci) {
|
||||
Bones const& bones = mManager[ci].bones;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!bones.skinningBufferMode)
|
||||
<< "Disable skinning buffer mode to use this API";
|
||||
ASSERT_PRECONDITION(!bones.skinningBufferMode,
|
||||
"Disable skinning buffer mode to use this API");
|
||||
|
||||
assert_invariant(bones.handle && offset + boneCount <= bones.count);
|
||||
if (bones.handle) {
|
||||
@@ -903,8 +880,8 @@ void FRenderableManager::setBones(Instance ci,
|
||||
if (ci) {
|
||||
Bones const& bones = mManager[ci].bones;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!bones.skinningBufferMode)
|
||||
<< "Disable skinning buffer mode to use this API";
|
||||
ASSERT_PRECONDITION(!bones.skinningBufferMode,
|
||||
"Disable skinning buffer mode to use this API");
|
||||
|
||||
assert_invariant(bones.handle && offset + boneCount <= bones.count);
|
||||
if (bones.handle) {
|
||||
@@ -919,11 +896,12 @@ void FRenderableManager::setSkinningBuffer(FRenderableManager::Instance ci,
|
||||
|
||||
Bones& bones = mManager[ci].bones;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(bones.skinningBufferMode)
|
||||
<< "Enable skinning buffer mode to use this API";
|
||||
ASSERT_PRECONDITION(bones.skinningBufferMode,
|
||||
"Enable skinning buffer mode to use this API");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(count <= CONFIG_MAX_BONE_COUNT)
|
||||
<< "SkinningBuffer larger than 256 (count=" << count << ")";
|
||||
ASSERT_PRECONDITION(
|
||||
count <= CONFIG_MAX_BONE_COUNT,
|
||||
"SkinningBuffer larger than 256 (count=%u)", count);
|
||||
|
||||
// According to the OpenGL ES 3.2 specification in 7.6.3 Uniform
|
||||
// Buffer Object Bindings:
|
||||
@@ -935,9 +913,10 @@ void FRenderableManager::setSkinningBuffer(FRenderableManager::Instance ci,
|
||||
|
||||
count = CONFIG_MAX_BONE_COUNT;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(count + offset <= skinningBuffer->getBoneCount())
|
||||
<< "SkinningBuffer overflow (size=" << skinningBuffer->getBoneCount()
|
||||
<< ", count=" << count << ", offset=" << offset << ")";
|
||||
ASSERT_PRECONDITION(
|
||||
count + offset <= skinningBuffer->getBoneCount(),
|
||||
"SkinningBuffer overflow (size=%u, count=%u, offset=%u)",
|
||||
skinningBuffer->getBoneCount(), count, offset);
|
||||
|
||||
bones.handle = skinningBuffer->getHwHandle();
|
||||
bones.count = uint16_t(count);
|
||||
@@ -957,9 +936,9 @@ static void updateMorphWeights(FEngine& engine, backend::Handle<backend::HwBuffe
|
||||
void FRenderableManager::setMorphWeights(Instance instance, float const* weights,
|
||||
size_t count, size_t offset) {
|
||||
if (instance) {
|
||||
FILAMENT_CHECK_PRECONDITION(count + offset <= CONFIG_MAX_MORPH_TARGET_COUNT)
|
||||
<< "Only " << CONFIG_MAX_MORPH_TARGET_COUNT
|
||||
<< " morph targets are supported (count=" << count << ", offset=" << offset << ")";
|
||||
ASSERT_PRECONDITION(count + offset <= CONFIG_MAX_MORPH_TARGET_COUNT,
|
||||
"Only %d morph targets are supported (count=%d, offset=%d)",
|
||||
CONFIG_MAX_MORPH_TARGET_COUNT, count, offset);
|
||||
|
||||
MorphWeights const& morphWeights = mManager[instance].morphWeights;
|
||||
if (morphWeights.handle) {
|
||||
@@ -970,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;
|
||||
FILAMENT_CHECK_PRECONDITION(morphWeights.count == count)
|
||||
<< "Only " << morphWeights.count << " morph targets can be set (count=" << count
|
||||
<< ")";
|
||||
ASSERT_PRECONDITION(morphWeights.count == morphTargetBuffer->getCount(),
|
||||
"Only %d morph targets can be set (count=%d)",
|
||||
morphWeights.count, morphTargetBuffer->getCount());
|
||||
|
||||
Slice<MorphTargets>& morphTargets = getMorphTargets(instance, level);
|
||||
if (primitiveIndex < morphTargets.size()) {
|
||||
@@ -986,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;
|
||||
|
||||
@@ -310,10 +308,10 @@ FILAMENT_DOWNCAST(RenderableManager)
|
||||
|
||||
void FRenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) {
|
||||
if (instance) {
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
static_cast<Visibility const&>(mManager[instance].visibility).geometryType ==
|
||||
GeometryType::DYNAMIC)
|
||||
<< "This renderable has staticBounds enabled; its AABB cannot change.";
|
||||
ASSERT_PRECONDITION(
|
||||
static_cast<Visibility const&>(
|
||||
mManager[instance].visibility).geometryType == GeometryType::DYNAMIC,
|
||||
"This renderable has staticBounds enabled; its AABB cannot change.");
|
||||
mManager[instance].aabb = aabb;
|
||||
}
|
||||
}
|
||||
@@ -389,8 +387,9 @@ void FRenderableManager::setSkinning(Instance instance, bool enable) {
|
||||
if (instance) {
|
||||
Visibility& visibility = mManager[instance].visibility;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(visibility.geometryType != GeometryType::STATIC || !enable)
|
||||
<< "Skinning can't be used with STATIC geometry";
|
||||
ASSERT_PRECONDITION(
|
||||
visibility.geometryType != GeometryType::STATIC || !enable,
|
||||
"Skinning can't be used with STATIC geometry");
|
||||
|
||||
visibility.skinning = enable;
|
||||
}
|
||||
@@ -400,8 +399,9 @@ void FRenderableManager::setMorphing(Instance instance, bool enable) {
|
||||
if (instance) {
|
||||
Visibility& visibility = mManager[instance].visibility;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(visibility.geometryType != GeometryType::STATIC || !enable)
|
||||
<< "Morphing can't be used with STATIC geometry";
|
||||
ASSERT_PRECONDITION(
|
||||
visibility.geometryType != GeometryType::STATIC || !enable,
|
||||
"Morphing can't be used with STATIC geometry");
|
||||
|
||||
visibility.morphing = enable;
|
||||
}
|
||||
|
||||
@@ -97,10 +97,9 @@ void UTILS_NOINLINE FCamera::setCustomProjection(mat4 const& p,
|
||||
void UTILS_NOINLINE FCamera::setCustomEyeProjection(math::mat4 const* projection, size_t count,
|
||||
math::mat4 const& projectionForCulling, double near, double far) {
|
||||
const Engine::Config& config = mEngine.getConfig();
|
||||
FILAMENT_CHECK_PRECONDITION(count >= config.stereoscopicEyeCount)
|
||||
<< "All eye projections must be supplied together, count must be >= "
|
||||
"config.stereoscopicEyeCount ("
|
||||
<< config.stereoscopicEyeCount << ")";
|
||||
ASSERT_PRECONDITION(count >= config.stereoscopicEyeCount,
|
||||
"All eye projections must be supplied together, count must be >= "
|
||||
"config.stereoscopicEyeCount (%d)", config.stereoscopicEyeCount);
|
||||
for (int i = 0; i < config.stereoscopicEyeCount; i++) {
|
||||
mEyeProjection[i] = projection[i];
|
||||
}
|
||||
@@ -114,13 +113,14 @@ void UTILS_NOINLINE FCamera::setProjection(Camera::Projection projection,
|
||||
double bottom, double top,
|
||||
double near, double far) {
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!(left == right || bottom == top ||
|
||||
ASSERT_PRECONDITION(!(
|
||||
left == right ||
|
||||
bottom == top ||
|
||||
(projection == Projection::PERSPECTIVE && (near <= 0 || far <= near)) ||
|
||||
(projection == Projection::ORTHO && (near == far))))
|
||||
<< "Camera preconditions not met in setProjection("
|
||||
<< (projection == Camera::Projection::PERSPECTIVE ? "PERSPECTIVE" : "ORTHO") << ", "
|
||||
<< left << ", " << right << ", " << bottom << ", " << top << ", " << near << ", " << far
|
||||
<< ")";
|
||||
(projection == Projection::ORTHO && (near == far))),
|
||||
"Camera preconditions not met in setProjection(%s, %f, %f, %f, %f, %f, %f)",
|
||||
projection == Camera::Projection::PERSPECTIVE ? "PERSPECTIVE" : "ORTHO",
|
||||
left, right, bottom, top, near, far);
|
||||
|
||||
mat4 c, p;
|
||||
switch (projection) {
|
||||
@@ -193,9 +193,8 @@ math::mat4 FCamera::getCullingProjectionMatrix() const noexcept {
|
||||
|
||||
const math::mat4& FCamera::getUserProjectionMatrix(uint8_t eyeId) const {
|
||||
const Engine::Config& config = mEngine.getConfig();
|
||||
FILAMENT_CHECK_PRECONDITION(eyeId < config.stereoscopicEyeCount)
|
||||
<< "eyeId must be < config.stereoscopicEyeCount (" << config.stereoscopicEyeCount
|
||||
<< ")";
|
||||
ASSERT_PRECONDITION(eyeId < config.stereoscopicEyeCount,
|
||||
"eyeId must be < config.stereoscopicEyeCount (%d)", config.stereoscopicEyeCount);
|
||||
return mEyeProjection[eyeId];
|
||||
}
|
||||
|
||||
@@ -211,9 +210,8 @@ void UTILS_NOINLINE FCamera::setModelMatrix(const mat4& modelMatrix) noexcept {
|
||||
|
||||
void UTILS_NOINLINE FCamera::setEyeModelMatrix(uint8_t eyeId, math::mat4 const& model) {
|
||||
const Engine::Config& config = mEngine.getConfig();
|
||||
FILAMENT_CHECK_PRECONDITION(eyeId < config.stereoscopicEyeCount)
|
||||
<< "eyeId must be < config.stereoscopicEyeCount (" << config.stereoscopicEyeCount
|
||||
<< ")";
|
||||
ASSERT_PRECONDITION(eyeId < config.stereoscopicEyeCount,
|
||||
"eyeId must be < config.stereoscopicEyeCount (%d)", config.stereoscopicEyeCount);
|
||||
mEyeFromView[eyeId] = inverse(model);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "generated/resources/materials.h"
|
||||
|
||||
@@ -104,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);
|
||||
|
||||
@@ -159,8 +157,8 @@ FEngine* FEngine::getEngine(void* token) {
|
||||
|
||||
FEngine* instance = static_cast<FEngine*>(token);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(ThreadUtils::isThisThread(instance->mMainThreadId))
|
||||
<< "Engine::createAsync() and Engine::getEngine() must be called on the same thread.";
|
||||
ASSERT_PRECONDITION(ThreadUtils::isThisThread(instance->mMainThreadId),
|
||||
"Engine::createAsync() and Engine::getEngine() must be called on the same thread.");
|
||||
|
||||
// we use mResourceAllocator as a proxy for "am I already initialized"
|
||||
if (!instance->mResourceAllocator) {
|
||||
@@ -354,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);
|
||||
@@ -441,8 +438,8 @@ void FEngine::shutdown() {
|
||||
// by construction this should never be nullptr
|
||||
assert_invariant(mResourceAllocator);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(ThreadUtils::isThisThread(mMainThreadId))
|
||||
<< "Engine::shutdown() called from the wrong thread!";
|
||||
ASSERT_PRECONDITION(ThreadUtils::isThisThread(mMainThreadId),
|
||||
"Engine::shutdown() called from the wrong thread!");
|
||||
|
||||
#ifndef NDEBUG
|
||||
// print out some statistics about this run
|
||||
@@ -585,14 +582,14 @@ void FEngine::flush() {
|
||||
}
|
||||
|
||||
void FEngine::flushAndWait() {
|
||||
FILAMENT_CHECK_PRECONDITION(!mCommandBufferQueue.isPaused())
|
||||
<< "Cannot call flushAndWait() when rendering thread is paused!";
|
||||
ASSERT_PRECONDITION(!mCommandBufferQueue.isPaused(),
|
||||
"Cannot call flushAndWait() when rendering thread is paused!");
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
// first make sure we've not terminated filament
|
||||
FILAMENT_CHECK_PRECONDITION(!mCommandBufferQueue.isExitRequested())
|
||||
<< "calling Engine::flushAndWait() after Engine::shutdown()!";
|
||||
ASSERT_PRECONDITION(!mCommandBufferQueue.isExitRequested(),
|
||||
"calling Engine::flushAndWait() after Engine::shutdown()!");
|
||||
|
||||
#endif
|
||||
|
||||
@@ -610,10 +607,10 @@ void FEngine::flushAndWait() {
|
||||
// if the fence didn't trigger after 250ms, check that the command queue thread is still
|
||||
// running (otherwise indicating a precondition violation).
|
||||
if (UTILS_UNLIKELY(status == FenceStatus::TIMEOUT_EXPIRED)) {
|
||||
FILAMENT_CHECK_PRECONDITION(!mCommandBufferQueue.isExitRequested())
|
||||
<< "called Engine::shutdown() WHILE in Engine::flushAndWait()!";
|
||||
ASSERT_PRECONDITION(!mCommandBufferQueue.isExitRequested(),
|
||||
"called Engine::shutdown() WHILE in Engine::flushAndWait()!");
|
||||
tryCount--;
|
||||
FILAMENT_CHECK_POSTCONDITION(tryCount) << "flushAndWait() failed inexplicably after 2s";
|
||||
ASSERT_POSTCONDITION(tryCount, "flushAndWait() failed inexplicably after 2s");
|
||||
// if the thread is still running, maybe we just need to give it more time
|
||||
continue;
|
||||
}
|
||||
@@ -678,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);
|
||||
|
||||
@@ -1240,10 +1236,10 @@ Engine::FeatureLevel FEngine::getSupportedFeatureLevel() const noexcept {
|
||||
}
|
||||
|
||||
Engine::FeatureLevel FEngine::setActiveFeatureLevel(FeatureLevel featureLevel) {
|
||||
FILAMENT_CHECK_PRECONDITION(featureLevel <= getSupportedFeatureLevel())
|
||||
<< "Feature level " << (unsigned)featureLevel << " not supported";
|
||||
FILAMENT_CHECK_PRECONDITION(mActiveFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1)
|
||||
<< "Cannot adjust feature level beyond 0 at runtime";
|
||||
ASSERT_PRECONDITION(featureLevel <= getSupportedFeatureLevel(),
|
||||
"Feature level %u not supported", (unsigned)featureLevel);
|
||||
ASSERT_PRECONDITION(mActiveFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1,
|
||||
"Cannot adjust feature level beyond 0 at runtime");
|
||||
return (mActiveFeatureLevel = std::max(mActiveFeatureLevel, featureLevel));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -61,8 +61,7 @@ FenceStatus FFence::waitAndDestroy(FFence* fence, Mode mode) noexcept {
|
||||
|
||||
UTILS_NOINLINE
|
||||
FenceStatus FFence::wait(Mode mode, uint64_t timeout) noexcept {
|
||||
FILAMENT_CHECK_PRECONDITION(UTILS_HAS_THREADING || timeout == 0)
|
||||
<< "Non-zero timeout requires threads.";
|
||||
ASSERT_PRECONDITION(UTILS_HAS_THREADING || timeout == 0, "Non-zero timeout requires threads.");
|
||||
|
||||
FEngine& engine = mEngine;
|
||||
|
||||
|
||||
@@ -150,9 +150,9 @@ IndirectLight::Builder& IndirectLight::Builder::rotation(mat3f const& rotation)
|
||||
|
||||
IndirectLight* IndirectLight::Builder::build(Engine& engine) {
|
||||
if (mImpl->mReflectionsMap) {
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
mImpl->mReflectionsMap->getTarget() == Texture::Sampler::SAMPLER_CUBEMAP)
|
||||
<< "reflection map must a cubemap";
|
||||
ASSERT_PRECONDITION(
|
||||
mImpl->mReflectionsMap->getTarget() == Texture::Sampler::SAMPLER_CUBEMAP,
|
||||
"reflection map must a cubemap");
|
||||
|
||||
if constexpr (IBL_INTEGRATION == IBL_INTEGRATION_IMPORTANCE_SAMPLING) {
|
||||
mImpl->mReflectionsMap->generateMipmaps(engine);
|
||||
@@ -160,9 +160,9 @@ IndirectLight* IndirectLight::Builder::build(Engine& engine) {
|
||||
}
|
||||
|
||||
if (mImpl->mIrradianceMap) {
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
mImpl->mIrradianceMap->getTarget() == Texture::Sampler::SAMPLER_CUBEMAP)
|
||||
<< "irradiance map must a cubemap";
|
||||
ASSERT_PRECONDITION(
|
||||
mImpl->mIrradianceMap->getTarget() == Texture::Sampler::SAMPLER_CUBEMAP,
|
||||
"irradiance map must a cubemap");
|
||||
}
|
||||
|
||||
return downcast(engine).createIndirectLight(*this);
|
||||
|
||||
@@ -51,11 +51,12 @@ InstanceBuffer::Builder& InstanceBuffer::Builder::localTransforms(
|
||||
}
|
||||
|
||||
InstanceBuffer* InstanceBuffer::Builder::build(Engine& engine) {
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mInstanceCount >= 1) << "instanceCount must be >= 1.";
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mInstanceCount <= engine.getMaxAutomaticInstances())
|
||||
<< "instanceCount is " << mImpl->mInstanceCount
|
||||
<< ", but instance count is limited to Engine::getMaxAutomaticInstances() ("
|
||||
<< engine.getMaxAutomaticInstances() << ") instances when supplying transforms.";
|
||||
ASSERT_PRECONDITION(mImpl->mInstanceCount >= 1, "instanceCount must be >= 1.");
|
||||
ASSERT_PRECONDITION(mImpl->mInstanceCount <= engine.getMaxAutomaticInstances(),
|
||||
"instanceCount is %zu, but instance count is limited to "
|
||||
"Engine::getMaxAutomaticInstances() (%zu) instances when supplying transforms.",
|
||||
mImpl->mInstanceCount,
|
||||
engine.getMaxAutomaticInstances());
|
||||
return downcast(engine).createInstanceBuffer(*this);
|
||||
}
|
||||
|
||||
@@ -75,10 +76,12 @@ FInstanceBuffer::FInstanceBuffer(FEngine& engine, const Builder& builder) {
|
||||
|
||||
void FInstanceBuffer::setLocalTransforms(
|
||||
math::mat4f const* localTransforms, size_t count, size_t offset) {
|
||||
FILAMENT_CHECK_PRECONDITION(offset + count <= mInstanceCount)
|
||||
<< "setLocalTransforms overflow. InstanceBuffer has only " << mInstanceCount
|
||||
<< " instances, but trying to set " << count
|
||||
<< " transforms at offset " << offset << ".";
|
||||
ASSERT_PRECONDITION(offset + count <= mInstanceCount,
|
||||
"setLocalTransforms overflow. InstanceBuffer has only %zu instances, but trying to set "
|
||||
"%zu transforms at offset %zu.",
|
||||
mInstanceCount,
|
||||
count,
|
||||
offset);
|
||||
memcpy(mLocalTransforms.data() + offset, localTransforms, sizeof(math::mat4f) * count);
|
||||
}
|
||||
|
||||
|
||||
@@ -91,24 +91,23 @@ static std::unique_ptr<MaterialParser> createParser(Backend backend,
|
||||
}
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
materialResult != MaterialParser::ParseResult::ERROR_MISSING_BACKEND)
|
||||
<< "the material was not built for any of the " << backendToString(backend)
|
||||
<< " backend's supported shader languages (" << languageNames.c_str() << ")\n";
|
||||
ASSERT_PRECONDITION(materialResult != MaterialParser::ParseResult::ERROR_MISSING_BACKEND,
|
||||
"the material was not built for any of the %s backend's supported shader "
|
||||
"languages (%s)\n",
|
||||
backendToString(backend), languageNames.c_str());
|
||||
}
|
||||
|
||||
if (backend == Backend::NOOP) {
|
||||
return materialParser;
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(materialResult == MaterialParser::ParseResult::SUCCESS)
|
||||
<< "could not parse the material package";
|
||||
ASSERT_PRECONDITION(materialResult == MaterialParser::ParseResult::SUCCESS,
|
||||
"could not parse the material package");
|
||||
|
||||
uint32_t version = 0;
|
||||
materialParser->getMaterialVersion(&version);
|
||||
FILAMENT_CHECK_PRECONDITION(version == MATERIAL_VERSION)
|
||||
<< "Material version mismatch. Expected " << MATERIAL_VERSION << " but received "
|
||||
<< version << ".";
|
||||
ASSERT_PRECONDITION(version == MATERIAL_VERSION,
|
||||
"Material version mismatch. Expected %d but received %d.", MATERIAL_VERSION, version);
|
||||
|
||||
assert_invariant(backend != Backend::DEFAULT && "Default backend has not been resolved.");
|
||||
|
||||
@@ -145,7 +144,7 @@ Material::Builder& Material::Builder::package(const void* payload, size_t size)
|
||||
|
||||
template<typename T, typename>
|
||||
Material::Builder& Material::Builder::constant(const char* name, size_t nameLength, T value) {
|
||||
FILAMENT_CHECK_PRECONDITION(name != nullptr) << "name cannot be null";
|
||||
ASSERT_PRECONDITION(name != nullptr, "name cannot be null");
|
||||
mImpl->mConstantSpecializations[{name, nameLength}] = value;
|
||||
return *this;
|
||||
}
|
||||
@@ -386,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);
|
||||
}
|
||||
|
||||
@@ -523,11 +523,10 @@ Program FMaterial::getProgramWithVariants(
|
||||
UTILS_UNUSED_IN_RELEASE bool const vsOK = mMaterialParser->getShader(vsBuilder, sm,
|
||||
vertexVariant, ShaderStage::VERTEX);
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(isNoop || (vsOK && !vsBuilder.empty()))
|
||||
<< "The material '" << mName.c_str()
|
||||
<< "' has not been compiled to include the required GLSL or SPIR-V chunks for the "
|
||||
"vertex shader (variant="
|
||||
<< variant.key << ", filtered=" << vertexVariant.key << ").";
|
||||
ASSERT_POSTCONDITION(isNoop || (vsOK && !vsBuilder.empty()),
|
||||
"The material '%s' has not been compiled to include the required "
|
||||
"GLSL or SPIR-V chunks for the vertex shader (variant=0x%x, filtered=0x%x).",
|
||||
mName.c_str(), variant.key, vertexVariant.key);
|
||||
|
||||
/*
|
||||
* Fragment shader
|
||||
@@ -538,11 +537,10 @@ Program FMaterial::getProgramWithVariants(
|
||||
UTILS_UNUSED_IN_RELEASE bool const fsOK = mMaterialParser->getShader(fsBuilder, sm,
|
||||
fragmentVariant, ShaderStage::FRAGMENT);
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(isNoop || (fsOK && !fsBuilder.empty()))
|
||||
<< "The material '" << mName.c_str()
|
||||
<< "' has not been compiled to include the required GLSL or SPIR-V chunks for the "
|
||||
"fragment shader (variant="
|
||||
<< variant.key << ", filtered=" << ").";
|
||||
ASSERT_POSTCONDITION(isNoop || (fsOK && !fsBuilder.empty()),
|
||||
"The material '%s' has not been compiled to include the required "
|
||||
"GLSL or SPIR-V chunks for the fragment shader (variant=0x%x, filtered=0x%x).",
|
||||
mName.c_str(), variant.key, fragmentVariant.key);
|
||||
|
||||
Program program;
|
||||
program.shader(ShaderStage::VERTEX, vsBuilder.data(), vsBuilder.size())
|
||||
@@ -907,29 +905,26 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Material::Builde
|
||||
for (auto const& [name, value] : builder->mConstantSpecializations) {
|
||||
std::string_view const key{ name.data(), name.size() };
|
||||
auto pos = mSpecializationConstantsNameToIndex.find(key);
|
||||
FILAMENT_CHECK_PRECONDITION(pos != mSpecializationConstantsNameToIndex.end())
|
||||
<< "The material " << mName.c_str_safe()
|
||||
<< " does not have a constant parameter named " << name.c_str() << ".";
|
||||
ASSERT_PRECONDITION(pos != mSpecializationConstantsNameToIndex.end(),
|
||||
"The material %s does not have a constant parameter named %s.",
|
||||
mName.c_str_safe(), name.c_str());
|
||||
const char* const types[3] = {"an int", "a float", "a bool"};
|
||||
const char* const errorMessage =
|
||||
"The constant parameter %s on material %s is of type %s, but %s was "
|
||||
"provided.";
|
||||
auto& constant = mMaterialConstants[pos->second];
|
||||
switch (constant.type) {
|
||||
case ConstantType::INT:
|
||||
FILAMENT_CHECK_PRECONDITION(std::holds_alternative<int32_t>(value))
|
||||
<< "The constant parameter " << name.c_str() << " on material "
|
||||
<< mName.c_str_safe() << " is of type int, but " << types[value.index()]
|
||||
<< " was provided.";
|
||||
ASSERT_PRECONDITION(std::holds_alternative<int32_t>(value), errorMessage,
|
||||
name.c_str(), mName.c_str_safe(), "int", types[value.index()]);
|
||||
break;
|
||||
case ConstantType::FLOAT:
|
||||
FILAMENT_CHECK_PRECONDITION(std::holds_alternative<float>(value))
|
||||
<< "The constant parameter " << name.c_str() << " on material "
|
||||
<< mName.c_str_safe() << " is of type float, but " << types[value.index()]
|
||||
<< " was provided.";
|
||||
ASSERT_PRECONDITION(std::holds_alternative<float>(value), errorMessage,
|
||||
name.c_str(), mName.c_str_safe(), "float", types[value.index()]);
|
||||
break;
|
||||
case ConstantType::BOOL:
|
||||
FILAMENT_CHECK_PRECONDITION(std::holds_alternative<bool>(value))
|
||||
<< "The constant parameter " << name.c_str() << " on material "
|
||||
<< mName.c_str_safe() << " is of type bool, but " << types[value.index()]
|
||||
<< " was provided.";
|
||||
ASSERT_PRECONDITION(std::holds_alternative<bool>(value), errorMessage,
|
||||
name.c_str(), mName.c_str_safe(), "bool", types[value.index()]);
|
||||
break;
|
||||
}
|
||||
uint32_t const index = pos->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
|
||||
|
||||
@@ -148,14 +148,15 @@ void FMorphTargetBuffer::terminate(FEngine& engine) {
|
||||
|
||||
void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t targetIndex,
|
||||
math::float3 const* positions, size_t count, size_t offset) {
|
||||
FILAMENT_CHECK_PRECONDITION(offset + count <= mVertexCount)
|
||||
<< "MorphTargetBuffer (size=" << (unsigned)mVertexCount
|
||||
<< ") overflow (count=" << (unsigned)count << ", offset=" << (unsigned)offset << ")";
|
||||
|
||||
auto size = getSize<VertexAttribute::POSITION>(count);
|
||||
ASSERT_PRECONDITION(offset + count <= mVertexCount,
|
||||
"MorphTargetBuffer (size=%lu) overflow (count=%u, offset=%u)",
|
||||
(unsigned)mVertexCount, (unsigned)count, (unsigned)offset);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(targetIndex < mCount)
|
||||
<< targetIndex << " target index must be < " << mCount;
|
||||
auto size = getSize<VertexAttribute::POSITION>(mVertexCount);
|
||||
|
||||
ASSERT_PRECONDITION(targetIndex < mCount,
|
||||
"%d target index must be < %d", targetIndex, mCount);
|
||||
|
||||
// We could use a pool instead of malloc() directly.
|
||||
auto* out = (float4*) malloc(size);
|
||||
@@ -171,14 +172,15 @@ void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t targetIndex,
|
||||
|
||||
void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t targetIndex,
|
||||
math::float4 const* positions, size_t count, size_t offset) {
|
||||
FILAMENT_CHECK_PRECONDITION(offset + count <= mVertexCount)
|
||||
<< "MorphTargetBuffer (size=" << (unsigned)mVertexCount
|
||||
<< ") overflow (count=" << (unsigned)count << ", offset=" << (unsigned)offset << ")";
|
||||
|
||||
auto size = getSize<VertexAttribute::POSITION>(count);
|
||||
ASSERT_PRECONDITION(offset + count <= mVertexCount,
|
||||
"MorphTargetBuffer (size=%lu) overflow (count=%u, offset=%u)",
|
||||
(unsigned)mVertexCount, (unsigned)count, (unsigned)offset);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(targetIndex < mCount)
|
||||
<< targetIndex << " target index must be < " << mCount;
|
||||
auto size = getSize<VertexAttribute::POSITION>(mVertexCount);
|
||||
|
||||
ASSERT_PRECONDITION(targetIndex < mCount,
|
||||
"%d target index must be < %d", targetIndex, mCount);
|
||||
|
||||
// We could use a pool instead of malloc() directly.
|
||||
auto* out = (float4*) malloc(size);
|
||||
@@ -193,14 +195,15 @@ void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t targetIndex,
|
||||
|
||||
void FMorphTargetBuffer::setTangentsAt(FEngine& engine, size_t targetIndex,
|
||||
math::short4 const* tangents, size_t count, size_t offset) {
|
||||
FILAMENT_CHECK_PRECONDITION(offset + count <= mVertexCount)
|
||||
<< "MorphTargetBuffer (size=" << (unsigned)mVertexCount
|
||||
<< ") overflow (count=" << (unsigned)count << ", offset=" << (unsigned)offset << ")";
|
||||
|
||||
const auto size = getSize<VertexAttribute::TANGENTS>(count);
|
||||
ASSERT_PRECONDITION(offset + count <= mVertexCount,
|
||||
"MorphTargetBuffer (size=%lu) overflow (count=%u, offset=%u)",
|
||||
(unsigned)mVertexCount, (unsigned)count, (unsigned)offset);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(targetIndex < mCount)
|
||||
<< targetIndex << " target index must be < " << mCount;
|
||||
const auto size = getSize<VertexAttribute::TANGENTS>(mVertexCount);
|
||||
|
||||
ASSERT_PRECONDITION(targetIndex < mCount,
|
||||
"%d target index must be < %d", targetIndex, mCount);
|
||||
|
||||
// We could use a pool instead of malloc() directly.
|
||||
auto* out = (short4*) malloc(size);
|
||||
@@ -223,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
|
||||
|
||||
@@ -71,20 +71,20 @@ RenderTarget* RenderTarget::Builder::build(Engine& engine) {
|
||||
const FRenderTarget::Attachment& depth = mImpl->mAttachments[(size_t)AttachmentPoint::DEPTH];
|
||||
|
||||
if (color.texture) {
|
||||
FILAMENT_CHECK_PRECONDITION(color.texture->getUsage() & TextureUsage::COLOR_ATTACHMENT)
|
||||
<< "Texture usage must contain COLOR_ATTACHMENT";
|
||||
ASSERT_PRECONDITION(color.texture->getUsage() & TextureUsage::COLOR_ATTACHMENT,
|
||||
"Texture usage must contain COLOR_ATTACHMENT");
|
||||
}
|
||||
|
||||
if (depth.texture) {
|
||||
FILAMENT_CHECK_PRECONDITION(depth.texture->getUsage() & TextureUsage::DEPTH_ATTACHMENT)
|
||||
<< "Texture usage must contain DEPTH_ATTACHMENT";
|
||||
ASSERT_PRECONDITION(depth.texture->getUsage() & TextureUsage::DEPTH_ATTACHMENT,
|
||||
"Texture usage must contain DEPTH_ATTACHMENT");
|
||||
}
|
||||
|
||||
const size_t maxDrawBuffers = downcast(engine).getDriverApi().getMaxDrawBuffers();
|
||||
for (size_t i = maxDrawBuffers; i < MAX_SUPPORTED_COLOR_ATTACHMENTS_COUNT; i++) {
|
||||
FILAMENT_CHECK_PRECONDITION(!mImpl->mAttachments[i].texture)
|
||||
<< "Only " << maxDrawBuffers << " color attachments are supported, but COLOR" << i
|
||||
<< " attachment is set";
|
||||
ASSERT_PRECONDITION(!mImpl->mAttachments[i].texture,
|
||||
"Only %u color attachments are supported, but COLOR%u attachment is set",
|
||||
maxDrawBuffers, i);
|
||||
}
|
||||
|
||||
uint32_t minWidth = std::numeric_limits<uint32_t>::max();
|
||||
@@ -102,8 +102,8 @@ RenderTarget* RenderTarget::Builder::build(Engine& engine) {
|
||||
}
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(minWidth == maxWidth && minHeight == maxHeight)
|
||||
<< "All attachments dimensions must match";
|
||||
ASSERT_PRECONDITION(minWidth == maxWidth && minHeight == maxHeight,
|
||||
"All attachments dimensions must match");
|
||||
|
||||
mImpl->mWidth = minWidth;
|
||||
mImpl->mHeight = minHeight;
|
||||
|
||||
@@ -387,8 +387,8 @@ void FRenderer::readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, u
|
||||
PixelBufferDescriptor&& buffer) {
|
||||
#ifndef NDEBUG
|
||||
const bool withinFrame = mSwapChain != nullptr;
|
||||
FILAMENT_CHECK_PRECONDITION(withinFrame) << "readPixels() on a SwapChain must be called after"
|
||||
" beginFrame() and before endFrame().";
|
||||
ASSERT_PRECONDITION(withinFrame, "readPixels() on a SwapChain must be called after"
|
||||
" beginFrame() and before endFrame().");
|
||||
#endif
|
||||
RendererUtils::readPixels(mEngine.getDriverApi(), mRenderTargetHandle,
|
||||
xoffset, yoffset, width, height, std::move(buffer));
|
||||
@@ -454,8 +454,8 @@ void FRenderer::renderStandaloneView(FView const* view) {
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(view->getRenderTarget())
|
||||
<< "View \"" << view->getName() << "\" must have a RenderTarget associated";
|
||||
ASSERT_PRECONDITION(view->getRenderTarget(),
|
||||
"View \"%s\" must have a RenderTarget associated", view->getName());
|
||||
|
||||
if (UTILS_LIKELY(view->getScene())) {
|
||||
mPreviousRenderTargets.clear();
|
||||
@@ -1230,9 +1230,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
|
||||
if (UTILS_UNLIKELY(outputIsSwapChain && view.isStencilBufferEnabled())) {
|
||||
assert_invariant(mSwapChain);
|
||||
FILAMENT_CHECK_PRECONDITION(mSwapChain->hasStencilBuffer())
|
||||
<< "View has stencil buffer enabled, but SwapChain does not have "
|
||||
"SwapChain::CONFIG_HAS_STENCIL_BUFFER flag set.";
|
||||
ASSERT_PRECONDITION(mSwapChain->hasStencilBuffer(),
|
||||
"View has stencil buffer enabled, but SwapChain does not have "
|
||||
"SwapChain::CONFIG_HAS_STENCIL_BUFFER flag set.");
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(engine.debug.shadowmap.display_shadow_texture)) {
|
||||
|
||||
@@ -96,20 +96,20 @@ void FSkinningBuffer::terminate(FEngine& engine) {
|
||||
|
||||
void FSkinningBuffer::setBones(FEngine& engine,
|
||||
RenderableManager::Bone const* transforms, size_t count, size_t offset) {
|
||||
FILAMENT_CHECK_PRECONDITION((offset + count) <= mBoneCount)
|
||||
<< "SkinningBuffer (size=" << (unsigned)mBoneCount
|
||||
<< ") overflow (boneCount=" << (unsigned)count << ", offset=" << (unsigned)offset
|
||||
<< ")";
|
||||
|
||||
ASSERT_PRECONDITION((offset + count) <= mBoneCount,
|
||||
"SkinningBuffer (size=%lu) overflow (boneCount=%u, offset=%u)",
|
||||
(unsigned)mBoneCount, (unsigned)count, (unsigned)offset);
|
||||
|
||||
setBones(engine, mHandle, transforms, count, offset);
|
||||
}
|
||||
|
||||
void FSkinningBuffer::setBones(FEngine& engine,
|
||||
math::mat4f const* transforms, size_t count, size_t offset) {
|
||||
FILAMENT_CHECK_PRECONDITION((offset + count) <= mBoneCount)
|
||||
<< "SkinningBuffer (size=" << (unsigned)mBoneCount
|
||||
<< ") overflow (boneCount=" << (unsigned)count << ", offset=" << (unsigned)offset
|
||||
<< ")";
|
||||
|
||||
ASSERT_PRECONDITION((offset + count) <= mBoneCount,
|
||||
"SkinningBuffer (size=%lu) overflow (boneCount=%u, offset=%u)",
|
||||
(unsigned)mBoneCount, (unsigned)count, (unsigned)offset);
|
||||
|
||||
setBones(engine, mHandle, transforms, count, offset);
|
||||
}
|
||||
|
||||
@@ -85,8 +85,8 @@ Skybox::Builder& Skybox::Builder::showSun(bool show) noexcept {
|
||||
Skybox* Skybox::Builder::build(Engine& engine) {
|
||||
FTexture* cubemap = downcast(mImpl->mEnvironmentMap);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!cubemap || cubemap->isCubemap())
|
||||
<< "environment maps must be a cubemap";
|
||||
ASSERT_PRECONDITION(!cubemap || cubemap->isCubemap(),
|
||||
"environment maps must be a cubemap");
|
||||
|
||||
return downcast(engine).createSkybox(*this);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -141,16 +141,15 @@ Texture::Builder& Texture::Builder::swizzle(Swizzle r, Swizzle g, Swizzle b, Swi
|
||||
}
|
||||
|
||||
Texture* Texture::Builder::build(Engine& engine) {
|
||||
FILAMENT_CHECK_PRECONDITION(Texture::isTextureFormatSupported(engine, mImpl->mFormat))
|
||||
<< "Texture format " << uint16_t(mImpl->mFormat) << " not supported on this platform";
|
||||
ASSERT_PRECONDITION(Texture::isTextureFormatSupported(engine, mImpl->mFormat),
|
||||
"Texture format %u not supported on this platform", mImpl->mFormat);
|
||||
|
||||
const bool isProtectedTexturesSupported =
|
||||
downcast(engine).getDriverApi().isProtectedTexturesSupported();
|
||||
const bool useProtectedMemory = bool(mImpl->mUsage & TextureUsage::PROTECTED);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
(isProtectedTexturesSupported && useProtectedMemory) || !useProtectedMemory)
|
||||
<< "Texture is PROTECTED but protected textures are not supported";
|
||||
ASSERT_PRECONDITION((isProtectedTexturesSupported && useProtectedMemory) || !useProtectedMemory,
|
||||
"Texture is PROTECTED but protected textures are not supported");
|
||||
|
||||
uint8_t maxLevelCount;
|
||||
switch (mImpl->mTarget) {
|
||||
@@ -187,7 +186,7 @@ Texture* Texture::Builder::build(Engine& engine) {
|
||||
const bool imported = mImpl->mImportedId;
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
FILAMENT_CHECK_PRECONDITION(!swizzled) << "WebGL does not support texture swizzling.";
|
||||
ASSERT_PRECONDITION(!swizzled, "WebGL does not support texture swizzling.");
|
||||
#endif
|
||||
|
||||
auto validateSamplerType = [&engine = downcast(engine)](SamplerType sampler) -> bool {
|
||||
@@ -204,15 +203,15 @@ Texture* Texture::Builder::build(Engine& engine) {
|
||||
}
|
||||
};
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(validateSamplerType(mImpl->mTarget))
|
||||
<< "SamplerType " << uint8_t(mImpl->mTarget) << " not support at feature level "
|
||||
<< uint8_t(engine.getActiveFeatureLevel());
|
||||
ASSERT_PRECONDITION(validateSamplerType(mImpl->mTarget),
|
||||
"SamplerType %u not support at feature level %u",
|
||||
mImpl->mTarget, engine.getActiveFeatureLevel());
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION((swizzled && sampleable) || !swizzled)
|
||||
<< "Swizzled texture must be SAMPLEABLE";
|
||||
ASSERT_PRECONDITION((swizzled && sampleable) || !swizzled,
|
||||
"Swizzled texture must be SAMPLEABLE");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION((imported && sampleable) || !imported)
|
||||
<< "Imported texture must be SAMPLEABLE";
|
||||
ASSERT_PRECONDITION((imported && sampleable) || !imported,
|
||||
"Imported texture must be SAMPLEABLE");
|
||||
|
||||
return downcast(engine).createTexture(*this);
|
||||
}
|
||||
@@ -269,42 +268,41 @@ void FTexture::setImage(FEngine& engine, size_t level,
|
||||
FTexture::PixelBufferDescriptor&& p) const {
|
||||
|
||||
if (UTILS_UNLIKELY(!engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_1))) {
|
||||
FILAMENT_CHECK_PRECONDITION(p.stride == 0 || p.stride == width)
|
||||
<< "PixelBufferDescriptor stride must be 0 (or width) at FEATURE_LEVEL_0";
|
||||
ASSERT_PRECONDITION(p.stride == 0 || p.stride == width,
|
||||
"PixelBufferDescriptor stride must be 0 (or width) at FEATURE_LEVEL_0");
|
||||
}
|
||||
|
||||
// this should have been validated already
|
||||
assert_invariant(isTextureFormatSupported(engine, mFormat));
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(p.type == PixelDataType::COMPRESSED ||
|
||||
validatePixelFormatAndType(mFormat, p.format, p.type))
|
||||
<< "The combination of internal format=" << unsigned(mFormat)
|
||||
<< " and {format=" << unsigned(p.format) << ", type=" << unsigned(p.type)
|
||||
<< "} is not supported.";
|
||||
ASSERT_PRECONDITION(p.type == PixelDataType::COMPRESSED ||
|
||||
validatePixelFormatAndType(mFormat, p.format, p.type),
|
||||
"The combination of internal format=%u and {format=%u, type=%u} is not supported.",
|
||||
unsigned(mFormat), unsigned(p.format), unsigned(p.type));
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!mStream) << "setImage() called on a Stream texture.";
|
||||
ASSERT_PRECONDITION(!mStream, "setImage() called on a Stream texture.");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(level < mLevelCount)
|
||||
<< "level=" << unsigned(level) << " is >= to levelCount=" << unsigned(mLevelCount)
|
||||
<< ".";
|
||||
ASSERT_PRECONDITION(level < mLevelCount,
|
||||
"level=%u is >= to levelCount=%u.", unsigned(level), unsigned(mLevelCount));
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mTarget != SamplerType::SAMPLER_EXTERNAL)
|
||||
<< "Texture SamplerType::SAMPLER_EXTERNAL not supported for this operation.";
|
||||
ASSERT_PRECONDITION(mTarget != SamplerType::SAMPLER_EXTERNAL,
|
||||
"Texture SamplerType::SAMPLER_EXTERNAL not supported for this operation.",
|
||||
unsigned(mTarget));
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mSampleCount <= 1) << "Operation not supported with multisample ("
|
||||
<< unsigned(mSampleCount) << ") texture.";
|
||||
ASSERT_PRECONDITION(mSampleCount <= 1,
|
||||
"Operation not supported with multisample (%u) texture.", unsigned(mSampleCount));
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(xoffset + width <= valueForLevel(level, mWidth))
|
||||
<< "xoffset (" << unsigned(xoffset) << ") + width (" << unsigned(width)
|
||||
<< ") > texture width (" << valueForLevel(level, mWidth) << ") at level ("
|
||||
<< unsigned(level) << ")";
|
||||
ASSERT_PRECONDITION(xoffset + width <= valueForLevel(level, mWidth),
|
||||
"xoffset (%u) + width (%u) > texture width (%u) at level (%u)",
|
||||
unsigned(xoffset), unsigned(width), unsigned(valueForLevel(level, mWidth)),
|
||||
unsigned(level));
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(yoffset + height <= valueForLevel(level, mHeight))
|
||||
<< "yoffset (" << unsigned(yoffset) << ") + height (" << unsigned(height)
|
||||
<< ") > texture height (" << valueForLevel(level, mHeight) << ") at level ("
|
||||
<< unsigned(level) << ")";
|
||||
ASSERT_PRECONDITION(yoffset + height <= valueForLevel(level, mHeight),
|
||||
"yoffset (%u) + height (%u) > texture height (%u) at level (%u)",
|
||||
unsigned(yoffset), unsigned(height), unsigned(valueForLevel(level, mHeight)),
|
||||
unsigned(level));
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(p.buffer) << "Data buffer is nullptr.";
|
||||
ASSERT_PRECONDITION(p.buffer, "Data buffer is nullptr.");
|
||||
|
||||
uint32_t effectiveTextureDepthOrLayers;
|
||||
switch (mTarget) {
|
||||
@@ -328,10 +326,9 @@ void FTexture::setImage(FEngine& engine, size_t level,
|
||||
break;
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(zoffset + depth <= effectiveTextureDepthOrLayers)
|
||||
<< "zoffset (" << unsigned(zoffset) << ") + depth (" << unsigned(depth)
|
||||
<< ") > texture depth (" << effectiveTextureDepthOrLayers << ") at level ("
|
||||
<< unsigned(level) << ")";
|
||||
ASSERT_PRECONDITION(zoffset + depth <= effectiveTextureDepthOrLayers,
|
||||
"zoffset (%u) + depth (%u) > texture depth (%u) at level (%u)",
|
||||
unsigned(zoffset), unsigned(depth), effectiveTextureDepthOrLayers, unsigned(level));
|
||||
|
||||
using PBD = PixelBufferDescriptor;
|
||||
size_t const stride = p.stride ? p.stride : width;
|
||||
@@ -339,13 +336,12 @@ void FTexture::setImage(FEngine& engine, size_t level,
|
||||
size_t const bpr = PBD::computeDataSize(p.format, p.type, stride, 1, p.alignment);
|
||||
size_t const bpl = bpr * height; // TODO: PBD should have a "layer stride"
|
||||
// TODO: PBD should have a p.depth (# layers to skip)
|
||||
FILAMENT_CHECK_PRECONDITION(bpp * p.left + bpr * p.top + bpl * (0 + depth) <= p.size)
|
||||
<< "buffer overflow: (size=" << size_t(p.size) << ", stride=" << size_t(p.stride)
|
||||
<< ", left=" << unsigned(p.left) << ", top=" << unsigned(p.top)
|
||||
<< ") smaller than specified region "
|
||||
"{{"
|
||||
<< unsigned(xoffset) << "," << unsigned(yoffset) << "," << unsigned(zoffset) << "},{"
|
||||
<< unsigned(width) << "," << unsigned(height) << "," << unsigned(depth) << ")}}";
|
||||
ASSERT_PRECONDITION(bpp * p.left + bpr * p.top + bpl * (0 + depth) <= p.size,
|
||||
"buffer overflow: (size=%lu, stride=%lu, left=%u, top=%u) smaller than specified region "
|
||||
"{{%u,%u,%u},{%u,%u,%u)}}",
|
||||
size_t(p.size), size_t(p.stride), unsigned(p.left), unsigned(p.top),
|
||||
unsigned(xoffset), unsigned(yoffset), unsigned(zoffset),
|
||||
unsigned(width), unsigned(height), unsigned(depth));
|
||||
|
||||
engine.getDriverApi().update3DImage(mHandle,
|
||||
uint8_t(level), xoffset, yoffset, zoffset, width, height, depth, std::move(p));
|
||||
@@ -371,23 +367,20 @@ void FTexture::setImage(FEngine& engine, size_t level,
|
||||
// this should have been validated already
|
||||
assert_invariant(isTextureFormatSupported(engine, mFormat));
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(buffer.type == PixelDataType::COMPRESSED ||
|
||||
validatePixelFormatAndType(mFormat, buffer.format, buffer.type))
|
||||
<< "The combination of internal format=" << unsigned(mFormat)
|
||||
<< " and {format=" << unsigned(buffer.format) << ", type=" << unsigned(buffer.type)
|
||||
<< "} is not supported.";
|
||||
ASSERT_PRECONDITION(buffer.type == PixelDataType::COMPRESSED ||
|
||||
validatePixelFormatAndType(mFormat, buffer.format, buffer.type),
|
||||
"The combination of internal format=%u and {format=%u, type=%u} is not supported.",
|
||||
unsigned(mFormat), unsigned(buffer.format), unsigned(buffer.type));
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!mStream) << "setImage() called on a Stream texture.";
|
||||
ASSERT_PRECONDITION(!mStream, "setImage() called on a Stream texture.");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(level < mLevelCount)
|
||||
<< "level=" << unsigned(level) << " is >= to levelCount=" << unsigned(mLevelCount)
|
||||
<< ".";
|
||||
ASSERT_PRECONDITION(level < mLevelCount,
|
||||
"level=%u is >= to levelCount=%u.", unsigned(level), unsigned(mLevelCount));
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(validateTarget(mTarget))
|
||||
<< "Texture Sampler type (" << unsigned(mTarget)
|
||||
<< ") not supported for this operation.";
|
||||
ASSERT_PRECONDITION(validateTarget(mTarget),
|
||||
"Texture Sampler type (%u) not supported for this operation.", unsigned(mTarget));
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(buffer.buffer) << "Data buffer is nullptr.";
|
||||
ASSERT_PRECONDITION(buffer.buffer, "Data buffer is nullptr.");
|
||||
|
||||
auto w = std::max(1u, mWidth >> level);
|
||||
auto h = std::max(1u, mHeight >> level);
|
||||
@@ -437,8 +430,8 @@ void FTexture::setExternalImage(FEngine& engine, void* image, size_t plane) noex
|
||||
|
||||
void FTexture::setExternalStream(FEngine& engine, FStream* stream) noexcept {
|
||||
if (stream) {
|
||||
FILAMENT_CHECK_PRECONDITION(mTarget == Sampler::SAMPLER_EXTERNAL)
|
||||
<< "Texture target must be SAMPLER_EXTERNAL";
|
||||
ASSERT_PRECONDITION(mTarget == Sampler::SAMPLER_EXTERNAL,
|
||||
"Texture target must be SAMPLER_EXTERNAL");
|
||||
|
||||
mStream = stream;
|
||||
engine.getDriverApi().setExternalStream(mHandle, stream->getHandle());
|
||||
@@ -449,15 +442,15 @@ void FTexture::setExternalStream(FEngine& engine, FStream* stream) noexcept {
|
||||
}
|
||||
|
||||
void FTexture::generateMipmaps(FEngine& engine) const noexcept {
|
||||
FILAMENT_CHECK_PRECONDITION(mTarget != SamplerType::SAMPLER_EXTERNAL)
|
||||
<< "External Textures are not mipmappable.";
|
||||
ASSERT_PRECONDITION(mTarget != SamplerType::SAMPLER_EXTERNAL,
|
||||
"External Textures are not mipmappable.");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mTarget != SamplerType::SAMPLER_3D)
|
||||
<< "3D Textures are not mipmappable.";
|
||||
ASSERT_PRECONDITION(mTarget != SamplerType::SAMPLER_3D,
|
||||
"3D Textures are not mipmappable.");
|
||||
|
||||
const bool formatMipmappable = engine.getDriverApi().isTextureFormatMipmappable(mFormat);
|
||||
FILAMENT_CHECK_PRECONDITION(formatMipmappable)
|
||||
<< "Texture format " << (unsigned)mFormat << " is not mipmappable.";
|
||||
ASSERT_PRECONDITION(formatMipmappable,
|
||||
"Texture format %u is not mipmappable.", (unsigned)mFormat);
|
||||
|
||||
if (mLevelCount < 2 || (mWidth == 1 && mHeight == 1)) {
|
||||
return;
|
||||
@@ -500,21 +493,24 @@ void FTexture::generatePrefilterMipmap(FEngine& engine,
|
||||
|
||||
/* validate input data */
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
buffer.format == PixelDataFormat::RGB || buffer.format == PixelDataFormat::RGBA)
|
||||
<< "input data format must be RGB or RGBA";
|
||||
ASSERT_PRECONDITION(buffer.format == PixelDataFormat::RGB ||
|
||||
buffer.format == PixelDataFormat::RGBA,
|
||||
"input data format must be RGB or RGBA");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(buffer.type == PixelDataType::FLOAT ||
|
||||
ASSERT_PRECONDITION(
|
||||
buffer.type == PixelDataType::FLOAT ||
|
||||
buffer.type == PixelDataType::HALF ||
|
||||
buffer.type == PixelDataType::UINT_10F_11F_11F_REV)
|
||||
<< "input data type must be FLOAT, HALF or UINT_10F_11F_11F_REV";
|
||||
buffer.type == PixelDataType::UINT_10F_11F_11F_REV,
|
||||
"input data type must be FLOAT, HALF or UINT_10F_11F_11F_REV");
|
||||
|
||||
/* validate texture */
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!(size & (size - 1)))
|
||||
<< "input data cubemap dimensions must be a power-of-two";
|
||||
ASSERT_PRECONDITION(!(size & (size-1)),
|
||||
"input data cubemap dimensions must be a power-of-two");
|
||||
|
||||
ASSERT_PRECONDITION(!isCompressed(),
|
||||
"reflections texture cannot be compressed");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!isCompressed()) << "reflections texture cannot be compressed";
|
||||
|
||||
PrefilterOptions const defaultOptions;
|
||||
options = options ? options : &defaultOptions;
|
||||
|
||||
@@ -148,10 +148,10 @@ VertexBuffer::Builder& VertexBuffer::Builder::advancedSkinning(bool enabled) noe
|
||||
}
|
||||
|
||||
VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mVertexCount > 0) << "vertexCount cannot be 0";
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mBufferCount > 0) << "bufferCount cannot be 0";
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mBufferCount <= MAX_VERTEX_BUFFER_COUNT)
|
||||
<< "bufferCount cannot be more than " << MAX_VERTEX_BUFFER_COUNT;
|
||||
ASSERT_PRECONDITION(mImpl->mVertexCount > 0, "vertexCount cannot be 0");
|
||||
ASSERT_PRECONDITION(mImpl->mBufferCount > 0, "bufferCount cannot be 0");
|
||||
ASSERT_PRECONDITION(mImpl->mBufferCount <= MAX_VERTEX_BUFFER_COUNT,
|
||||
"bufferCount cannot be more than %d", MAX_VERTEX_BUFFER_COUNT);
|
||||
|
||||
// Next we check if any unused buffer slots have been allocated. This helps prevent errors
|
||||
// because uploading to an unused slot can trigger undefined behavior in the backend.
|
||||
@@ -164,8 +164,8 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
|
||||
attributedBuffers.set(attributes[j].buffer);
|
||||
|
||||
if (engine.getActiveFeatureLevel() == backend::FeatureLevel::FEATURE_LEVEL_0) {
|
||||
FILAMENT_CHECK_PRECONDITION(!(attributes[j].flags & Attribute::FLAG_INTEGER_TARGET))
|
||||
<< "Attribute::FLAG_INTEGER_TARGET not supported at FEATURE_LEVEL_0";
|
||||
ASSERT_PRECONDITION(!(attributes[j].flags & Attribute::FLAG_INTEGER_TARGET),
|
||||
"Attribute::FLAG_INTEGER_TARGET not supported at FEATURE_LEVEL_0");
|
||||
}
|
||||
|
||||
// also checks that we don't use an invalid type with integer attributes
|
||||
@@ -180,23 +180,23 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
|
||||
(1 << (int)ET::HALF2) |
|
||||
(1 << (int)ET::HALF3) |
|
||||
(1 << (int)ET::HALF4);
|
||||
FILAMENT_CHECK_PRECONDITION(!(invalidIntegerTypes & (1 << (int)attributes[j].type)))
|
||||
<< "invalid integer vertex attribute type " << int(attributes[j].type);
|
||||
ASSERT_PRECONDITION(!(invalidIntegerTypes & (1 << (int)attributes[j].type)),
|
||||
"invalid integer vertex attribute type %d", attributes[j].type);
|
||||
}
|
||||
});
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(attributedBuffers.count() == mImpl->mBufferCount)
|
||||
<< "At least one buffer slot was never assigned to an attribute.";
|
||||
ASSERT_PRECONDITION(attributedBuffers.count() == mImpl->mBufferCount,
|
||||
"At least one buffer slot was never assigned to an attribute.");
|
||||
|
||||
if (mImpl->mAdvancedSkinningEnabled) {
|
||||
FILAMENT_CHECK_PRECONDITION(!mImpl->mDeclaredAttributes[VertexAttribute::BONE_INDICES])
|
||||
<< "Vertex buffer attribute BONE_INDICES is already defined, "
|
||||
"no advanced skinning is allowed";
|
||||
FILAMENT_CHECK_PRECONDITION(!mImpl->mDeclaredAttributes[VertexAttribute::BONE_WEIGHTS])
|
||||
<< "Vertex buffer attribute BONE_WEIGHTS is already defined, "
|
||||
"no advanced skinning is allowed";
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mBufferCount < (MAX_VERTEX_BUFFER_COUNT - 2))
|
||||
<< "Vertex buffer uses to many buffers (" << mImpl->mBufferCount << ")";
|
||||
ASSERT_PRECONDITION(!mImpl->mDeclaredAttributes[VertexAttribute::BONE_INDICES],
|
||||
"Vertex buffer attribute BONE_INDICES is already defined, "
|
||||
"no advanced skinning is allowed");
|
||||
ASSERT_PRECONDITION(!mImpl->mDeclaredAttributes[VertexAttribute::BONE_WEIGHTS],
|
||||
"Vertex buffer attribute BONE_WEIGHTS is already defined, "
|
||||
"no advanced skinning is allowed");
|
||||
ASSERT_PRECONDITION(mImpl->mBufferCount < (MAX_VERTEX_BUFFER_COUNT - 2),
|
||||
"Vertex buffer uses to many buffers (%u)", mImpl->mBufferCount);
|
||||
}
|
||||
|
||||
return downcast(engine).createVertexBuffer(*this);
|
||||
@@ -328,22 +328,21 @@ size_t FVertexBuffer::getVertexCount() const noexcept {
|
||||
|
||||
void FVertexBuffer::setBufferAt(FEngine& engine, uint8_t bufferIndex,
|
||||
backend::BufferDescriptor&& buffer, uint32_t byteOffset) {
|
||||
FILAMENT_CHECK_PRECONDITION(!mBufferObjectsEnabled) << "Please use setBufferObjectAt()";
|
||||
ASSERT_PRECONDITION(!mBufferObjectsEnabled, "Please use setBufferObjectAt()");
|
||||
if (bufferIndex < mBufferCount) {
|
||||
assert_invariant(mBufferObjects[bufferIndex]);
|
||||
engine.getDriverApi().updateBufferObject(mBufferObjects[bufferIndex],
|
||||
std::move(buffer), byteOffset);
|
||||
} else {
|
||||
FILAMENT_CHECK_PRECONDITION(bufferIndex < mBufferCount)
|
||||
<< "bufferIndex must be < bufferCount";
|
||||
ASSERT_PRECONDITION(bufferIndex < mBufferCount, "bufferIndex must be < bufferCount");
|
||||
}
|
||||
}
|
||||
|
||||
void FVertexBuffer::setBufferObjectAt(FEngine& engine, uint8_t bufferIndex,
|
||||
FBufferObject const * bufferObject) {
|
||||
FILAMENT_CHECK_PRECONDITION(mBufferObjectsEnabled) << "Please use setBufferAt()";
|
||||
FILAMENT_CHECK_PRECONDITION(bufferObject->getBindingType() == BufferObject::BindingType::VERTEX)
|
||||
<< "Binding type must be VERTEX.";
|
||||
ASSERT_PRECONDITION(mBufferObjectsEnabled, "Please use setBufferAt()");
|
||||
ASSERT_PRECONDITION(bufferObject->getBindingType() == BufferObject::BindingType::VERTEX,
|
||||
"Binding type must be VERTEX.");
|
||||
if (bufferIndex < mBufferCount) {
|
||||
auto hwBufferObject = bufferObject->getHwHandle();
|
||||
engine.getDriverApi().setVertexBufferObject(mHandle, bufferIndex, hwBufferObject);
|
||||
@@ -351,15 +350,14 @@ void FVertexBuffer::setBufferObjectAt(FEngine& engine, uint8_t bufferIndex,
|
||||
// used only in buffer object mode
|
||||
mBufferObjects[bufferIndex] = hwBufferObject;
|
||||
} else {
|
||||
FILAMENT_CHECK_PRECONDITION(bufferIndex < mBufferCount)
|
||||
<< "bufferIndex must be < bufferCount";
|
||||
ASSERT_PRECONDITION(bufferIndex < mBufferCount, "bufferIndex must be < bufferCount");
|
||||
}
|
||||
}
|
||||
|
||||
void FVertexBuffer::updateBoneIndicesAndWeights(FEngine& engine,
|
||||
std::unique_ptr<uint16_t[]> skinJoints,
|
||||
std::unique_ptr<float[]> skinWeights) {
|
||||
FILAMENT_CHECK_PRECONDITION(mAdvancedSkinningEnabled) << "No advanced skinning enabled";
|
||||
ASSERT_PRECONDITION(mAdvancedSkinningEnabled, "No advanced skinning enabled");
|
||||
auto jointsData = skinJoints.release();
|
||||
uint8_t const indicesIndex = mAttributes[VertexAttribute::BONE_INDICES].buffer;
|
||||
engine.getDriverApi().updateBufferObject(mBufferObjects[indicesIndex],
|
||||
|
||||
@@ -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();
|
||||
@@ -1175,14 +1175,12 @@ void FView::setStereoscopicOptions(const StereoscopicOptions& options) noexcept
|
||||
}
|
||||
|
||||
void FView::setMaterialGlobal(uint32_t index, float4 const& value) {
|
||||
FILAMENT_CHECK_PRECONDITION(index < 4)
|
||||
<< "material global variable index (" << +index << ") out of range";
|
||||
ASSERT_PRECONDITION(index < 4, "material global variable index (%u) out of range", +index);
|
||||
mMaterialGlobals[index] = value;
|
||||
}
|
||||
|
||||
math::float4 FView::getMaterialGlobal(uint32_t index) const {
|
||||
FILAMENT_CHECK_PRECONDITION(index < 4)
|
||||
<< "material global variable index (" << +index << ") out of range";
|
||||
ASSERT_PRECONDITION(index < 4, "material global variable index (%u) out of range", +index);
|
||||
return mMaterialGlobals[index];
|
||||
}
|
||||
|
||||
|
||||
@@ -302,9 +302,9 @@ FrameGraphHandle FrameGraph::readInternal(FrameGraphHandle handle, PassNode* pas
|
||||
|
||||
// Check preconditions
|
||||
bool const passAlreadyAWriter = node->hasWriteFrom(passNode);
|
||||
FILAMENT_CHECK_PRECONDITION(!passAlreadyAWriter)
|
||||
<< "Pass \"" << passNode->getName() << "\" already writes to \"" << node->getName()
|
||||
<< "\"";
|
||||
ASSERT_PRECONDITION(!passAlreadyAWriter,
|
||||
"Pass \"%s\" already writes to \"%s\"",
|
||||
passNode->getName(), node->getName());
|
||||
|
||||
if (!node->hasWriterPass() && !resource->isImported()) {
|
||||
// TODO: we're attempting to read from a resource that was never written and is not
|
||||
@@ -464,9 +464,9 @@ bool FrameGraph::isValid(FrameGraphHandle handle) const {
|
||||
}
|
||||
|
||||
void FrameGraph::assertValid(FrameGraphHandle handle) const {
|
||||
FILAMENT_CHECK_PRECONDITION(isValid(handle))
|
||||
<< "Resource handle is invalid or uninitialized {id=" << (int)handle.index
|
||||
<< ", version=" << (int)handle.version << "}";
|
||||
ASSERT_PRECONDITION(isValid(handle),
|
||||
"Resource handle is invalid or uninitialized {id=%u, version=%u}",
|
||||
(int)handle.index, (int)handle.version);
|
||||
}
|
||||
|
||||
bool FrameGraph::isCulled(FrameGraphPassBase const& pass) const noexcept {
|
||||
|
||||
@@ -32,18 +32,18 @@ const char* FrameGraphResources::getPassName() const noexcept {
|
||||
// this perhaps weirdly returns a reference, this is to express the fact that if this method
|
||||
// fails, it has to assert (or throw), it can't return for e.g. a nullptr, because the public
|
||||
// API doesn't return pointers.
|
||||
// We still use FILAMENT_CHECK_PRECONDITION() because these failures are due to post conditions not met.
|
||||
// We still use ASSERT_PRECONDITION() because these failures are due to post conditions not met.
|
||||
VirtualResource& FrameGraphResources::getResource(FrameGraphHandle handle) const {
|
||||
FILAMENT_CHECK_PRECONDITION(handle) << "Uninitialized handle when using FrameGraphResources.";
|
||||
ASSERT_PRECONDITION(handle, "Uninitialized handle when using FrameGraphResources.");
|
||||
|
||||
VirtualResource* const resource = mFrameGraph.getResource(handle);
|
||||
|
||||
auto& declaredHandles = mPassNode.mDeclaredHandles;
|
||||
const bool hasReadOrWrite = declaredHandles.find(handle.index) != declaredHandles.cend();
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(hasReadOrWrite)
|
||||
<< "Pass \"" << mPassNode.getName() << "\" didn't declare any access to resource \""
|
||||
<< resource->name << "\"";
|
||||
ASSERT_PRECONDITION(hasReadOrWrite,
|
||||
"Pass \"%s\" didn't declare any access to resource \"%s\"",
|
||||
mPassNode.getName(), resource->name);
|
||||
|
||||
assert_invariant(resource->refcount);
|
||||
|
||||
@@ -55,8 +55,9 @@ FrameGraphResources::RenderPassInfo FrameGraphResources::getRenderPassInfo(uint3
|
||||
RenderPassNode const& renderPassNode = static_cast<RenderPassNode const&>(mPassNode);
|
||||
RenderPassNode::RenderPassData const* pRenderPassData = renderPassNode.getRenderPassData(id);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(pRenderPassData) << "using invalid RenderPass index " << id
|
||||
<< " in Pass \"" << mPassNode.getName() << "\"";
|
||||
ASSERT_PRECONDITION(pRenderPassData,
|
||||
"using invalid RenderPass index %u in Pass \"%s\"",
|
||||
id, mPassNode.getName());
|
||||
|
||||
return { pRenderPassData->backend.target, pRenderPassData->backend.params };
|
||||
}
|
||||
|
||||
@@ -100,9 +100,9 @@ void ImportedRenderTarget::assertConnect(FrameGraphTexture::Usage u) {
|
||||
FrameGraphTexture::Usage::DEPTH_ATTACHMENT |
|
||||
FrameGraphTexture::Usage::STENCIL_ATTACHMENT;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(none(u & ~ANY_ATTACHMENT))
|
||||
<< "Imported render target resource \"" << name
|
||||
<< "\" can only be used as an attachment (usage=" << utils::to_string(u).c_str() << ')';
|
||||
ASSERT_PRECONDITION(none(u & ~ANY_ATTACHMENT),
|
||||
"Imported render target resource \"%s\" can only be used as an attachment (usage=%s)",
|
||||
name, utils::to_string(u).c_str());
|
||||
}
|
||||
|
||||
bool ImportedRenderTarget::connect(DependencyGraph& graph, PassNode* passNode,
|
||||
|
||||
@@ -296,10 +296,9 @@ protected:
|
||||
private:
|
||||
UTILS_NOINLINE
|
||||
void assertConnect(FrameGraphTexture::Usage u) {
|
||||
FILAMENT_CHECK_PRECONDITION((u & this->usage) == u)
|
||||
<< "Requested usage " << utils::to_string(u).c_str()
|
||||
<< " not available on imported resource \"" << this->name << "\" with usage "
|
||||
<< utils::to_string(this->usage).c_str();
|
||||
ASSERT_PRECONDITION((u & this->usage) == u,
|
||||
"Requested usage %s not available on imported resource \"%s\" with usage %s",
|
||||
utils::to_string(u).c_str(), this->name, utils::to_string(this->usage).c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.52.2"
|
||||
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.2/filament-v1.52.2-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 = {
|
||||
|
||||
@@ -31,9 +31,10 @@ bool loadLibrary() {
|
||||
#endif
|
||||
|
||||
module = dlopen(dylibPath, RTLD_NOW | RTLD_LOCAL);
|
||||
FILAMENT_CHECK_POSTCONDITION(module != nullptr)
|
||||
<< "BlueVK is unable to load entry points: " << dlerror() << ".\n"
|
||||
"Install the LunarG SDK with 'System Global Installation' and reboot.\n";
|
||||
ASSERT_POSTCONDITION(module != nullptr,
|
||||
"BlueVK is unable to load entry points: %s.\n"
|
||||
"Install the LunarG SDK with 'System Global Installation' and reboot.\n",
|
||||
dlerror());
|
||||
return module != nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<>
|
||||
|
||||
@@ -86,16 +86,16 @@ BufferInterfaceBlock BufferInterfaceBlock::Builder::build() {
|
||||
});
|
||||
|
||||
// if there is one, check it's the last entry
|
||||
FILAMENT_CHECK_PRECONDITION(pos == mEntries.end() || pos == mEntries.end() - 1)
|
||||
<< "the variable-size array must be the last entry";
|
||||
ASSERT_PRECONDITION(pos == mEntries.end() || pos == mEntries.end() - 1,
|
||||
"the variable-size array must be the last entry");
|
||||
|
||||
// if we have a variable size array, we can't be a UBO
|
||||
FILAMENT_CHECK_PRECONDITION(pos == mEntries.end() || mTarget == Target::SSBO)
|
||||
<< "variable size arrays not supported for UBOs";
|
||||
ASSERT_PRECONDITION(pos == mEntries.end() || mTarget == Target::SSBO,
|
||||
"variable size arrays not supported for UBOs");
|
||||
|
||||
// std430 not available for UBOs
|
||||
FILAMENT_CHECK_PRECONDITION(mAlignment == Alignment::std140 || mTarget == Target::SSBO)
|
||||
<< "UBOs must use std140";
|
||||
ASSERT_PRECONDITION(mAlignment == Alignment::std140 || mTarget == Target::SSBO,
|
||||
"UBOs must use std140");
|
||||
|
||||
return BufferInterfaceBlock(*this);
|
||||
}
|
||||
@@ -166,7 +166,8 @@ ssize_t BufferInterfaceBlock::getFieldOffset(std::string_view name, size_t index
|
||||
BufferInterfaceBlock::FieldInfo const* BufferInterfaceBlock::getFieldInfo(
|
||||
std::string_view name) const {
|
||||
auto pos = mInfoMap.find(name);
|
||||
FILAMENT_CHECK_PRECONDITION(pos != mInfoMap.end()) << "uniform named \"%.*s\" not found";
|
||||
ASSERT_PRECONDITION(pos != mInfoMap.end(),
|
||||
"uniform named \"%.*s\" not found", name.size(), name.data());
|
||||
return &mFieldInfoList[pos->second];
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,8 @@ SamplerInterfaceBlock::SamplerInterfaceBlock(Builder const& builder) noexcept
|
||||
const SamplerInterfaceBlock::SamplerInfo* SamplerInterfaceBlock::getSamplerInfo(
|
||||
std::string_view name) const {
|
||||
auto pos = mInfoMap.find(name);
|
||||
FILAMENT_CHECK_PRECONDITION(pos != mInfoMap.end()) << "sampler named \"" << name << "\" not found";
|
||||
ASSERT_PRECONDITION(pos != mInfoMap.end(), "sampler named \"%.*s\" not found",
|
||||
name.size(), name.data());
|
||||
return &mSamplersInfoList[pos->second];
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user