Compare commits

..
Author SHA1 Message Date
Powei Feng 3e32e6cf2a backend-test: centralize read pixel logic 2024-05-16 15:08:56 -07:00
136 changed files with 1287 additions and 2164 deletions
+5 -40
View File
@@ -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
View File
@@ -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.
+2 -2
View File
@@ -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
View File
@@ -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
@@ -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 -1
View File
@@ -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.
+2 -23
View File
@@ -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 ""
-1
View File
@@ -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
+2 -2
View File
@@ -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;
+3 -3
View File
@@ -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;
+11 -7
View File
@@ -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);
}
+2 -2
View File
@@ -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();
+5 -6
View File
@@ -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;
+52 -56
View File
@@ -65,9 +65,12 @@ private:
const char* mName;
};
class TrackedMetalBuffer {
public:
#ifndef FILAMENT_METAL_BUFFER_TRACKING
#define FILAMENT_METAL_BUFFER_TRACKING 0
#endif
class MetalBufferTracking {
public:
static constexpr size_t EXCESS_BUFFER_COUNT = 30000;
enum class Type {
@@ -91,66 +94,57 @@ public:
}
}
TrackedMetalBuffer() noexcept : mBuffer(nil) {}
TrackedMetalBuffer(nullptr_t) noexcept : mBuffer(nil) {}
TrackedMetalBuffer(id<MTLBuffer> buffer, Type type) : mBuffer(buffer), mType(type) {
#if FILAMENT_METAL_BUFFER_TRACKING
static void initialize() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
for (size_t i = 0; i < TypeCount; i++) {
aliveBuffers[i] = [NSHashTable weakObjectsHashTable];
}
});
}
static void setPlatform(MetalPlatform* p) { platform = p; }
static void track(id<MTLBuffer> buffer, Type type) {
assert_invariant(type != Type::NONE);
if (buffer) {
aliveBuffers[toIndex(type)]++;
mType = type;
if (getAliveBuffers() >= EXCESS_BUFFER_COUNT) {
if (platform && platform->hasDebugUpdateStatFunc()) {
platform->debugUpdateStat("filament.metal.excess_buffers_allocated",
TrackedMetalBuffer::getAliveBuffers());
}
if (UTILS_UNLIKELY(getAliveBuffers() >= EXCESS_BUFFER_COUNT)) {
if (platform && platform->hasDebugUpdateStatFunc()) {
platform->debugUpdateStat("filament.metal.excess_buffers_allocated",
MetalBufferTracking::getAliveBuffers());
}
}
[aliveBuffers[toIndex(type)] addObject:buffer];
}
~TrackedMetalBuffer() {
if (mBuffer) {
assert_invariant(mType != Type::NONE);
aliveBuffers[toIndex(mType)]--;
}
}
TrackedMetalBuffer(TrackedMetalBuffer&&) = delete;
TrackedMetalBuffer(TrackedMetalBuffer const&) = delete;
TrackedMetalBuffer& operator=(TrackedMetalBuffer const&) = delete;
TrackedMetalBuffer& operator=(TrackedMetalBuffer&& rhs) noexcept {
swap(rhs);
return *this;
}
id<MTLBuffer> get() const noexcept { return mBuffer; }
operator bool() const noexcept { return bool(mBuffer); }
static uint64_t getAliveBuffers() {
uint64_t sum = 0;
for (const auto& v : aliveBuffers) {
sum += v;
for (size_t i = 1; i < TypeCount; i++) {
sum += getAliveBuffers(static_cast<Type>(i));
}
return sum;
}
static uint64_t getAliveBuffers(Type type) {
assert_invariant(type != Type::NONE);
return aliveBuffers[toIndex(type)];
NSHashTable* hashTable = aliveBuffers[toIndex(type)];
// Caution! We can't simply use hashTable.count here, which is inaccurate.
// See http://cocoamine.net/blog/2013/12/13/nsmaptable-and-zeroing-weak-references/
return hashTable.objectEnumerator.allObjects.count;
}
static void setPlatform(MetalPlatform* p) { platform = p; }
#else
static void initialize() {}
static void setPlatform(MetalPlatform* p) {}
static id<MTLBuffer> track(id<MTLBuffer> buffer, Type type) { return buffer; }
static uint64_t getAliveBuffers() { return 0; }
static uint64_t getAliveBuffers(Type type) { return 0; }
#endif
private:
void swap(TrackedMetalBuffer& other) noexcept {
std::swap(mBuffer, other.mBuffer);
std::swap(mType, other.mType);
}
id<MTLBuffer> mBuffer;
Type mType = Type::NONE;
#if FILAMENT_METAL_BUFFER_TRACKING
static std::array<NSHashTable<id<MTLBuffer>>*, TypeCount> aliveBuffers;
static MetalPlatform* platform;
static std::array<uint64_t, TypeCount> aliveBuffers;
#endif
};
class MetalBuffer {
@@ -204,7 +198,7 @@ public:
private:
TrackedMetalBuffer mBuffer;
id<MTLBuffer> mBuffer;
size_t mBufferSize = 0;
void* mCpuBuffer = nullptr;
MetalContext& mContext;
@@ -253,9 +247,11 @@ public:
mBufferOptions(options),
mSlotSizeBytes(computeSlotSize(layout)),
mSlotCount(slotCount) {
ScopedAllocationTimer timer("ring");
mBuffer = { [device newBufferWithLength:mSlotSizeBytes * mSlotCount options:mBufferOptions],
TrackedMetalBuffer::Type::RING };
{
ScopedAllocationTimer timer("ring");
mBuffer = [device newBufferWithLength:mSlotSizeBytes * mSlotCount options:mBufferOptions];
}
MetalBufferTracking::track(mBuffer, MetalBufferTracking::Type::RING);
assert_invariant(mBuffer);
}
@@ -275,11 +271,11 @@ public:
// finishes executing.
{
ScopedAllocationTimer timer("ring");
mAuxBuffer = { [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions],
TrackedMetalBuffer::Type::RING };
mAuxBuffer = [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions];
}
MetalBufferTracking::track(mAuxBuffer, MetalBufferTracking::Type::RING);
assert_invariant(mAuxBuffer);
return { mAuxBuffer.get(), 0 };
return { mAuxBuffer, 0 };
}
mCurrentSlot = (mCurrentSlot + 1) % mSlotCount;
mOccupiedSlots->fetch_add(1, std::memory_order_relaxed);
@@ -308,9 +304,9 @@ public:
*/
std::pair<id<MTLBuffer>, NSUInteger> getCurrentAllocation() const {
if (UTILS_UNLIKELY(mAuxBuffer)) {
return { mAuxBuffer.get(), 0 };
return { mAuxBuffer, 0 };
}
return { mBuffer.get(), mCurrentSlot * mSlotSizeBytes };
return { mBuffer, mCurrentSlot * mSlotSizeBytes };
}
bool canAccomodateLayout(MTLSizeAndAlign layout) const {
@@ -319,8 +315,8 @@ public:
private:
id<MTLDevice> mDevice;
TrackedMetalBuffer mBuffer;
TrackedMetalBuffer mAuxBuffer;
id<MTLBuffer> mBuffer;
id<MTLBuffer> mAuxBuffer;
MTLResourceOptions mBufferOptions;
+17 -14
View File
@@ -22,10 +22,14 @@
namespace filament {
namespace backend {
std::array<uint64_t, TrackedMetalBuffer::TypeCount> TrackedMetalBuffer::aliveBuffers = { 0 };
MetalPlatform* TrackedMetalBuffer::platform = nullptr;
MetalPlatform* ScopedAllocationTimer::platform = nullptr;
#if FILAMENT_METAL_BUFFER_TRACKING
std::array<NSHashTable<id<MTLBuffer>>*, MetalBufferTracking::TypeCount>
MetalBufferTracking::aliveBuffers;
MetalPlatform* MetalBufferTracking::platform = nullptr;
#endif
MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) {
// If the buffer is less than 4K in size and is updated frequently, we don't use an explicit
@@ -41,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,
+1 -1
View File
@@ -32,7 +32,7 @@ struct MetalContext;
// Immutable POD representing a shared CPU-GPU buffer.
struct MetalBufferPoolEntry {
TrackedMetalBuffer buffer;
id<MTLBuffer> buffer;
size_t capacity;
mutable uint64_t lastAccessed;
mutable uint32_t referenceCount;
@@ -48,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
-14
View File
@@ -55,18 +55,6 @@ struct MetalVertexBuffer;
constexpr static uint8_t MAX_SAMPLE_COUNT = 8; // Metal devices support at most 8 MSAA samples
class MetalPushConstantBuffer {
public:
void setPushConstant(PushConstantVariant value, uint8_t index);
bool isDirty() const { return mDirty; }
void setBytes(id<MTLCommandEncoder> encoder, ShaderStage stage);
void clear();
private:
std::vector<PushConstantVariant> mPushConstants;
bool mDirty = false;
};
struct MetalContext {
explicit MetalContext(size_t metalFreedTextureListSize)
: texturesToDestroy(metalFreedTextureListSize) {}
@@ -121,8 +109,6 @@ struct MetalContext {
PolygonOffset currentPolygonOffset = {0.0f, 0.0f};
std::array<MetalPushConstantBuffer, Program::SHADER_TYPE_COUNT> currentPushConstants;
MetalSamplerGroup* samplerBindings[Program::SAMPLER_BINDING_COUNT] = {};
// Keeps track of sampler groups we've finalized for the current render pass.
+1 -65
View File
@@ -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
-2
View File
@@ -17,7 +17,6 @@
#ifndef TNT_FILAMENT_DRIVER_METALDRIVER_H
#define TNT_FILAMENT_DRIVER_METALDRIVER_H
#include <backend/DriverEnums.h>
#include "private/backend/Driver.h"
#include "DriverBase.h"
@@ -141,7 +140,6 @@ private:
void enumerateBoundBuffers(BufferObjectBinding bindingType,
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f);
backend::StereoscopicType const mStereoscopicType;
};
} // namespace backend
+124 -147
View File
@@ -102,12 +102,12 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
mContext(new MetalContext(driverConfig.textureUseAfterFreePoolSize)),
mHandleAllocator("Handles",
driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck),
mStereoscopicType(driverConfig.stereoscopicType) {
driverConfig.disableHandleUseAfterFreeCheck) {
mContext->driver = this;
TrackedMetalBuffer::setPlatform(platform);
ScopedAllocationTimer::setPlatform(platform);
MetalBufferTracking::initialize();
MetalBufferTracking::setPlatform(platform);
mContext->device = mPlatform.createDevice();
assert_invariant(mContext->device);
@@ -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);
}
+3 -4
View File
@@ -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() {
+23 -30
View File
@@ -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);
+2 -4
View File
@@ -43,8 +43,7 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) {
// ------------------------------------------------------
// 0 Zero buffer (placeholder vertex buffer) 1
// 1-16 Filament vertex buffers 16 limited by MAX_VERTEX_BUFFER_COUNT
// 17-25 Uniform buffers 9 Program::UNIFORM_BINDING_COUNT
// 26 Push constants 1
// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT
// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT
//
// Total 31
@@ -54,8 +53,7 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) {
// Bindings Buffer name Count
// ------------------------------------------------------
// 0-3 SSBO buffers 4 MAX_SSBO_COUNT
// 17-25 Uniform buffers 9 Program::UNIFORM_BINDING_COUNT
// 26 Push constants 1
// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT
// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT
//
// Total 18
+3 -9
View File
@@ -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;
}
+1 -1
View File
@@ -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{{
+25 -22
View File
@@ -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};
}
+1 -6
View File
@@ -120,21 +120,16 @@ public:
}
inline bool isImageCubeArraySupported() const noexcept {
return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE;
return mPhysicalDeviceFeatures.imageCubeArray;
}
inline bool isDebugMarkersSupported() const noexcept {
return mDebugMarkersSupported;
}
inline bool isDebugUtilsSupported() const noexcept {
return mDebugUtilsSupported;
}
inline bool isClipDistanceSupported() const noexcept {
return mPhysicalDeviceFeatures.shaderClipDistance == VK_TRUE;
}
private:
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
VkPhysicalDeviceProperties mPhysicalDeviceProperties = {};
+46 -51
View File
@@ -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);
+1 -2
View File
@@ -28,7 +28,6 @@
#include "VulkanSamplerCache.h"
#include "VulkanStagePool.h"
#include "VulkanUtility.h"
#include "backend/DriverEnums.h"
#include "caching/VulkanDescriptorSetManager.h"
#include "caching/VulkanPipelineLayoutCache.h"
@@ -167,10 +166,10 @@ private:
VkPipelineLayout pipelineLayout;
};
BoundPipeline mBoundPipeline = {};
RenderPassFboBundle mRenderPassFboInfo;
bool const mIsSRGBSwapChainSupported;
backend::StereoscopicType const mStereoscopicType;
};
} // namespace filament::backend
@@ -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;
}
}
+7 -7
View File
@@ -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 {
+51 -41
View File
@@ -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 {
+11
View File
@@ -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;
+34 -100
View File
@@ -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], &params);
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], &params);
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, &params);
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, &params);
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, &params);
// 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);
+13 -33
View File
@@ -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
+1 -1
View File
@@ -315,7 +315,7 @@ public:
*
* @see View::setStereoscopicOptions
*/
StereoscopicType stereoscopicType = StereoscopicType::NONE;
StereoscopicType stereoscopicType = StereoscopicType::INSTANCED;
/*
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
+37 -49
View File
@@ -464,11 +464,16 @@ public:
Builder& boneIndicesAndWeights(size_t primitiveIndex,
utils::FixedCapacityVector<
utils::FixedCapacityVector<math::float2>> indicesAndWeightsVector) noexcept;
/**
* Controls if the renderable has legacy vertex morphing targets, zero by default. This is
* Controls if the renderable has vertex morphing targets, zero by default. This is
* required to enable GPU morphing.
*
* Filament supports two morphing modes: standard (default) and legacy.
*
* For standard morphing, A MorphTargetBuffer must be created and provided via
* RenderableManager::setMorphTargetBufferAt(). Standard morphing supports up to
* \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets.
*
* For legacy morphing, the attached VertexBuffer must provide data in the
* appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only
* supports up to 4 morph targets and will be deprecated in the future. Legacy morphing must
@@ -481,52 +486,26 @@ public:
Builder& morphing(size_t targetCount) noexcept;
/**
* Controls if the renderable has vertex morphing targets, zero by default. This is
* required to enable GPU morphing.
* Specifies the morph target buffer for a primitive.
*
* Filament supports two morphing modes: standard (default) and legacy.
* The morph target buffer must have an associated renderable and geometry. Two conditions
* must be met:
* 1. The number of morph targets in the buffer must equal the renderable's morph target
* count.
* 2. The vertex count of each morph target must equal the geometry's vertex count.
*
* For standard morphing, A MorphTargetBuffer must be provided.
* Standard morphing supports up to \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets.
*
* For legacy morphing, the attached VertexBuffer must provide data in the
* appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only
* supports up to 4 morph targets and will be deprecated in the future. Legacy morphing must
* be enabled on the material definition: either via the legacyMorphing material attribute
* or by calling filamat::MaterialBuilder::useLegacyMorphing().
*
* See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis
* to advance the animation.
*/
Builder& morphing(MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept;
/**
* @deprecated Use morphing(uint8_t level, size_t primitiveIndex, size_t offset, size_t count) instead
* @param level the level of detail (lod), only 0 can be specified
* @param primitiveIndex zero-based index of the primitive, must be less than the count passed to Builder constructor
* @param morphTargetBuffer specifies the morph target buffer
* @param offset specifies where in the morph target buffer to start reading (expressed as a number of vertices)
* @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3)
*/
Builder& morphing(uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer,
size_t offset, size_t count) noexcept;
/**
* @deprecated Use morphing(uint8_t level, size_t primitiveIndex, size_t offset, size_t count) instead
*/
inline Builder& morphing(uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept {
return morphing(level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
/**
* Specifies the the range of the MorphTargetBuffer to use with this primitive.
*
* @param level the level of detail (lod), only 0 can be specified
* @param primitiveIndex zero-based index of the primitive, must be less than the count passed to Builder constructor
* @param offset specifies where in the morph target buffer to start reading (expressed as a number of vertices)
* @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3)
*/
Builder& morphing(uint8_t level, size_t primitiveIndex,
size_t offset, size_t count) noexcept;
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept;
/**
* Sets the drawing order for blended primitives. The drawing order is either global or
@@ -786,19 +765,14 @@ public:
/**
* Associates a MorphTargetBuffer to the given primitive.
*/
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
size_t offset, size_t count);
/** @deprecated */
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, size_t offset, size_t count);
/** @deprecated */
/**
* Utility method to change a MorphTargetBuffer to the given primitive
*/
inline void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) {
setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer);
/**
* Get a MorphTargetBuffer to the given primitive or null if it doesn't exist.
@@ -932,6 +906,20 @@ protected:
~RenderableManager() = default;
};
RenderableManager::Builder& RenderableManager::Builder::morphing(
uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept {
return morphing(level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
void RenderableManager::setMorphTargetBufferAt(
Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) {
setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
template<typename VECTOR, typename INDEX, typename, typename>
Box RenderableManager::computeAABB(
VECTOR const* UTILS_NONNULL vertices,
+1 -1
View File
@@ -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;
+4 -7
View File
@@ -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 {
+1 -6
View File
@@ -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) {
+1 -2
View File
@@ -251,7 +251,6 @@ public:
uint32_t indexCount; // 4 bytes
uint32_t index = 0; // 4 bytes
backend::SamplerGroupHandle morphTargetBuffer; // 4 bytes
uint32_t morphingOffset = 0; // 4 bytes
backend::RasterState rasterState; // 4 bytes
@@ -262,7 +261,7 @@ public:
bool hasMorphing : 1; // 1 bit
bool hasHybridInstancing : 1; // 1 bit
uint32_t rfu[2]; // 16 bytes
uint32_t rfu[3]; // 16 bytes
};
static_assert(sizeof(PrimitiveInfo) == 56);
-5
View File
@@ -164,11 +164,6 @@ void RenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level,
downcast(morphTargetBuffer), offset, count);
}
void RenderableManager::setMorphTargetBufferAt(
Instance instance, uint8_t level, size_t primitiveIndex, size_t offset, size_t count) {
downcast(this)->setMorphTargetBufferAt(instance, level, primitiveIndex, offset, count);
}
MorphTargetBuffer* RenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level,
size_t primitiveIndex) const noexcept {
return downcast(this)->getMorphTargetBufferAt(instance, level, primitiveIndex);
+9 -8
View File
@@ -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));
}
+76 -106
View File
@@ -86,7 +86,6 @@ struct RenderableManager::BuilderDetails {
RenderableManager::Builder::GeometryType mGeometryType : 2;
size_t mSkinningBoneCount = 0;
size_t mMorphTargetCount = 0;
FMorphTargetBuffer* mMorphTargetBuffer = nullptr;
Bone const* mUserBones = nullptr;
mat4f const* mUserBoneMatrices = nullptr;
FSkinningBuffer* mSkinningBuffer = nullptr;
@@ -274,18 +273,6 @@ RenderableManager::Builder& RenderableManager::Builder::morphing(size_t targetCo
return *this;
}
RenderableManager::Builder& RenderableManager::Builder::morphing(
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept {
mImpl->mMorphTargetBuffer = downcast(morphTargetBuffer);
mImpl->mMorphTargetCount = morphTargetBuffer->getCount();
return *this;
}
RenderableManager::Builder& RenderableManager::Builder::morphing(
uint8_t level, size_t primitiveIndex, size_t offset, size_t count) noexcept {
return morphing(level, primitiveIndex, mImpl->mMorphTargetBuffer, offset, count);
}
RenderableManager::Builder& RenderableManager::Builder::morphing(uint8_t, size_t primitiveIndex,
MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) noexcept {
std::vector<Entry>& entries = mImpl->mEntries;
@@ -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) {
+10 -10
View File
@@ -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;
}
+14 -16
View File
@@ -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);
}
+17 -21
View File
@@ -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));
}
+3 -3
View File
@@ -184,8 +184,8 @@ public:
return CONFIG_MAX_INSTANCES;
}
bool isStereoSupported() const noexcept {
return getDriver().isStereoSupported();
bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept {
return getDriver().isStereoSupported(stereoscopicType);
}
static size_t getMaxStereoscopicEyes() noexcept {
@@ -600,7 +600,7 @@ public:
bool debug_froxel_visualization = false;
} lighting;
struct {
bool combine_multiview_images = false;
bool combine_multiview_images = true;
} stereo;
matdbg::DebugServer* server = nullptr;
} debug;
+1 -2
View File
@@ -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;
+6 -6
View File
@@ -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);
+12 -9
View File
@@ -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);
}
+31 -36
View File
@@ -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;
+23 -20
View File
@@ -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
+9 -9
View File
@@ -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;
+7 -7
View File
@@ -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)) {
+8 -8
View File
@@ -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);
}
+3 -5
View File
@@ -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;
}
+74 -78
View File
@@ -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;
+25 -27
View File
@@ -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],
+3 -5
View File
@@ -60,7 +60,7 @@ static constexpr float PID_CONTROLLER_Kd = 0.0f;
FView::FView(FEngine& engine)
: mFroxelizer(engine),
mFogEntity(engine.getEntityManager().create()),
mIsStereoSupported(engine.getDriverApi().isStereoSupported()),
mIsStereoSupported(engine.getDriverApi().isStereoSupported(engine.getConfig().stereoscopicType)),
mPerViewUniforms(engine) {
DriverApi& driver = engine.getDriverApi();
@@ -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];
}
+6 -6
View File
@@ -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 {
+8 -7
View File
@@ -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 };
}
+3 -3
View File
@@ -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,
+3 -4
View File
@@ -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());
}
};
+2 -2
View File
@@ -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 = {
+4 -3
View File
@@ -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<>
+8 -7
View File
@@ -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