Compare commits

..
Author SHA1 Message Date
Powei Feng ed937ce995 Testing ankerl containers 2024-07-25 16:36:34 +08:00
2508 changed files with 16701 additions and 1970 deletions
+1
View File
@@ -746,6 +746,7 @@ add_subdirectory(${EXTERNAL}/draco/tnt)
add_subdirectory(${EXTERNAL}/jsmn/tnt)
add_subdirectory(${EXTERNAL}/stb/tnt)
add_subdirectory(${EXTERNAL}/getopt)
add_subdirectory(${EXTERNAL}/unordered_dense)
# Note that this has to be placed after mikktspace in order for combine_static_libs to work.
add_subdirectory(${LIBRARIES}/geometry)
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.54.2'
implementation 'com.google.android.filament:filament-android:1.53.2'
}
```
@@ -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.54.2'
pod 'Filament', '~> 1.53.2'
```
### Snapshots
-20
View File
@@ -7,26 +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.54.2
- Add a `name` API to Filament objects for debugging handle use-after-free assertions
## v1.54.1
## v1.54.0
- materials: add a new `stereoscopicType` material parameter. [⚠️ **New Material Version**]
- Fix a crash when compiling shaders on IMG devices
## v1.53.5
- engine: Fix bug causing certain sampler parameters to not be applied correctly in GLES 2.0 and on
certain GLES 3.0 drivers.
## v1.53.4
## v1.53.3
- Add drag and drop support for IBL files for desktop gltf_viewer.
-1
View File
@@ -128,7 +128,6 @@ target_include_directories(filament-jni PRIVATE
..
${FILAMENT_DIR}/include
../../filament/backend/include
../../third_party/robin-map
../../libs/utils/include)
# Force a relink when the version script is changed:
@@ -28,14 +28,6 @@
using namespace filament;
using namespace backend;
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Renderer_nSkipFrame(JNIEnv *, jclass, jlong nativeRenderer,
jlong vsyncSteadyClockTimeNano) {
Renderer *renderer = (Renderer *) nativeRenderer;
renderer->skipFrame(uint64_t(vsyncSteadyClockTimeNano));
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Renderer_nBeginFrame(JNIEnv *, jclass, jlong nativeRenderer,
jlong nativeSwapChain, jlong frameTimeNanos) {
@@ -531,12 +531,3 @@ Java_com_google_android_filament_View_nGetFogEntity(JNIEnv *env, jclass clazz,
View *view = (View *) nativeView;
return (jint)view->getFogEntity().getId();
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nClearFrameHistory(JNIEnv *env, jclass clazz,
jlong nativeView, jlong nativeEngine) {
View *view = (View *) nativeView;
Engine *engine = (Engine *) nativeEngine;
view->clearFrameHistory(*engine);
}
@@ -425,12 +425,9 @@ public class Engine {
public long resourceAllocatorCacheSizeMB = 64;
/*
* This value determines how many frames texture entries are kept for in the cache. This
* is a soft limit, meaning some texture older than this are allowed to stay in the cache.
* Typically only one texture is evicted per frame.
* The default is 1.
* This value determines for how many frames are texture entries kept in the cache.
*/
public long resourceAllocatorCacheMaxAge = 1;
public long resourceAllocatorCacheMaxAge = 2;
/*
* Disable backend handles use-after-free checks.
@@ -297,20 +297,6 @@ public class Renderer {
nSetVsyncTime(getNativeObject(), steadyClockTimeNano);
}
/**
* Call skipFrame when momentarily skipping frames, for instance if the content of the
* scene doesn't change.
*
* @param vsyncSteadyClockTimeNano The time in nanoseconds when the frame started being rendered,
* in the {@link System#nanoTime()} timebase. Divide this value by 1000000 to
* convert it to the {@link android.os.SystemClock#uptimeMillis()}
* time base. This typically comes from
* {@link android.view.Choreographer.FrameCallback}.
*/
public void skipFrame(long vsyncSteadyClockTimeNano) {
nSkipFrame(getNativeObject(), vsyncSteadyClockTimeNano);
}
/**
* Sets up a frame for this <code>Renderer</code>.
* <p><code>beginFrame</code> manages frame pacing, and returns whether or not a frame should be
@@ -730,7 +716,6 @@ public class Renderer {
private static native void nSetPresentationTime(long nativeObject, long monotonicClockNanos);
private static native void nSetVsyncTime(long nativeObject, long steadyClockTimeNano);
private static native void nSkipFrame(long nativeObject, long vsyncSteadyClockTimeNano);
private static native boolean nBeginFrame(long nativeRenderer, long nativeSwapChain, long frameTimeNanos);
private static native void nEndFrame(long nativeRenderer);
private static native void nRender(long nativeRenderer, long nativeView);
@@ -1233,18 +1233,6 @@ public class View {
return nGetFogEntity(getNativeObject());
}
/**
* When certain temporal features are used (e.g.: TAA or Screen-space reflections), the view
* keeps a history of previous frame renders associated with the Renderer the view was last
* used with. When switching Renderer, it may be necessary to clear that history by calling
* this method. Similarly, if the whole content of the screen change, like when a cut-scene
* starts, clearing the history might be needed to avoid artifacts due to the previous frame
* being very different.
*/
public void clearFrameHistory(Engine engine) {
nClearFrameHistory(getNativeObject(), engine.getNativeObject());
}
public long getNativeObject() {
if (mNativeObject == 0) {
throw new IllegalStateException("Calling method on destroyed View");
@@ -1306,7 +1294,7 @@ public class View {
private static native void nSetMaterialGlobal(long nativeView, int index, float x, float y, float z, float w);
private static native void nGetMaterialGlobal(long nativeView, int index, float[] out);
private static native int nGetFogEntity(long nativeView);
private static native void nClearFrameHistory(long nativeView, long nativeEngine);
/**
* List of available ambient occlusion techniques.
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.54.2
VERSION_NAME=1.53.2
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+2 -11
View File
@@ -1308,12 +1308,7 @@ Description
declare a variable called `eyeDirection` you can access it in the fragment shader using
`variable_eyeDirection`. In the vertex shader, the interpolant name is simply a member of
the `MaterialVertexInputs` structure (`material.eyeDirection` in your example). Each
interpolant is of type `float4` (`vec4`) in the shaders. By default the precision of the
interpolant is `highp` in *both* the vertex and fragment shaders.
An alternate syntax can be used to specify both the name and precision of the interpolant.
In this case the specified precision is used as-is in both fragment and vertex stages, in
particular if `default` is specified the default precision is used is the fragment shader
(`mediump`) and in the vertex shader (`highp`).
interpolant is of type `float4` (`vec4`) in the shaders.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
@@ -1325,11 +1320,7 @@ material {
}
],
variables : [
eyeDirection,
{
name : eyeColor,
precision : medium
}
eyeDirection
],
vertexDomain : device,
depthWrite : false,
-1
View File
@@ -61,7 +61,6 @@ set(SRCS
src/Engine.cpp
src/Exposure.cpp
src/Fence.cpp
src/FilamentBuilder.cpp
src/FrameInfo.cpp
src/FrameSkipper.cpp
src/Froxelizer.cpp
+1
View File
@@ -313,6 +313,7 @@ endif()
target_link_libraries(${TARGET} PUBLIC math)
target_link_libraries(${TARGET} PUBLIC utils)
target_link_libraries(${TARGET} PRIVATE unordered_dense)
# Android, iOS, and WebGL do not use bluegl.
if(FILAMENT_SUPPORTS_OPENGL AND NOT IOS AND NOT ANDROID AND NOT WEBGL)
@@ -1244,7 +1244,7 @@ enum class Workaround : uint16_t {
ADRENO_UNIFORM_ARRAY_CRASH,
// Workaround a Metal pipeline compilation error with the message:
// "Could not statically determine the target of a texture". See light_indirect.fs
METAL_STATIC_TEXTURE_TARGET_ERROR,
A8X_STATIC_TEXTURE_TARGET_ERROR,
// Adreno drivers sometimes aren't able to blit into a layer of a texture array.
DISABLE_BLIT_INTO_TEXTURE_ARRAY,
// Multiple workarounds needed for PowerVR GPUs
@@ -162,10 +162,6 @@ DECL_DRIVER_API_0(finish)
// reset state tracking, if the driver does any state tracking (e.g. GL)
DECL_DRIVER_API_0(resetState)
DECL_DRIVER_API_N(setDebugTag,
backend::HandleBase::HandleId, handleId,
utils::CString, tag)
/*
* Creating driver objects
* -----------------------
@@ -20,12 +20,11 @@
#include <backend/Handle.h>
#include <utils/Allocator.h>
#include <utils/CString.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <tsl/robin_map.h>
@@ -40,7 +39,7 @@
#define HandleAllocatorGL HandleAllocator<32, 64, 136> // ~4520 / pool / MiB
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<32, 64, 552> // ~1660 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<32, 48, 552> // ~1660 / pool / MiB
namespace filament::backend {
@@ -174,10 +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;
// getHandleTag() is only called if the check fails.
FILAMENT_CHECK_POSTCONDITION(expectedAge == age)
<< "use-after-free of Handle with id=" << handle.getId()
<< ", tag=" << getHandleTag(handle.getId()).c_str_safe();
FILAMENT_CHECK_POSTCONDITION(expectedAge == age) <<
"use-after-free of Handle with id=" << handle.getId();
}
}
@@ -204,29 +201,6 @@ public:
return handle_cast<Dp>(const_cast<Handle<B>&>(handle));
}
void associateTagToHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
// TODO: for now, only pool handles check for use-after-free, so we only keep tags for
// those
if (isPoolHandle(id)) {
// Truncate the age to get the debug tag
uint32_t const key = id & ~(HANDLE_DEBUG_TAG_MASK ^ HANDLE_AGE_MASK);
// This line is the costly part. In the future, we could potentially use a custom
// allocator.
mDebugTags[key] = std::move(tag);
}
}
utils::CString getHandleTag(HandleBase::HandleId id) const noexcept {
if (!isPoolHandle(id)) {
return "(no tag)";
}
uint32_t const key = id & ~(HANDLE_DEBUG_TAG_MASK ^ HANDLE_AGE_MASK);
if (auto pos = mDebugTags.find(key); pos != mDebugTags.end()) {
return pos->second;
}
return "(no tag)";
}
private:
template<typename D>
@@ -344,24 +318,12 @@ private:
}
}
// number if bits allotted to the handle's age (currently 4 max)
static constexpr uint32_t HANDLE_AGE_BIT_COUNT = 4;
// number if bits allotted to the handle's debug tag (HANDLE_AGE_BIT_COUNT max)
static constexpr uint32_t HANDLE_DEBUG_TAG_BIT_COUNT = 2;
// bit shift for both the age and debug tag
static constexpr uint32_t HANDLE_AGE_SHIFT = 27;
// mask for the heap (vs pool) flag
static constexpr uint32_t HANDLE_HEAP_FLAG = 0x80000000u;
// mask for the age
static constexpr uint32_t HANDLE_AGE_MASK =
((1 << HANDLE_AGE_BIT_COUNT) - 1) << HANDLE_AGE_SHIFT;
// mask for the debug tag
static constexpr uint32_t HANDLE_DEBUG_TAG_MASK =
((1 << HANDLE_DEBUG_TAG_BIT_COUNT) - 1) << HANDLE_AGE_SHIFT;
// mask for the index
static constexpr uint32_t HANDLE_INDEX_MASK = 0x07FFFFFFu;
static_assert(HANDLE_DEBUG_TAG_BIT_COUNT <= HANDLE_AGE_BIT_COUNT);
// we handle a 4 bits age per address
static constexpr uint32_t HANDLE_HEAP_FLAG = 0x80000000u; // pool vs heap handle
static constexpr uint32_t HANDLE_AGE_MASK = 0x78000000u; // handle's age
static constexpr uint32_t HANDLE_INDEX_MASK = 0x07FFFFFFu; // handle index
static constexpr uint32_t HANDLE_TAG_MASK = HANDLE_AGE_MASK;
static constexpr uint32_t HANDLE_AGE_SHIFT = 27;
static bool isPoolHandle(HandleBase::HandleId id) noexcept {
return (id & HANDLE_HEAP_FLAG) == 0u;
@@ -376,7 +338,7 @@ private:
// a non-pool handle.
if (UTILS_LIKELY(isPoolHandle(id))) {
char* const base = (char*)mHandleArena.getArea().begin();
uint32_t const tag = id & HANDLE_AGE_MASK;
uint32_t const tag = id & HANDLE_TAG_MASK;
size_t const offset = (id & HANDLE_INDEX_MASK) * Allocator::getAlignment();
return { static_cast<void*>(base + offset), tag };
}
@@ -391,7 +353,7 @@ private:
size_t const offset = (char*)p - base;
assert_invariant((offset % Allocator::getAlignment()) == 0);
auto id = HandleBase::HandleId(offset / Allocator::getAlignment());
id |= tag & HANDLE_AGE_MASK;
id |= tag & HANDLE_TAG_MASK;
assert_invariant((id & HANDLE_HEAP_FLAG) == 0);
return id;
}
@@ -401,7 +363,6 @@ private:
// Below is only used when running out of space in the HandleArena
mutable utils::Mutex mLock;
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
tsl::robin_map<HandleBase::HandleId, utils::CString> mDebugTags;
HandleBase::HandleId mId = 0;
bool mUseAfterFreeCheckDisabled = false;
};
+5 -8
View File
@@ -101,8 +101,9 @@ void CommandBufferQueue::flush() noexcept {
size_t const used = std::distance(
static_cast<char const*>(begin), static_cast<char const*>(end));
std::unique_lock<utils::Mutex> lock(mLock);
mCommandBuffersToExecute.push_back({ begin, end });
mCondition.notify_one();
// circular buffer is too small, we corrupted the stream
FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) <<
@@ -111,13 +112,11 @@ void CommandBufferQueue::flush() noexcept {
"Space used at this time: " << used <<
" bytes, overflow: " << used - mFreeSpace << " bytes";
mFreeSpace -= used;
mCommandBuffersToExecute.push_back({ begin, end });
mCondition.notify_one();
// wait until there is enough space in the buffer
mFreeSpace -= used;
if (UTILS_UNLIKELY(mFreeSpace < requiredSize)) {
#ifndef NDEBUG
size_t const totalUsed = circularBuffer.size() - mFreeSpace;
slog.d << "CommandStream used too much space (will block): "
@@ -154,10 +153,8 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
}
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Range const& buffer) {
size_t const used = std::distance(
static_cast<char const*>(buffer.begin), static_cast<char const*>(buffer.end));
std::lock_guard<utils::Mutex> const lock(mLock);
mFreeSpace += used;
mFreeSpace += uintptr_t(buffer.end) - uintptr_t(buffer.begin);
mCondition.notify_one();
}
-3
View File
@@ -80,9 +80,6 @@ HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size,
bool disableUseAfterFreeCheck) noexcept
: mHandleArena(name, size, disableUseAfterFreeCheck),
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) {
// Reserve initial space for debug tags. This prevents excessive calls to malloc when the first
// few tags are set.
mDebugTags.reserve(512);
}
template <size_t P0, size_t P1, size_t P2>
-2
View File
@@ -160,8 +160,6 @@ public:
size_t size, bool forceGpuBuffer = false);
~MetalBuffer();
[[nodiscard]] bool wasAllocationSuccessful() const noexcept { return mBuffer || mCpuBuffer; }
MetalBuffer(const MetalBuffer& rhs) = delete;
MetalBuffer& operator=(const MetalBuffer& rhs) = delete;
+2 -2
View File
@@ -53,8 +53,8 @@ MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType,
mBuffer = { [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate],
TrackedMetalBuffer::Type::GENERIC };
}
// mBuffer might fail to be allocated. Clients can check for this by calling
// wasAllocationSuccessful().
FILAMENT_CHECK_POSTCONDITION(mBuffer)
<< "Could not allocate Metal buffer of size " << size << ".";
}
MetalBuffer::~MetalBuffer() {
+1 -1
View File
@@ -93,7 +93,7 @@ struct MetalContext {
} highestSupportedGpuFamily;
struct {
bool staticTextureTargetError;
bool a8xStaticTextureTargetError;
} bugs;
// sampleCountLookup[requestedSamples] gives a <= sample count supported by the device.
+2 -3
View File
@@ -57,6 +57,7 @@ class MetalDriver final : public DriverBase {
public:
static Driver* create(MetalPlatform* platform, const Platform::DriverConfig& driverConfig);
void runAtNextTick(const std::function<void()>& fn) noexcept;
private:
@@ -72,12 +73,10 @@ private:
/*
* Tasks run regularly on the driver thread.
* Not thread-safe; tasks are run from the driver thead and must be enqueued from the driver
* thread.
*/
void runAtNextTick(const std::function<void()>& fn) noexcept;
void executeTickOps() noexcept;
std::vector<std::function<void()>> mTickOps;
std::mutex mTickOpsLock;
/*
* Driver interface
+14 -40
View File
@@ -53,14 +53,14 @@ Driver* MetalDriverFactory::create(MetalPlatform* const platform, const Platform
// MetalRenderPrimitive : 24 many
// MetalVertexBuffer : 32 moderate
// -- less than or equal 32 bytes
// MetalIndexBuffer : 40 moderate
// MetalFence : 48 few
// MetalIndexBuffer : 56 moderate
// MetalBufferObject : 64 many
// -- less than or equal 64 bytes
// MetalBufferObject : 48 many
// -- less than or equal 48 bytes
// MetalSamplerGroup : 112 few
// MetalProgram : 152 moderate
// MetalTexture : 152 moderate
// MetalSwapChain : 208 few
// MetalSwapChain : 184 few
// MetalRenderTarget : 272 few
// MetalVertexBufferInfo : 552 moderate
// -- less than or equal to 552 bytes
@@ -162,10 +162,8 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
sc[s] = [mContext->device supportsTextureSampleCount:s] ? s : sc[s - 1];
}
mContext->bugs.staticTextureTargetError =
[mContext->device.name containsString:@"Apple A8X GPU"] ||
[mContext->device.name containsString:@"Apple A8 GPU"] ||
[mContext->device.name containsString:@"Apple A7 GPU"];
mContext->bugs.a8xStaticTextureTargetError =
[mContext->device.name containsString:@"Apple A8X GPU"];
mContext->commandQueue = mPlatform.createCommandQueue(mContext->device);
mContext->pipelineStateCache.setDevice(mContext->device);
@@ -320,40 +318,17 @@ void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih) {
MetalVertexBufferInfo const* const vbi = handle_cast<const MetalVertexBufferInfo>(vbih);
construct_handle<MetalVertexBuffer>(vbh, *mContext, vertexCount, vbi->bufferCount, vbih);
// No actual GPU memory is allocated here, so no need to check for allocation success.
}
void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {
auto elementSize = (uint8_t)getElementTypeSize(elementType);
auto* indexBuffer =
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
auto& buffer = indexBuffer->buffer;
// If the allocation was not successful, postpone the error message until the next tick, to give
// Filament a chance to call setDebugTag on the handle; this way we get a nicer error message.
if (UTILS_UNLIKELY(!buffer.wasAllocationSuccessful())) {
const size_t byteCount = buffer.getSize();
runAtNextTick([byteCount, this, ibh]() {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Could not allocate Metal index buffer of size " << byteCount
<< ", tag=" << mHandleAllocator.getHandleTag(ibh.getId()).c_str_safe();
});
}
auto elementSize = (uint8_t) getElementTypeSize(elementType);
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
}
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
auto* bufferObject =
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
// If the allocation was not successful, postpone the error message until the next tick, to give
// Filament a chance to call setDebugTag on the handle; this way we get a nicer error message.
if (UTILS_UNLIKELY(!bufferObject->getBuffer()->wasAllocationSuccessful())) {
runAtNextTick([byteCount, this, boh]() {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Could not allocate Metal buffer of size " << byteCount
<< ", tag=" << mHandleAllocator.getHandleTag(boh.getId()).c_str_safe();
});
}
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
}
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
@@ -865,8 +840,8 @@ bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
return true;
case Workaround::ADRENO_UNIFORM_ARRAY_CRASH:
return false;
case Workaround::METAL_STATIC_TEXTURE_TARGET_ERROR:
return mContext->bugs.staticTextureTargetError;
case Workaround::A8X_STATIC_TEXTURE_TARGET_ERROR:
return mContext->bugs.a8xStaticTextureTargetError;
case Workaround::DISABLE_BLIT_INTO_TEXTURE_ARRAY:
return false;
default:
@@ -2084,17 +2059,16 @@ void MetalDriver::enumerateBoundBuffers(BufferObjectBinding bindingType,
void MetalDriver::resetState(int) {
}
void MetalDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
}
void MetalDriver::runAtNextTick(const std::function<void()>& fn) noexcept {
std::lock_guard<std::mutex> const lock(mTickOpsLock);
mTickOps.push_back(fn);
}
void MetalDriver::executeTickOps() noexcept {
std::vector<std::function<void()>> ops;
mTickOpsLock.lock();
std::swap(ops, mTickOps);
mTickOpsLock.unlock();
for (const auto& f : ops) {
f();
}
@@ -257,6 +257,10 @@ void MetalSwapChain::present() {
}
}
#ifndef FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD
#define FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD 1
#endif
class PresentDrawableData {
public:
PresentDrawableData() = delete;
@@ -275,10 +279,14 @@ public:
[that->mDrawable present];
}
#if FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD == 1
// mDrawable is acquired on the driver thread. Typically, we would release this object on
// the same thread, but after receiving consistent crash reports from within
// [CAMetalDrawable dealloc], we suspect this object requires releasing on the main thread.
dispatch_async(dispatch_get_main_queue(), ^{ cleanupAndDestroy(that); });
#else
that->mDriver->runAtNextTick([that]() { cleanupAndDestroy(that); });
#endif
}
private:
-3
View File
@@ -388,7 +388,4 @@ void NoopDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
void NoopDriver::resetState(int) {
}
void NoopDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
}
} // namespace filament
+11 -62
View File
@@ -83,20 +83,12 @@
#define HAS_MAPBUFFERS 1
#endif
#define DEBUG_GROUP_MARKER_NONE 0x00 // no debug marker
#define DEBUG_GROUP_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_GROUP_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
#define DEBUG_GROUP_MARKER_ALL 0x03 // all markers
#define DEBUG_MARKER_NONE 0x00 // no debug marker
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
#define DEBUG_MARKER_ALL 0x03 // all markers
#define DEBUG_MARKER_NONE 0x00 // no debug marker
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
#define DEBUG_MARKER_ALL 0x03 // all markers
// set to the desired debug marker level (for user markers [default: All])
#define DEBUG_GROUP_MARKER_LEVEL DEBUG_GROUP_MARKER_ALL
// set to the desired debug level (for internal debugging [Default: None])
// set to the desired debug marker level
#define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE
#if DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
@@ -204,37 +196,11 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
OpenGLDriver::DebugMarker::DebugMarker(OpenGLDriver& driver, const char* string) noexcept
: driver(driver) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
if (UTILS_LIKELY(driver.getContext().ext.EXT_debug_marker)) {
glPushGroupMarkerEXT(GLsizei(strlen(string)), string);
}
#endif
#endif
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
SYSTRACE_CONTEXT();
SYSTRACE_NAME_BEGIN(string);
#endif
#endif
driver.pushGroupMarker(string, strlen(string));
}
OpenGLDriver::DebugMarker::~DebugMarker() noexcept {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
if (UTILS_LIKELY(driver.getContext().ext.EXT_debug_marker)) {
glPopGroupMarkerEXT();
}
#endif
#endif
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
SYSTRACE_CONTEXT();
SYSTRACE_NAME_END();
#endif
#endif
driver.popGroupMarker();
}
// ------------------------------------------------------------------------------------------------
@@ -2210,10 +2176,6 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
// Updating driver objects
// ------------------------------------------------------------------------------------------------
void OpenGLDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
}
void OpenGLDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh,
uint32_t index, Handle<HwBufferObject> boh) {
DEBUG_MARKER()
@@ -2379,15 +2341,6 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
GLTexture const* const t = handle_cast<const GLTexture*>(th);
assert_invariant(t);
if (UTILS_UNLIKELY(es2)
#if defined(GL_EXT_texture_filter_anisotropic)
|| UTILS_UNLIKELY(anisotropyWorkaround)
#endif
) {
// We must set texture parameters on the texture itself.
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
}
SamplerParams params = pSamplers[i].s;
if (UTILS_UNLIKELY(t->target == SamplerType::SAMPLER_EXTERNAL)) {
// From OES_EGL_image_external spec:
@@ -3198,14 +3151,14 @@ void OpenGLDriver::insertEventMarker(char const* string, uint32_t len) {
void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_OPENGL
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
glPushGroupMarkerEXT(GLsizei(len ? len : strlen(string)), string);
}
#endif
#endif
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
SYSTRACE_CONTEXT();
SYSTRACE_NAME_BEGIN(string);
#endif
@@ -3215,14 +3168,14 @@ void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
void OpenGLDriver::popGroupMarker(int) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_OPENGL
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
glPopGroupMarkerEXT();
}
#endif
#endif
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
SYSTRACE_CONTEXT();
SYSTRACE_NAME_END();
#endif
@@ -3928,7 +3881,6 @@ void OpenGLDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
DEBUG_MARKER()
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
return;
@@ -3950,7 +3902,6 @@ void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
}
void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
DEBUG_MARKER()
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
return;
@@ -3971,7 +3922,6 @@ void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_
}
void OpenGLDriver::scissor(Viewport scissor) {
DEBUG_MARKER()
setScissor(scissor);
}
@@ -3991,7 +3941,6 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
}
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
DEBUG_MARKER()
getShaderCompilerService().tick();
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(program);
+1 -1
View File
@@ -81,7 +81,7 @@ public:
const Platform::DriverConfig& driverConfig) noexcept;
class DebugMarker {
UTILS_UNUSED OpenGLDriver& driver;
OpenGLDriver& driver;
public:
DebugMarker(OpenGLDriver& driver, const char* string) noexcept;
~DebugMarker() noexcept;
@@ -143,25 +143,21 @@ void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQ
driver.runEveryNowAndThen([&context = mContext, weak]() -> bool {
auto state = weak.lock();
if (!state) {
// The timer query state has been destroyed on the way, very likely due to the IBL
// prefilter context destruction. We still return true to get this element removed from
// the query list.
return true;
if (state) {
GLuint available = 0;
context.procs.getQueryObjectuiv(state->gl.query, GL_QUERY_RESULT_AVAILABLE, &available);
CHECK_GL_ERROR(utils::slog.e)
if (!available) {
// we need to try this one again later
return false;
}
GLuint64 elapsedTime = 0;
// we won't end-up here if we're on ES and don't have GL_EXT_disjoint_timer_query
context.procs.getQueryObjectui64v(state->gl.query, GL_QUERY_RESULT, &elapsedTime);
state->elapsed.store((int64_t)elapsedTime, std::memory_order_relaxed);
} else {
state->elapsed.store(int64_t(TimerQueryResult::ERROR), std::memory_order_relaxed);
}
GLuint available = 0;
context.procs.getQueryObjectuiv(state->gl.query, GL_QUERY_RESULT_AVAILABLE, &available);
CHECK_GL_ERROR(utils::slog.e)
if (!available) {
// we need to try this one again later
return false;
}
GLuint64 elapsedTime = 0;
// we won't end-up here if we're on ES and don't have GL_EXT_disjoint_timer_query
context.procs.getQueryObjectui64v(state->gl.query, GL_QUERY_RESULT, &elapsedTime);
state->elapsed.store((int64_t)elapsedTime, std::memory_order_relaxed);
return true;
});
}
@@ -26,28 +26,16 @@
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <utils/JobSystem.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <utils/Systrace.h>
#include <array>
#include <cctype>
#include <chrono>
#include <mutex>
#include <memory>
#include <string>
#include <string_view>
#include <thread>
#include <utility>
#include <variant>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
using namespace utils;
@@ -488,12 +476,6 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
// check status of program linking and shader compilation, logs error and free all resources
// in case of error.
bool const success = checkProgramStatus(token);
// Unless we have matdbg, we panic if a program is invalid. Otherwise, we'd get a UB.
// The compilation error has been logged to log.e by this point.
FILAMENT_CHECK_POSTCONDITION(FILAMENT_ENABLE_MATDBG || success)
<< "OpenGL program " << token->name.c_str_safe() << " failed to link or compile";
if (UTILS_LIKELY(success)) {
program = token->gl.program;
// no need to keep the shaders around
@@ -597,31 +579,24 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
version = "#version 310 es\n";
}
std::array<std::string_view, 5> sources = {
version,
prolog,
specializationConstantString,
packingFunctions,
{ body.data(), body.size() - 1 } // null-terminated
const std::array<const char*, 5> sources = {
version.data(),
prolog.data(),
specializationConstantString.c_str(),
packingFunctions.data(),
body.data()
};
// Some of the sources may be zero-length. Remove them as to avoid passing lengths of
// zero to glShaderSource(). glShaderSource should work with lengths of zero, but some
// drivers instead interpret zero as a sentinel for a null-terminated string.
auto partitionPoint = std::stable_partition(
sources.begin(), sources.end(), [](std::string_view s) { return !s.empty(); });
size_t count = std::distance(sources.begin(), partitionPoint);
std::array<const char*, 5> shaderStrings;
std::array<GLint, 5> lengths;
for (size_t i = 0; i < count; i++) {
shaderStrings[i] = sources[i].data();
lengths[i] = sources[i].size();
}
const std::array<GLint, 5> lengths = {
(GLint)version.length(),
(GLint)prolog.length(),
(GLint)specializationConstantString.length(),
(GLint)packingFunctions.length(),
(GLint)body.length() - 1 // null terminated
};
GLuint const shaderId = glCreateShader(glShaderType);
glShaderSource(shaderId, count, shaderStrings.data(), lengths.data());
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
glCompileShader(shaderId);
#ifndef NDEBUG
@@ -59,11 +59,7 @@ VkExtent2D VulkanAttachment::getExtent2D() const {
VkImageView VulkanAttachment::getImageView() {
assert_invariant(texture);
VkImageSubresourceRange range = getSubresourceRange();
if (range.layerCount > 1) {
return texture->getMultiviewAttachmentView(range);
}
return texture->getAttachmentView(range);
return texture->getAttachmentView(getSubresourceRange());
}
bool VulkanAttachment::isDepth() const {
@@ -77,7 +73,7 @@ VkImageSubresourceRange VulkanAttachment::getSubresourceRange() const {
.baseMipLevel = uint32_t(level),
.levelCount = 1,
.baseArrayLayer = uint32_t(layer),
.layerCount = layerCount,
.layerCount = 1,
};
}
@@ -43,8 +43,6 @@ struct VulkanCommandBuffer;
struct VulkanAttachment {
VulkanTexture* texture = nullptr;
uint8_t level = 0;
uint8_t baseViewIndex = 0;
uint8_t layerCount = 1;
uint16_t layer = 0;
bool isDepth() const;
+1 -15
View File
@@ -595,8 +595,6 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
colorTargets[i] = {
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(color[i].handle),
.level = color[i].level,
.baseViewIndex = color[i].baseViewIndex,
.layerCount = layerCount,
.layer = color[i].layer,
};
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = colorTargets[i].getExtent2D();
@@ -611,8 +609,6 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
depthStencil[0] = {
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(depth.handle),
.level = depth.level,
.baseViewIndex = depth.baseViewIndex,
.layerCount = layerCount,
.layer = depth.layer,
};
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[0].getExtent2D();
@@ -625,8 +621,6 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
depthStencil[1] = {
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(stencil.handle),
.level = stencil.level,
.baseViewIndex = stencil.baseViewIndex,
.layerCount = layerCount,
.layer = stencil.layer,
};
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[1].getExtent2D();
@@ -643,7 +637,7 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
auto renderTarget = mResourceAllocator.construct<VulkanRenderTarget>(rth, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, width, height,
samples, colorTargets, depthStencil, mStagePool, layerCount);
samples, colorTargets, depthStencil, mStagePool);
mResourceManager.acquire(renderTarget);
}
@@ -1273,8 +1267,6 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
}
}
uint8_t const renderTargetLayerCount = rt->getLayerCount();
// Create the VkRenderPass or fetch it from cache.
VulkanFboCache::RenderPassKey rpkey = {
.initialColorLayoutMask = 0,
@@ -1285,12 +1277,10 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
.discardEnd = discardEndVal,
.samples = rt->getSamples(),
.subpassMask = uint8_t(params.subpassMask),
.viewCount = renderTargetLayerCount,
};
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
const VulkanAttachment& info = rt->getColor(i);
if (info.texture) {
assert_invariant(info.layerCount == renderTargetLayerCount);
rpkey.initialColorLayoutMask |= 1 << i;
rpkey.colorFormat[i] = info.getFormat();
if (rpkey.samples > 1 && info.texture->samples == 1) {
@@ -2032,10 +2022,6 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
void VulkanDriver::resetState(int) {
}
void VulkanDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mResourceAllocator.associateHandle(handleId, std::move(tag));
}
// explicit instantiation of the Dispatcher
template class ConcreteDispatcher<VulkanDriver>;
@@ -43,7 +43,6 @@ bool VulkanFboCache::RenderPassEq::operator()(const RenderPassKey& k1,
if (k1.samples != k2.samples) return false;
if (k1.needsResolveMask != k2.needsResolveMask) return false;
if (k1.subpassMask != k2.subpassMask) return false;
if (k1.viewCount != k2.viewCount) return false;
return true;
}
@@ -187,25 +186,6 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
.pDependencies = dependencies
};
VkRenderPassMultiviewCreateInfo multiviewCreateInfo = {};
uint32_t const subpassViewMask = (1 << config.viewCount) - 1;
// Prepare a view mask array for the maximum number of subpasses. All subpasses have all views
// activated.
uint32_t const viewMasks[2] = {subpassViewMask, subpassViewMask};
if (config.viewCount > 1) {
// Fill the multiview create info.
multiviewCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
multiviewCreateInfo.pNext = nullptr;
multiviewCreateInfo.subpassCount = hasSubpasses ? 2u : 1u;
multiviewCreateInfo.pViewMasks = viewMasks;
multiviewCreateInfo.dependencyCount = 0;
multiviewCreateInfo.pViewOffsets = nullptr;
multiviewCreateInfo.correlationMaskCount = 1;
multiviewCreateInfo.pCorrelationMasks = &subpassViewMask;
renderPassInfo.pNext = &multiviewCreateInfo;
}
int attachmentIndex = 0;
// Populate the Color Attachments.
+1 -1
View File
@@ -64,7 +64,7 @@ public:
uint8_t samples; // 1 byte
uint8_t needsResolveMask; // 1 byte
uint8_t subpassMask; // 1 byte
uint8_t viewCount; // 1 byte
uint8_t padding2; // 1 byte
};
struct RenderPassVal {
VkRenderPass handle;
@@ -323,12 +323,11 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
uint32_t width, uint32_t height, uint8_t samples,
VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount)
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool)
: HwRenderTarget(width, height),
VulkanResource(VulkanResourceType::RENDER_TARGET),
mOffscreen(true),
mSamples(samples),
mLayerCount(layerCount) {
mSamples(samples) {
for (int index = 0; index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; index++) {
mColor[index] = color[index];
}
+1 -3
View File
@@ -304,7 +304,7 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
VulkanContext const& context, VmaAllocator allocator,
VulkanCommands* commands, uint32_t width, uint32_t height,
uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount);
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool);
// Creates a special "default" render target (i.e. associated with the swap chain)
explicit VulkanRenderTarget();
@@ -319,7 +319,6 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
VulkanAttachment& getMsaaDepth();
uint8_t getColorTargetCount(const VulkanRenderPass& pass) const;
uint8_t getSamples() const { return mSamples; }
uint8_t getLayerCount() const { return mLayerCount; }
bool hasDepth() const { return mDepth.texture; }
bool isSwapChain() const { return !mOffscreen; }
void bindToSwapChain(VulkanSwapChain& surf);
@@ -331,7 +330,6 @@ private:
VulkanAttachment mMsaaDepthAttachment = {};
const bool mOffscreen : 1;
uint8_t mSamples : 7;
uint8_t mLayerCount = 1;
};
struct VulkanBufferObject;
@@ -53,7 +53,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::getOrCreatePipelin
// If a cached object exists, re-use it, otherwise create a new one.
if (PipelineMap::iterator pipelineIter = mPipelines.find(mPipelineRequirements);
pipelineIter != mPipelines.end()) {
auto& pipeline = pipelineIter.value();
auto& pipeline = pipelineIter->second;
pipeline.lastUsed = mCurrentTime;
return &pipeline;
}
@@ -242,7 +242,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
return nullptr;
}
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value();
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first->second;
}
void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
@@ -310,7 +310,7 @@ void VulkanPipelineCache::gc() noexcept {
// Any pipeline older than FVK_MAX_COMMAND_BUFFERS can be safely destroyed.
using ConstPipeIterator = decltype(mPipelines)::const_iterator;
for (ConstPipeIterator iter = mPipelines.begin(); iter != mPipelines.end();) {
const PipelineCacheEntry& cacheEntry = iter.value();
const PipelineCacheEntry& cacheEntry = iter->second;
if (cacheEntry.lastUsed + FVK_MAX_PIPELINE_AGE < mCurrentTime) {
vkDestroyPipeline(mDevice, iter->second.handle, VKALLOC);
iter = mPipelines.erase(iter);
@@ -33,11 +33,11 @@
#include <utils/compiler.h>
#include <utils/Hash.h>
#include <ankerl/unordered_dense.h>
#include <list>
#include <tsl/robin_map.h>
#include <type_traits>
#include <vector>
#include <unordered_map>
namespace filament::backend {
@@ -238,7 +238,10 @@ private:
// CACHE CONTAINERS
// ----------------
using PipelineMap = tsl::robin_map<PipelineKey, PipelineCacheEntry,
// using PipelineMap = tsl::robin_map<PipelineKey, PipelineCacheEntry,
// PipelineHashFn, PipelineEqual>;
using PipelineMap = ankerl::unordered_dense::map<PipelineKey, PipelineCacheEntry,
PipelineHashFn, PipelineEqual>;
private:
@@ -105,10 +105,6 @@ public:
mHandleAllocatorImpl.deallocate(handle, obj);
}
inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag));
}
private:
AllocatorImpl mHandleAllocatorImpl;
@@ -387,15 +387,12 @@ void VulkanTexture::setPrimaryRange(uint32_t minMiplevel, uint32_t maxMiplevel)
}
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) {
// Attachments should only have one mipmap level and one layer.
range.levelCount = 1;
range.layerCount = 1;
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D, {});
}
VkImageView VulkanTexture::getMultiviewAttachmentView(VkImageSubresourceRange range) {
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY, {});
}
VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range, VkImageViewType type) {
return getImageView(range, type, mSwizzle);
}
@@ -73,10 +73,6 @@ struct VulkanTexture : public HwTexture, VulkanResource {
// and the identity swizzle.
VkImageView getAttachmentView(VkImageSubresourceRange range);
// Gets or creates a cached VkImageView for a single subresource that can be used as a render
// target attachment when rendering with multiview.
VkImageView getMultiviewAttachmentView(VkImageSubresourceRange range);
// This is a workaround for the first few frames where we're waiting for the texture to actually
// be uploaded. In that case, we bind the sampler to an empty texture, but the corresponding
// imageView needs to be of the right type. Hence, we provide an option to indicate the
@@ -172,7 +172,7 @@ public:
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=" << mUnusedCount;
<< " capacity=" << mCapacity << " count=" << "%s";
mSize++;
return createSet(layout->bitmask, vkSet);
}
@@ -53,7 +53,7 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
}
if (PipelineLayoutMap::iterator iter = mPipelineLayouts.find(key); iter != mPipelineLayouts.end()) {
PipelineLayoutCacheEntry& entry = iter.value();
PipelineLayoutCacheEntry& entry = iter->second;
entry.lastUsed = mTimestamp++;
return entry.handle;
}
@@ -23,6 +23,7 @@
#include <utils/Hash.h>
#include <tsl/robin_map.h>
#include <ankerl/unordered_dense.h>
namespace filament::backend {
@@ -73,7 +74,10 @@ private:
}
};
using PipelineLayoutMap = tsl::robin_map<PipelineLayoutKey, PipelineLayoutCacheEntry,
// using PipelineLayoutMap = tsl::robin_map<PipelineLayoutKey, PipelineLayoutCacheEntry,
// PipelineLayoutKeyHashFn, PipelineLayoutKeyEqual>;
using PipelineLayoutMap = ankerl::unordered_dense::map<PipelineLayoutKey, PipelineLayoutCacheEntry,
PipelineLayoutKeyHashFn, PipelineLayoutKeyEqual>;
VkDevice mDevice;
+1 -16
View File
@@ -54,7 +54,7 @@ public:
using BufferDescriptor = backend::BufferDescriptor;
using BindingType = backend::BufferObjectBinding;
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -78,21 +78,6 @@ public:
*/
Builder& bindingType(BindingType bindingType) noexcept;
/**
* Associate an optional name with this BufferObject for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this BufferObject
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the BufferObject and returns a pointer to it. After creation, the buffer
* object is uninitialized. Use BufferObject::setBuffer() to initialize it.
+2 -28
View File
@@ -350,12 +350,9 @@ public:
uint32_t resourceAllocatorCacheSizeMB = 64;
/*
* This value determines how many frames texture entries are kept for in the cache. This
* is a soft limit, meaning some texture older than this are allowed to stay in the cache.
* Typically only one texture is evicted per frame.
* The default is 1.
* This value determines for how many frames are texture entries kept in the cache.
*/
uint32_t resourceAllocatorCacheMaxAge = 1;
uint32_t resourceAllocatorCacheMaxAge = 2;
/*
* Disable backend handles use-after-free checks.
@@ -896,29 +893,6 @@ public:
/** Tells whether an InstanceBuffer object is valid */
bool isValid(const InstanceBuffer* UTILS_NULLABLE p) const;
/**
* Retrieve the count of each resource tracked by Engine.
* This is intended for debugging.
* @{
*/
size_t getBufferObjectCount() const noexcept;
size_t getViewCount() const noexcept;
size_t getSceneCount() const noexcept;
size_t getSwapChainCount() const noexcept;
size_t getStreamCount() const noexcept;
size_t getIndexBufferCount() const noexcept;
size_t getSkinningBufferCount() const noexcept;
size_t getMorphTargetBufferCount() const noexcept;
size_t getInstanceBufferCount() const noexcept;
size_t getVertexBufferCount() const noexcept;
size_t getIndirectLightCount() const noexcept;
size_t getMaterialCount() const noexcept;
size_t getTextureCount() const noexcept;
size_t getSkyboxeCount() const noexcept;
size_t getColorGradingCount() const noexcept;
size_t getRenderTargetCount() const noexcept;
/** @} */
/**
* Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until
* all commands to this point are executed. Note that does guarantee that the
-18
View File
@@ -19,7 +19,6 @@
#include <utils/compiler.h>
#include <utils/PrivateImplementation.h>
#include <utils/CString.h>
#include <stddef.h>
@@ -55,23 +54,6 @@ public:
template<typename T>
using BuilderBase = utils::PrivateImplementation<T>;
// This needs to be public because it is used in the following template.
UTILS_PUBLIC void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept;
template <typename Builder>
class UTILS_PUBLIC BuilderNameMixin {
public:
Builder& name(const char* name, size_t len) noexcept {
builderMakeName(mName, name, len);
return static_cast<Builder&>(*this);
}
utils::CString const& getName() const noexcept { return mName; }
private:
utils::CString mName;
};
} // namespace filament
#endif // TNT_FILAMENT_FILAMENTAPI_H
+1 -16
View File
@@ -59,7 +59,7 @@ public:
UINT = uint8_t(backend::ElementType::UINT), //!< 32-bit indices
};
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -83,21 +83,6 @@ public:
*/
Builder& bufferType(IndexType indexType) noexcept;
/**
* Associate an optional name with this IndexBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this IndexBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the IndexBuffer object and returns a pointer to it. After creation, the index
* buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer.
+1 -16
View File
@@ -38,7 +38,7 @@ class UTILS_PUBLIC InstanceBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
@@ -70,21 +70,6 @@ public:
*/
Builder& localTransforms(math::mat4f const* UTILS_NULLABLE localTransforms) noexcept;
/**
* Associate an optional name with this InstanceBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this InstanceBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the InstanceBuffer object and returns a pointer to it.
*/
+1 -16
View File
@@ -39,7 +39,7 @@ class UTILS_PUBLIC MorphTargetBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -63,21 +63,6 @@ public:
*/
Builder& count(size_t count) noexcept;
/**
* Associate an optional name with this MorphTargetBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this MorphTargetBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the MorphTargetBuffer object and returns a pointer to it.
*
-8
View File
@@ -271,14 +271,6 @@ public:
*/
void setVsyncTime(uint64_t steadyClockTimeNano) noexcept;
/**
* Call skipFrame when momentarily skipping frames, for instance if the content of the
* scene doesn't change.
*
* @param vsyncSteadyClockTimeNano
*/
void skipFrame(uint64_t vsyncSteadyClockTimeNano = 0u);
/**
* Set-up a frame for this Renderer.
*
+1 -16
View File
@@ -39,7 +39,7 @@ class UTILS_PUBLIC SkinningBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -69,21 +69,6 @@ public:
*/
Builder& initialize(bool initialize = true) noexcept;
/**
* Associate an optional name with this SkinningBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this SkinningBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the SkinningBuffer object and returns a pointer to it.
*
+1 -16
View File
@@ -94,7 +94,7 @@ public:
*
* To create a NATIVE stream, call the <pre>stream</pre> method on the builder.
*/
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -136,21 +136,6 @@ public:
*/
Builder& height(uint32_t height) noexcept;
/**
* Associate an optional name with this Stream for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this Stream
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the Stream object and returns a pointer to it.
*
+1 -16
View File
@@ -112,7 +112,7 @@ public:
//! Use Builder to construct a Texture object instance
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -202,21 +202,6 @@ public:
*/
Builder& swizzle(Swizzle r, Swizzle g, Swizzle b, Swizzle a) noexcept;
/**
* Associate an optional name with this Texture for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this Texture
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the Texture object and returns a pointer to it.
*
+1 -16
View File
@@ -61,7 +61,7 @@ public:
using AttributeType = backend::ElementType;
using BufferDescriptor = backend::BufferDescriptor;
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -158,21 +158,6 @@ public:
*/
Builder& advancedSkinning(bool enabled) noexcept;
/**
* Associate an optional name with this VertexBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this VertexBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the VertexBuffer object and returns a pointer to it.
*
+1 -14
View File
@@ -24,7 +24,6 @@
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/FixedCapacityVector.h>
#include <math/mathfwd.h>
@@ -41,7 +40,6 @@ class CallbackHandler;
class Camera;
class ColorGrading;
class Engine;
class MaterialInstance;
class RenderTarget;
class Scene;
@@ -728,7 +726,7 @@ public:
void setDebugCamera(Camera* UTILS_NULLABLE camera) noexcept;
//! debugging: returns a Camera from the point of view of *the* dominant directional light used for shadowing.
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept;
Camera const* UTILS_NULLABLE getDirectionalShadowCamera() const noexcept;
/** Result of a picking query */
@@ -880,17 +878,6 @@ public:
*/
utils::Entity getFogEntity() const noexcept;
/**
* When certain temporal features are used (e.g.: TAA or Screen-space reflections), the view
* keeps a history of previous frame renders associated with the Renderer the view was last
* used with. When switching Renderer, it may be necessary to clear that history by calling
* this method. Similarly, if the whole content of the screen change, like when a cut-scene
* starts, clearing the history might be needed to avoid artifacts due to the previous frame
* being very different.
*/
void clearFrameHistory(Engine& engine) noexcept;
/**
* List of available ambient occlusion techniques
* @deprecated use AmbientOcclusionOptions::enabled instead
-67
View File
@@ -16,8 +16,6 @@
#include "details/Engine.h"
#include "ResourceAllocator.h"
#include "details/BufferObject.h"
#include "details/Camera.h"
#include "details/Fence.h"
@@ -268,71 +266,6 @@ bool Engine::isValid(const InstanceBuffer* p) const {
return downcast(this)->isValid(downcast(p));
}
size_t Engine::getBufferObjectCount() const noexcept {
return downcast(this)->getBufferObjectCount();
}
size_t Engine::getViewCount() const noexcept {
return downcast(this)->getViewCount();
}
size_t Engine::getSceneCount() const noexcept {
return downcast(this)->getSceneCount();
}
size_t Engine::getSwapChainCount() const noexcept {
return downcast(this)->getSwapChainCount();
}
size_t Engine::getStreamCount() const noexcept {
return downcast(this)->getStreamCount();
}
size_t Engine::getIndexBufferCount() const noexcept {
return downcast(this)->getIndexBufferCount();
}
size_t Engine::getSkinningBufferCount() const noexcept {
return downcast(this)->getSkinningBufferCount();
}
size_t Engine::getMorphTargetBufferCount() const noexcept {
return downcast(this)->getMorphTargetBufferCount();
}
size_t Engine::getInstanceBufferCount() const noexcept {
return downcast(this)->getInstanceBufferCount();
}
size_t Engine::getVertexBufferCount() const noexcept {
return downcast(this)->getVertexBufferCount();
}
size_t Engine::getIndirectLightCount() const noexcept {
return downcast(this)->getIndirectLightCount();
}
size_t Engine::getMaterialCount() const noexcept {
return downcast(this)->getMaterialCount();
}
size_t Engine::getTextureCount() const noexcept {
return downcast(this)->getTextureCount();
}
size_t Engine::getSkyboxeCount() const noexcept {
return downcast(this)->getSkyboxeCount();
}
size_t Engine::getColorGradingCount() const noexcept {
return downcast(this)->getColorGradingCount();
}
size_t Engine::getRenderTargetCount() const noexcept {
return downcast(this)->getRenderTargetCount();
}
void Engine::flushAndWait() {
downcast(this)->flushAndWait();
}
-31
View File
@@ -1,31 +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 <filament/FilamentAPI.h>
#include <algorithm>
namespace filament {
void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept {
if (!name) {
return;
}
size_t const length = std::min(len, size_t { 128u });
outName = utils::CString(name, length);
}
} // namespace filament
-3
View File
@@ -21,9 +21,6 @@
#include <fg/FrameGraphTexture.h>
#include <math/mat4.h>
#include <math/vec2.h>
#include <stdint.h>
namespace filament {
+19 -26
View File
@@ -25,7 +25,6 @@
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <utils/Systrace.h>
#include <algorithm>
#include <atomic>
@@ -79,33 +78,25 @@ void FrameInfoManager::beginFrame(DriverApi& driver, Config const& config, uint3
});
// now is a good time to check the oldest active query
while (mLast != mIndex) {
uint64_t elapsed = 0;
TimerQueryResult const result = driver.getTimerQueryValue(mQueries[mLast].handle, &elapsed);
switch (result) {
case TimerQueryResult::NOT_READY:
// nothing to do
break;
case TimerQueryResult::ERROR:
mLast = (mLast + 1) % POOL_COUNT;
break;
case TimerQueryResult::AVAILABLE: {
SYSTRACE_CONTEXT();
SYSTRACE_VALUE32("FrameInfo::elapsed", uint32_t(elapsed));
// conversion to our duration happens here
pFront = mQueries[mLast].pInfo;
pFront->frameTime = std::chrono::duration<uint64_t, std::nano>(elapsed);
mLast = (mLast + 1) % POOL_COUNT;
denoiseFrameTime(history, config);
break;
}
}
if (result != TimerQueryResult::AVAILABLE) {
uint64_t elapsed = 0;
TimerQueryResult const result = driver.getTimerQueryValue(mQueries[mLast].handle, &elapsed);
switch (result) {
case TimerQueryResult::NOT_READY:
// nothing to do
break;
case TimerQueryResult::ERROR:
mLast = (mLast + 1) % POOL_COUNT;
break;
case TimerQueryResult::AVAILABLE:
// conversion to our duration happens here
pFront = mQueries[mLast].pInfo;
pFront->frameTime = std::chrono::duration<uint64_t, std::nano>(elapsed);
mLast = (mLast + 1) % POOL_COUNT;
denoiseFrameTime(config);
break;
}
// read the pending timer queries until we find one that's not ready
}
// keep this just for debugging
if constexpr (false) {
using namespace utils;
@@ -138,13 +129,15 @@ void FrameInfoManager::endFrame(DriverApi& driver) noexcept {
mIndex = (mIndex + 1) % POOL_COUNT;
}
void FrameInfoManager::denoiseFrameTime(FrameHistoryQueue& history, Config const& config) noexcept {
void FrameInfoManager::denoiseFrameTime(Config const& config) noexcept {
auto& history = mFrameTimeHistory;
assert_invariant(!history.empty());
// find the first slot that has a valid frame duration
size_t first = history.size();
for (size_t i = 0, c = history.size(); i < c; ++i) {
if (history[i].frameTime != duration(0)) {
assert_invariant(std::addressof(history[i]) == pFront);
first = i;
break;
}
+5 -6
View File
@@ -163,16 +163,15 @@ public:
// call this immediately before "swap buffers"
void endFrame(backend::DriverApi& driver) noexcept;
details::FrameInfo getLastFrameInfo() const noexcept {
// if pFront is not set yet, return FrameInfo(). But the `valid` field will be false in this case.
return pFront ? *pFront : details::FrameInfo{};
details::FrameInfo const& getLastFrameInfo() const noexcept {
// if pFront is not set yet, return front() but in this case front().valid will be false
return pFront ? *pFront : mFrameTimeHistory.front();
}
utils::FixedCapacityVector<Renderer::FrameInfo> getFrameInfoHistory(size_t historySize) const noexcept;
private:
using FrameHistoryQueue = CircularQueue<FrameInfoImpl, MAX_FRAMETIME_HISTORY>;
static void denoiseFrameTime(FrameHistoryQueue& history, Config const& config) noexcept;
void denoiseFrameTime(Config const& config) noexcept;
struct Query {
backend::Handle<backend::HwTimerQuery> handle{};
FrameInfoImpl* pInfo = nullptr;
@@ -181,7 +180,7 @@ private:
uint32_t mIndex = 0; // index of current query
uint32_t mLast = 0; // index of oldest query still active
FrameInfoImpl* pFront = nullptr; // the most recent slot with a valid frame time
FrameHistoryQueue mFrameTimeHistory;
CircularQueue<FrameInfoImpl, MAX_FRAMETIME_HISTORY> mFrameTimeHistory;
};
+18 -25
View File
@@ -16,22 +16,16 @@
#include "FrameSkipper.h"
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/debug.h>
#include <algorithm>
#include <stddef.h>
namespace filament {
using namespace utils;
using namespace backend;
FrameSkipper::FrameSkipper(size_t latency) noexcept
: mLast(std::max(latency, MAX_FRAME_LATENCY) - 1) {
: mLast(latency - 1) {
assert_invariant(latency <= MAX_FRAME_LATENCY);
}
@@ -47,32 +41,31 @@ void FrameSkipper::terminate(DriverApi& driver) noexcept {
bool FrameSkipper::beginFrame(DriverApi& driver) noexcept {
auto& fences = mDelayedFences;
if (fences.front()) {
// Do we have a latency old fence?
auto status = driver.getFenceStatus(fences.front());
if (UTILS_UNLIKELY(status == FenceStatus::TIMEOUT_EXPIRED)) {
// The fence hasn't signaled yet, skip this frame
auto fence = fences.front();
if (fence) {
auto status = driver.getFenceStatus(fence);
if (status == FenceStatus::TIMEOUT_EXPIRED) {
// Sync not ready, skip frame
return false;
}
assert_invariant(status == FenceStatus::CONDITION_SATISFIED);
driver.destroyFence(fence);
}
// shift all fences down by 1
std::move(fences.begin() + 1, fences.end(), fences.begin());
fences.back() = {};
return true;
}
void FrameSkipper::endFrame(DriverApi& driver) noexcept {
auto& fences = mDelayedFences;
size_t const last = mLast;
// pop the oldest fence and advance the other ones
if (fences.front()) {
driver.destroyFence(fences.front());
// If the user produced a new frame despite the fact that the previous one wasn't finished
// (i.e. FrameSkipper::beginFrame() returned false), we need to make sure to replace
// a fence that might be here already)
auto& fence = mDelayedFences[mLast];
if (fence) {
driver.destroyFence(fence);
}
std::move(fences.begin() + 1, fences.end(), fences.begin());
// add a new fence to the end
assert_invariant(!fences[last]);
fences[last] = driver.createFence();
fence = driver.createFence();
}
} // namespace filament
+1 -4
View File
@@ -22,9 +22,6 @@
#include <array>
#include <stddef.h>
#include <stdint.h>
namespace filament {
/*
@@ -63,7 +60,7 @@ public:
private:
using Container = std::array<backend::Handle<backend::HwFence>, MAX_FRAME_LATENCY>;
mutable Container mDelayedFences{};
uint8_t const mLast;
size_t mLast;
};
} // namespace filament
-6
View File
@@ -354,12 +354,6 @@ bool MaterialParser::getSpecularAntiAliasingThreshold(float* value) const noexce
return mImpl.getFromSimpleChunk(ChunkType::MaterialSpecularAntiAliasingThreshold, value);
}
bool MaterialParser::getStereoscopicType(backend::StereoscopicType* value) const noexcept {
static_assert(sizeof(StereoscopicType) == sizeof(uint8_t),
"StereoscopicType expected size is wrong");
return mImpl.getFromSimpleChunk(ChunkType::MaterialStereoscopicType, reinterpret_cast<uint8_t*>(value));
}
bool MaterialParser::getRequiredAttributes(AttributeBitset* value) const noexcept {
uint32_t rawAttributes = 0;
if (!mImpl.getFromSimpleChunk(ChunkType::MaterialRequiredAttributes, &rawAttributes)) {
-1
View File
@@ -122,7 +122,6 @@ public:
bool hasSpecularAntiAliasing(bool* value) const noexcept;
bool getSpecularAntiAliasingVariance(float* value) const noexcept;
bool getSpecularAntiAliasingThreshold(float* value) const noexcept;
bool getStereoscopicType(backend::StereoscopicType*) const noexcept;
bool getShader(filaflat::ShaderContent& shader, backend::ShaderModel shaderModel,
Variant variant, backend::ShaderStage stage) noexcept;
+6 -14
View File
@@ -494,6 +494,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
struct StructureMipmapData {
FrameGraphId<FrameGraphTexture> depth;
uint32_t rt[8];
};
fg.addPass<StructureMipmapData>("StructureMipmap",
@@ -504,7 +505,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
.level = uint8_t(i)
});
out = builder.write(out, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
builder.declareRenderPass("Structure mip target", {
data.rt[i - 1] = builder.declareRenderPass("Structure mip target", {
.attachments = { .depth = out }
});
}
@@ -2945,10 +2946,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
1.0f / outputDesc.height});
}
if (blitterNames[index] == "blitLow") {
mi->setParameter("levelOfDetail", 0.0f);
}
mi->setParameter("viewport", float4{
(float)vp.left / inputDesc.width,
(float)vp.bottom / inputDesc.height,
@@ -3007,8 +3004,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
SamplerMagFilter filterMag,
SamplerMinFilter filterMin) noexcept {
uint32_t const layer = fg.getSubResourceDescriptor(input).layer;
float const levelOfDetail = fg.getSubResourceDescriptor(input).level;
// TODO: add support for sub-resources
assert_invariant(fg.getSubResourceDescriptor(input).layer == 0);
assert_invariant(fg.getSubResourceDescriptor(input).level == 0);
struct QuadBlitData {
FrameGraphId<FrameGraphTexture> input;
@@ -3034,8 +3032,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
// --------------------------------------------------------------------------------
// set uniforms
PostProcessMaterial const& material =
getPostProcessMaterial(layer ? "blitArray" : "blitLow");
PostProcessMaterial const& material = getPostProcessMaterial("blitLow");
auto* mi = material.getMaterialInstance(mEngine);
mi->setParameter("color", color, {
.filterMag = filterMag,
@@ -3047,10 +3044,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height
});
mi->setParameter("levelOfDetail", levelOfDetail);
if (layer) {
mi->setParameter("layerIndex", layer);
}
mi->commit(driver);
mi->use(driver);
@@ -3193,7 +3186,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
assert_invariant(dst);
assert_invariant(srcDesc.format == dstDesc.format);
assert_invariant(srcDesc.width == dstDesc.width && srcDesc.height == dstDesc.height);
assert_invariant(srcDesc.samples > 1 && dstDesc.samples <= 1);
driver.resolve(
dst, dstSubDesc.level, dstSubDesc.layer,
src, srcSubDesc.level, srcSubDesc.layer);
+29 -32
View File
@@ -118,11 +118,10 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
uint32_t const customCommandCount =
builder.mCustomCommands.has_value() ? builder.mCustomCommands->size() : 0;
Command* const commandBegin = builder.mArena.alloc<Command>(commandCount + customCommandCount);
Command* commandEnd = commandBegin + (commandCount + customCommandCount);
assert_invariant(commandBegin);
Command* const curr = builder.mArena.alloc<Command>(commandCount + customCommandCount);
assert_invariant(curr);
if (UTILS_UNLIKELY(builder.mArena.getAllocator().isHeapAllocation(commandBegin))) {
if (UTILS_UNLIKELY(builder.mArena.getAllocator().isHeapAllocation(curr))) {
static bool sLogOnce = true;
if (UTILS_UNLIKELY(sLogOnce)) {
sLogOnce = false;
@@ -131,7 +130,10 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
}
}
appendCommands(engine, { commandBegin, commandCount },
mCommandBegin = curr;
mCommandEnd = curr + commandCount + customCommandCount;
appendCommands(engine, { curr, commandCount },
builder.mUboHandle,
builder.mVisibleRenderables,
builder.mCommandTypeFlags,
@@ -142,36 +144,32 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
builder.mCameraForwardVector);
if (builder.mCustomCommands.has_value()) {
Command* p = commandBegin + commandCount;
Command* p = curr + commandCount;
for (auto [channel, passId, command, order, fn]: builder.mCustomCommands.value()) {
appendCustomCommand(p++, channel, passId, command, order, fn);
}
}
// sort commands once we're done adding commands
commandEnd = resize(builder.mArena,
RenderPass::sortCommands(commandBegin, commandEnd));
sortCommands(builder.mArena);
if (engine.isAutomaticInstancingEnabled()) {
int32_t stereoscopicEyeCount = 1;
if (builder.mFlags & IS_INSTANCED_STEREOSCOPIC) {
stereoscopicEyeCount *= engine.getConfig().stereoscopicEyeCount;
}
commandEnd = resize(builder.mArena,
instanceify(engine, commandBegin, commandEnd, stereoscopicEyeCount));
instanceify(engine, builder.mArena, stereoscopicEyeCount);
}
// these are `const` from this point on...
mCommandBegin = commandBegin;
mCommandEnd = commandEnd;
}
// this destructor is actually heavy because it inlines ~vector<>
RenderPass::~RenderPass() noexcept = default;
RenderPass::Command* RenderPass::resize(Arena& arena, Command* const last) noexcept {
arena.rewind(last);
return last;
void RenderPass::resize(Arena& arena, size_t count) noexcept {
if (mCommandBegin) {
mCommandEnd = mCommandBegin + count;
arena.rewind(mCommandEnd);
}
}
void RenderPass::appendCommands(FEngine& engine,
@@ -262,19 +260,18 @@ void RenderPass::appendCustomCommand(Command* commands,
commands->key = cmd;
}
RenderPass::Command* RenderPass::sortCommands(
Command* const begin, Command* const end) noexcept {
SYSTRACE_NAME("sort commands");
void RenderPass::sortCommands(Arena& arena) noexcept {
SYSTRACE_NAME("sort and trim commands");
std::sort(begin, end);
std::sort(mCommandBegin, mCommandEnd);
// find the last command
Command* const last = std::partition_point(begin, end,
Command const* const last = std::partition_point(mCommandBegin, mCommandEnd,
[](Command const& c) {
return c.key != uint64_t(Pass::SENTINEL);
});
return last;
resize(arena, uint32_t(last - mCommandBegin));
}
void RenderPass::execute(RenderPass const& pass,
@@ -287,9 +284,7 @@ void RenderPass::execute(RenderPass const& pass,
driver.endRenderPass();
}
RenderPass::Command* RenderPass::instanceify(FEngine& engine,
Command* curr, Command* const last,
int32_t eyeCount) const noexcept {
void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) noexcept {
SYSTRACE_NAME("instanceify");
// instanceify works by scanning the **sorted** command stream, looking for repeat draw
@@ -302,12 +297,14 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
UTILS_UNUSED uint32_t drawCallsSavedCount = 0;
Command* curr = mCommandBegin;
Command* const last = mCommandEnd;
Command* firstSentinel = nullptr;
PerRenderableData const* uboData = nullptr;
PerRenderableData* stagingBuffer = nullptr;
uint32_t stagingBufferSize = 0;
uint32_t instancedPrimitiveOffset = 0;
size_t const count = last - curr;
// TODO: for the case of instancing we could actually use 128 instead of 64 instances
constexpr size_t maxInstanceCount = CONFIG_MAX_INSTANCES;
@@ -346,6 +343,7 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
if (UTILS_UNLIKELY(!stagingBuffer)) {
// create a temporary UBO for instancing
size_t const count = mCommandEnd - mCommandBegin;
mInstancedUboHandle = BufferObjectSharedHandle{
engine.getDriverApi().createBufferObject(
count * sizeof(PerRenderableData) + sizeof(PerRenderableUib),
@@ -387,7 +385,7 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
if (UTILS_UNLIKELY(firstSentinel)) {
//slog.d << "auto-instancing, saving " << drawCallsSavedCount << " draw calls, out of "
// << count << io::endl;
// << mCommandEnd - mCommandBegin << io::endl;
// we have instanced primitives
DriverApi& driver = engine.getDriverApi();
@@ -403,15 +401,14 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
stagingBuffer = nullptr;
// remove all the canceled commands
auto lastCommand = std::remove_if(firstSentinel, last, [](auto const& command) {
auto lastCommand = std::remove_if(firstSentinel, mCommandEnd, [](auto const& command) {
return command.key == uint64_t(Pass::SENTINEL);
});
return lastCommand;
resize(arena, uint32_t(lastCommand - mCommandBegin));
}
assert_invariant(stagingBuffer == nullptr);
return last;
}
@@ -610,7 +607,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
// Here, objects close to the camera (but behind) will be drawn first.
// An alternative that keeps the mathematical ordering is given here:
// distanceBits ^= ((int32_t(distanceBits) >> 31) | 0x80000000u);
float const distance = -(dot(soaWorldAABBCenter[i], cameraForward) - cameraPositionDotCameraForward);
float const distance = -dot(soaWorldAABBCenter[i], cameraForward) - cameraPositionDotCameraForward;
uint32_t const distanceBits = reinterpret_cast<uint32_t const&>(distance);
// calculate the per-primitive face winding order inversion
+6 -9
View File
@@ -408,16 +408,13 @@ private:
uint8_t channel, Pass pass, CustomCommand custom, uint32_t order,
Executor::CustomCommandFn command);
static Command* resize(Arena& arena, Command* const last) noexcept;
void resize(Arena& arena, size_t count) noexcept;
// sorts commands then trims sentinels
static Command* sortCommands(
Command* begin, Command* end) noexcept;
void sortCommands(Arena& arena) noexcept;
// instanceify commands then trims sentinels
RenderPass::Command* instanceify(FEngine& engine,
Command* begin, Command* end,
int32_t eyeCount) const noexcept;
void instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) noexcept;
// We choose the command count per job to minimize JobSystem overhead.
static constexpr size_t JOBS_PARALLEL_FOR_COMMANDS_COUNT = 128;
@@ -451,10 +448,10 @@ private:
FScene::RenderableSoa const& mRenderableSoa;
backend::Viewport const mScissorViewport;
Command const* /* const */ mCommandBegin = nullptr; // Pointer to the first command
Command const* /* const */ mCommandEnd = nullptr; // Pointer to one past the last command
Command* mCommandBegin = nullptr; // Pointer to the first command
Command* mCommandEnd = nullptr; // Pointer to one past the last command
// a UBO for instanced primitives
mutable BufferObjectSharedHandle mInstancedUboHandle;
BufferObjectSharedHandle mInstancedUboHandle;
// a vector for our custom commands
using CustomCommandVector = std::vector<Executor::CustomCommandFn,
utils::STLAllocator<Executor::CustomCommandFn, LinearAllocatorArena>>;
+1 -6
View File
@@ -16,10 +16,9 @@
#include <filament/Renderer.h>
#include "ResourceAllocator.h"
#include "details/Renderer.h"
#include "details/Engine.h"
#include "details/Renderer.h"
#include "details/View.h"
#include <utils/FixedCapacityVector.h>
@@ -46,10 +45,6 @@ void Renderer::setPresentationTime(int64_t monotonic_clock_ns) {
downcast(this)->setPresentationTime(monotonic_clock_ns);
}
void Renderer::skipFrame(uint64_t vsyncSteadyClockTimeNano) {
downcast(this)->skipFrame(vsyncSteadyClockTimeNano);
}
bool Renderer::beginFrame(SwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano) {
return downcast(this)->beginFrame(downcast(swapChain), vsyncSteadyClockTimeNano);
}
+45 -43
View File
@@ -40,7 +40,6 @@
#include <utils/Panic.h>
#include <algorithm>
#include <optional>
#include <utility>
#include <stddef.h>
@@ -51,9 +50,8 @@ namespace filament {
using namespace backend;
using namespace math;
RendererUtils::ColorPassOutput RendererUtils::colorPass(
FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
FrameGraph& fg, const char* name, FEngine& engine, FView const& view,
ColorPassInput const& colorPassInput,
FrameGraphTexture::Descriptor const& colorBufferDesc,
ColorPassConfig const& config, PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass::Executor passExecutor) noexcept {
@@ -69,28 +67,31 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass(
FrameGraphId<FrameGraphTexture> structure;
};
Blackboard& blackboard = fg.getBlackboard();
auto& colorPass = fg.addPass<ColorPassData>(name,
[&](FrameGraph::Builder& builder, ColorPassData& data) {
TargetBufferFlags const clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
TargetBufferFlags clearDepthFlags = config.clearFlags & TargetBufferFlags::DEPTH;
TargetBufferFlags clearStencilFlags = config.clearFlags & TargetBufferFlags::STENCIL;
uint8_t layerCount = 0;
data.color = colorPassInput.linearColor;
data.depth = colorPassInput.depth;
data.shadows = colorPassInput.shadows;
data.ssao = colorPassInput.ssao;
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
data.ssao = blackboard.get<FrameGraphTexture>("ssao");
data.color = blackboard.get<FrameGraphTexture>("color");
data.depth = blackboard.get<FrameGraphTexture>("depth");
// Screen-space reflection or refractions
if (config.hasScreenSpaceReflectionsOrRefractions) {
data.ssr = colorPassInput.ssr;
data.ssr = blackboard.get<FrameGraphTexture>("ssr");
if (data.ssr) {
data.ssr = builder.sample(data.ssr);
}
}
if (config.hasContactShadows) {
data.structure = colorPassInput.structure;
data.structure = blackboard.get<FrameGraphTexture>("structure");
assert_invariant(data.structure);
data.structure = builder.sample(data.structure);
}
@@ -153,8 +154,6 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass(
}
if (colorGradingConfig.asSubpass) {
assert_invariant(config.msaa <= 1);
assert_invariant(colorBufferDesc.samples <= 1);
data.output = builder.createTexture("Tonemapped Buffer", {
.width = colorBufferDesc.width,
.height = colorBufferDesc.height,
@@ -173,6 +172,9 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass(
data.color = builder.write(data.color, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.depth = builder.write(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
if (engine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW) {
layerCount = engine.getConfig().stereoscopicEyeCount;
}
/*
* There is a bit of magic happening here regarding the viewport used.
@@ -194,8 +196,9 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass(
.stencil = data.stencil },
.clearColor = config.clearColor,
.samples = config.msaa,
.layerCount = static_cast<uint8_t>(colorBufferDesc.depth),
.layerCount = layerCount,
.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags});
blackboard["depth"] = data.depth;
},
[=, passExecutor = std::move(passExecutor), &view, &engine](FrameGraphResources const& resources,
ColorPassData const& data, DriverApi& driver) {
@@ -261,21 +264,24 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass(
}
);
return {
.linearColor = colorPass->color,
.tonemappedColor = colorPass->output, // can be null
.depth = colorPass->depth
};
// when color grading is done as a subpass, the output of the color-pass is the ldr buffer
auto output = colorGradingConfig.asSubpass ? colorPass->output : colorPass->color;
blackboard["color"] = output;
return output;
}
std::optional<RendererUtils::ColorPassOutput> RendererUtils::refractionPass(
std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
FrameGraph& fg, FEngine& engine, FView const& view,
ColorPassInput colorPassInput,
ColorPassConfig config,
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass const& pass) noexcept {
auto& blackboard = fg.getBlackboard();
auto input = blackboard.get<FrameGraphTexture>("color");
FrameGraphId<FrameGraphTexture> output;
// find the first refractive object in channel 2
RenderPass::Command const* const refraction = std::partition_point(pass.begin(), pass.end(),
[](auto const& command) {
@@ -290,35 +296,34 @@ std::optional<RendererUtils::ColorPassOutput> RendererUtils::refractionPass(
// if there wasn't any refractive object, just skip everything below.
if (UTILS_UNLIKELY(hasScreenSpaceRefraction)) {
assert_invariant(!colorPassInput.linearColor);
assert_invariant(!colorPassInput.depth);
PostProcessManager& ppm = engine.getPostProcessManager();
// clear the color/depth buffers, which will orphan (and cull) the color pass
input.clear();
blackboard.remove("color");
blackboard.remove("depth");
config.hasScreenSpaceReflectionsOrRefractions = true;
PostProcessManager& ppm = engine.getPostProcessManager();
auto const opaquePassOutput = RendererUtils::colorPass(fg,
"Color Pass (opaque)", engine, view, colorPassInput, {
input = RendererUtils::colorPass(fg, "Color Pass (opaque)", engine, view, {
// When rendering the opaques, we need to conserve the sample buffer,
// so create a config that specifies the sample count.
.width = config.physicalViewport.width,
.height = config.physicalViewport.height,
.samples = config.msaa,
.format = config.hdrFormat
},
config, { .asSubpass = false, .customResolve = false },
}, config, { .asSubpass = false },
pass.getExecutor(pass.begin(), refraction));
// generate the mipmap chain
PostProcessManager::generateMipmapSSR(ppm, fg,
opaquePassOutput.linearColor,
ssrConfig.refraction,
true, ssrConfig);
input, ssrConfig.refraction, true, ssrConfig);
// Now we're doing the refraction pass proper.
// This uses the same framebuffer (color and depth) used by the opaque pass.
// For this reason, the `colorBufferDesc` parameter of colorPass() below is only used for
// the width and height.
colorPassInput.linearColor = opaquePassOutput.linearColor;
colorPassInput.depth = opaquePassOutput.depth;
// This uses the same framebuffer (color and depth) used by the opaque pass. This happens
// automatically because these are set in the Blackboard (they were set by the opaque
// pass). For this reason, `desc` below is only used in colorPass() for the width and
// height.
// Since we're reusing the existing target we don't want to clear any of its buffer.
// Important: if this target ended up being an imported target, then the clearFlags
@@ -326,25 +331,22 @@ std::optional<RendererUtils::ColorPassOutput> RendererUtils::refractionPass(
// and we'd end up clearing the opaque pass. This scenario never happens because it is
// prevented in Renderer.cpp's final blit.
config.clearFlags = TargetBufferFlags::NONE;
auto transparentPassOutput = RendererUtils::colorPass(fg, "Color Pass (transparent)",
engine, view, colorPassInput, {
output = RendererUtils::colorPass(fg, "Color Pass (transparent)", engine, view, {
.width = config.physicalViewport.width,
.height = config.physicalViewport.height },
config, colorGradingConfig,
pass.getExecutor(refraction, pass.end()));
config, colorGradingConfig, pass.getExecutor(refraction, pass.end()));
if (config.msaa > 1 && !colorGradingConfig.asSubpass) {
// We need to do a resolve here because later passes (such as color grading or DoF) will
// need to sample from 'output'. However, because we have MSAA, we know we're not
// sampleable. And this is because in the SSR case, we had to use a renderbuffer to
// conserve the multi-sample buffer.
transparentPassOutput.linearColor = ppm.resolve(fg, "Resolved Color Buffer",
transparentPassOutput.linearColor, { .levels = 1 });
output = ppm.resolve(fg, "Resolved Color Buffer", output, { .levels = 1 });
}
return transparentPassOutput;
} else {
output = input;
}
return std::nullopt;
return { output, hasScreenSpaceRefraction };
}
UTILS_NOINLINE
+2 -24
View File
@@ -21,19 +21,14 @@
#include "RenderPass.h"
#include "fg/FrameGraphId.h"
#include "fg/FrameGraphTexture.h"
#include <filament/Viewport.h>
#include <backend/DriverEnums.h>
#include <backend/PixelBufferDescriptor.h>
#include <math/vec2.h>
#include <math/vec4.h>
#include <stdint.h>
#include <optional>
#include <utility>
namespace filament {
@@ -76,32 +71,15 @@ public:
bool screenSpaceReflectionHistoryNotReady;
};
struct ColorPassInput {
FrameGraphId<FrameGraphTexture> linearColor;
FrameGraphId<FrameGraphTexture> tonemappedColor;
FrameGraphId<FrameGraphTexture> depth;
FrameGraphId<FrameGraphTexture> shadows;
FrameGraphId<FrameGraphTexture> ssao;
FrameGraphId<FrameGraphTexture> ssr;
FrameGraphId<FrameGraphTexture> structure;
};
struct ColorPassOutput {
FrameGraphId<FrameGraphTexture> linearColor;
FrameGraphId<FrameGraphTexture> tonemappedColor;
FrameGraphId<FrameGraphTexture> depth;
};
static ColorPassOutput colorPass(
static FrameGraphId<FrameGraphTexture> colorPass(
FrameGraph& fg, const char* name, FEngine& engine, FView const& view,
ColorPassInput const& colorPassInput,
FrameGraphTexture::Descriptor const& colorBufferDesc,
ColorPassConfig const& config,
PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass::Executor passExecutor) noexcept;
static std::optional<RendererUtils::ColorPassOutput> refractionPass(
static std::pair<FrameGraphId<FrameGraphTexture>, bool> refractionPass(
FrameGraph& fg, FEngine& engine, FView const& view,
ColorPassInput colorPassInput,
ColorPassConfig config,
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
PostProcessManager::ColorGradingConfig colorGradingConfig,
+32 -131
View File
@@ -27,18 +27,15 @@
#include "private/backend/DriverApi.h"
#include <utils/algorithm.h>
#include <utils/bitset.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <array>
#include <algorithm>
#include <iterator>
#include <memory>
#include <optional>
#include <utility>
#include <stddef.h>
@@ -92,15 +89,8 @@ void ResourceAllocator::AssociativeContainer<K, V, H>::emplace(ARGS&& ... args)
}
// ------------------------------------------------------------------------------------------------
ResourceAllocatorInterface::~ResourceAllocatorInterface() = default;
// ------------------------------------------------------------------------------------------------
ResourceAllocatorDisposerInterface::~ResourceAllocatorDisposerInterface() = default;
// ------------------------------------------------------------------------------------------------
size_t ResourceAllocator::TextureKey::getSize() const noexcept {
size_t const pixelCount = width * height * depth;
size_t size = pixelCount * FTexture::getFormatSize(format);
@@ -120,22 +110,16 @@ size_t ResourceAllocator::TextureKey::getSize() const noexcept {
ResourceAllocator::ResourceAllocator(Engine::Config const& config, DriverApi& driverApi) noexcept
: mCacheMaxAge(config.resourceAllocatorCacheMaxAge),
mBackend(driverApi),
mDisposer(std::make_shared<ResourceAllocatorDisposer>(driverApi)) {
}
ResourceAllocator::ResourceAllocator(std::shared_ptr<ResourceAllocatorDisposer> disposer,
Engine::Config const& config, DriverApi& driverApi) noexcept
: mCacheMaxAge(config.resourceAllocatorCacheMaxAge),
mBackend(driverApi),
mDisposer(std::move(disposer)) {
mBackend(driverApi) {
}
ResourceAllocator::~ResourceAllocator() noexcept {
assert_invariant(mTextureCache.empty());
assert_invariant(!mTextureCache.size());
assert_invariant(!mInUseTextures.size());
}
void ResourceAllocator::terminate() noexcept {
assert_invariant(!mInUseTextures.size());
auto& textureCache = mTextureCache;
for (auto it = textureCache.begin(); it != textureCache.end();) {
mBackend.destroyTexture(it->second.handle);
@@ -190,7 +174,7 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name,
swizzle[0], swizzle[1], swizzle[2], swizzle[3]);
}
}
mDisposer->checkout(handle, key);
mInUseTextures.emplace(handle, key);
} else {
if (swizzle == defaultSwizzle) {
handle = mBackend.createTexture(
@@ -206,99 +190,58 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name,
void ResourceAllocator::destroyTexture(TextureHandle h) noexcept {
if constexpr (mEnabled) {
auto const key = mDisposer->checkin(h);
if (UTILS_LIKELY(key.has_value())) {
uint32_t const size = key.value().getSize();
mTextureCache.emplace(key.value(), TextureCachePayload{ h, mAge, size });
mCacheSize += size;
mCacheSizeHiWaterMark = std::max(mCacheSizeHiWaterMark, mCacheSize);
}
// find the texture in the in-use list (it must be there!)
auto it = mInUseTextures.find(h);
assert_invariant(it != mInUseTextures.end());
// move it to the cache
const TextureKey key = it->second;
uint32_t const size = key.getSize();
mTextureCache.emplace(key, TextureCachePayload{ h, mAge, size });
mCacheSize += size;
// remove it from the in-use list
mInUseTextures.erase(it);
} else {
mBackend.destroyTexture(h);
}
}
ResourceAllocatorDisposerInterface& ResourceAllocator::getDisposer() noexcept {
return *mDisposer;
}
void ResourceAllocator::gc() noexcept {
// this is called regularly -- usually once per frame of each Renderer
void ResourceAllocator::gc(bool skippedFrame) noexcept {
// this is called regularly -- usually once per frame
// increase our age at each (non-skipped) frame
const size_t age = mAge;
if (!skippedFrame) {
mAge++;
}
// increase our age
const size_t age = mAge++;
// Purging strategy:
// - remove all entries older than MAX_AGE_SKIPPED_FRAME when skipping a frame
// - remove entries older than mCacheMaxAgeSoft
// - remove only MAX_EVICTION_COUNT entry per gc(),
// - look for the number of unique resource ages present in the cache (this basically gives
// us how many buckets of resources we have corresponding to previous frames.
// - remove all resources that have an age older than the MAX_UNIQUE_AGE_COUNT'th bucket
// - remove entries that are older than a certain age
// - remove only one entry per gc(),
auto& textureCache = mTextureCache;
// when skipping a frame, the maximum age to keep in the cache
constexpr size_t MAX_AGE_SKIPPED_FRAME = 1;
// maximum entry count to evict per GC, under the mCacheMaxAgeSoft limit
constexpr size_t MAX_EVICTION_COUNT = 1;
// maximum number of unique ages in the cache
constexpr size_t MAX_UNIQUE_AGE_COUNT = 3;
utils::bitset32 ages;
uint32_t evictedCount = 0;
for (auto it = textureCache.begin(); it != textureCache.end();) {
size_t const ageDiff = age - it->second.age;
if ((ageDiff >= MAX_AGE_SKIPPED_FRAME && skippedFrame) ||
(ageDiff >= mCacheMaxAge && evictedCount < MAX_EVICTION_COUNT)) {
evictedCount++;
const size_t ageDiff = age - it->second.age;
if (ageDiff >= mCacheMaxAge) {
purge(it);
// only purge a single entry per gc
break;
} else {
// build the set of ages present in the cache after eviction
ages.set(std::min(size_t(31), ageDiff));
++it;
}
}
// if we have MAX_UNIQUE_AGE_COUNT ages or more, we evict all the resources that
// are older than the MAX_UNIQUE_AGE_COUNT'th age.
if (!skippedFrame && ages.count() >= MAX_UNIQUE_AGE_COUNT) {
uint32_t bits = ages.getValue();
// remove from the set the ages we keep
for (size_t i = 0; i < MAX_UNIQUE_AGE_COUNT - 1; i++) {
bits &= ~(1 << utils::ctz(bits));
}
size_t const maxAge = utils::ctz(bits);
for (auto it = textureCache.begin(); it != textureCache.end();) {
const size_t ageDiff = age - it->second.age;
if (ageDiff >= maxAge) {
purge(it);
} else {
++it;
}
}
}
}
UTILS_NOINLINE
void ResourceAllocator::dump(bool brief) const noexcept {
constexpr float MiB = 1.0f / float(1u << 20u);
slog.d << "# entries=" << mTextureCache.size()
<< ", sz=" << (float)mCacheSize * MiB << " MiB"
<< ", max=" << (float)mCacheSizeHiWaterMark * MiB << " MiB"
<< io::endl;
slog.d << "# entries=" << mTextureCache.size() << ", sz=" << mCacheSize / float(1u << 20u)
<< " MiB" << io::endl;
if (!brief) {
for (auto const& it : mTextureCache) {
auto w = it.first.width;
auto h = it.first.height;
auto f = FTexture::getFormatSize(it.first.format);
slog.d << it.first.name << ": w=" << w << ", h=" << h << ", f=" << f << ", sz="
<< (float)it.second.size * MiB << io::endl;
<< it.second.size / float(1u << 20u) << io::endl;
}
}
}
@@ -311,46 +254,4 @@ void ResourceAllocator::purge(
mTextureCache.erase(pos);
}
// ------------------------------------------------------------------------------------------------
ResourceAllocatorDisposer::ResourceAllocatorDisposer(DriverApi& driverApi) noexcept
: mBackend(driverApi) {
}
ResourceAllocatorDisposer::~ResourceAllocatorDisposer() noexcept {
assert_invariant(mInUseTextures.empty());
}
void ResourceAllocatorDisposer::terminate() noexcept {
assert_invariant(mInUseTextures.empty());
}
void ResourceAllocatorDisposer::destroy(backend::TextureHandle handle) noexcept {
if (handle) {
auto r = checkin(handle);
if (r.has_value()) {
mBackend.destroyTexture(handle);
}
}
}
void ResourceAllocatorDisposer::checkout(backend::TextureHandle handle,
ResourceAllocator::TextureKey key) {
mInUseTextures.emplace(handle, key);
}
std::optional<ResourceAllocator::TextureKey> ResourceAllocatorDisposer::checkin(
backend::TextureHandle handle) {
// find the texture in the in-use list (it must be there!)
auto it = mInUseTextures.find(handle);
assert_invariant(it != mInUseTextures.end());
if (it == mInUseTextures.end()) {
return std::nullopt;
}
TextureKey const key = it->second;
// remove it from the in-use list
mInUseTextures.erase(it);
return key;
}
} // namespace filament
+5 -46
View File
@@ -28,29 +28,16 @@
#include <utils/Hash.h>
#include <array>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
#include <utility>
#include <cstddef>
#include <stddef.h>
#include <stdint.h>
namespace filament {
class ResourceAllocatorDisposer;
// The only reason we use an interface here is for unit-tests, so we can mock this allocator.
// This is not too time-critical, so that's okay.
class ResourceAllocatorDisposerInterface {
public:
virtual void destroy(backend::TextureHandle handle) noexcept = 0;
protected:
virtual ~ResourceAllocatorDisposerInterface();
};
class ResourceAllocatorInterface {
public:
virtual backend::RenderTargetHandle createRenderTarget(const char* name,
@@ -73,20 +60,15 @@ public:
virtual void destroyTexture(backend::TextureHandle h) noexcept = 0;
virtual ResourceAllocatorDisposerInterface& getDisposer() noexcept = 0;
protected:
virtual ~ResourceAllocatorInterface();
};
class ResourceAllocator final : public ResourceAllocatorInterface {
public:
explicit ResourceAllocator(std::shared_ptr<ResourceAllocatorDisposer> disposer,
Engine::Config const& config, backend::DriverApi& driverApi) noexcept;
explicit ResourceAllocator(
Engine::Config const& config, backend::DriverApi& driverApi) noexcept;
~ResourceAllocator() noexcept override;
void terminate() noexcept;
@@ -111,9 +93,7 @@ public:
void destroyTexture(backend::TextureHandle h) noexcept override;
ResourceAllocatorDisposerInterface& getDisposer() noexcept override;
void gc(bool skippedFrame = false) noexcept;
void gc() noexcept;
private:
size_t const mCacheMaxAge;
@@ -201,7 +181,6 @@ private:
using value_type = typename Container::value_type::second_type;
size_t size() const { return mContainer.size(); }
bool empty() const { return size() == 0; }
iterator begin() { return mContainer.begin(); }
const_iterator begin() const { return mContainer.begin(); }
iterator end() { return mContainer.end(); }
@@ -214,36 +193,16 @@ private:
};
using CacheContainer = AssociativeContainer<TextureKey, TextureCachePayload>;
using InUseContainer = AssociativeContainer<backend::TextureHandle, TextureKey>;
void purge(ResourceAllocator::CacheContainer::iterator const& pos);
backend::DriverApi& mBackend;
std::shared_ptr<ResourceAllocatorDisposer> mDisposer;
CacheContainer mTextureCache;
InUseContainer mInUseTextures;
size_t mAge = 0;
uint32_t mCacheSize = 0;
uint32_t mCacheSizeHiWaterMark = 0;
static constexpr bool mEnabled = true;
friend class ResourceAllocatorDisposer;
};
class ResourceAllocatorDisposer final : public ResourceAllocatorDisposerInterface {
using TextureKey = ResourceAllocator::TextureKey;
public:
explicit ResourceAllocatorDisposer(backend::DriverApi& driverApi) noexcept;
~ResourceAllocatorDisposer() noexcept override;
void terminate() noexcept;
void destroy(backend::TextureHandle handle) noexcept override;
private:
friend class ResourceAllocator;
void checkout(backend::TextureHandle handle, TextureKey key);
std::optional<TextureKey> checkin(backend::TextureHandle handle);
using InUseContainer = ResourceAllocator::AssociativeContainer<backend::TextureHandle, TextureKey>;
backend::DriverApi& mBackend;
InUseContainer mInUseTextures;
};
} // namespace filament
-9
View File
@@ -28,7 +28,6 @@ namespace filament {
class ResourceListBase {
public:
using iterator = typename tsl::robin_set<void*>::iterator;
using const_iterator = typename tsl::robin_set<void*>::const_iterator;
explicit ResourceListBase(const char* typeName);
ResourceListBase(ResourceListBase&& rhs) noexcept = default;
@@ -59,14 +58,6 @@ public:
return mList.end();
}
const_iterator begin() const noexcept {
return mList.begin();
}
const_iterator end() const noexcept {
return mList.end();
}
protected:
void forEach(void(*f)(void* user, void *p), void* user) const noexcept;
tsl::robin_set<void*> mList;
+44 -62
View File
@@ -57,6 +57,9 @@ namespace filament {
using namespace math;
using namespace backend;
// do this only if depth-clamp is available
static constexpr bool USE_DEPTH_CLAMP = false;
ShadowMap::ShadowMap(FEngine& engine) noexcept
: mPerShadowMapUniforms(engine),
mShadowType(ShadowType::DIRECTIONAL),
@@ -123,8 +126,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
FScene::LightSoa const& lightData, size_t index,
filament::CameraInfo const& camera,
ShadowMapInfo const& shadowMapInfo,
SceneInfo const& sceneInfo,
bool useDepthClamp) noexcept {
SceneInfo const& sceneInfo) noexcept {
// reset the visible shadow status
mHasVisibleShadows = false;
@@ -135,20 +137,13 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
const auto direction = lightData.elementAt<FScene::SHADOW_DIRECTION>(index);
auto const [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] =
computeDirectionalShadowBounds(engine, direction, params, camera, sceneInfo,
useDepthClamp);
auto [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] =
computeDirectionalShadowBounds(engine, direction, params, camera, sceneInfo);
if (UTILS_UNLIKELY(!visibleShadows)) {
return {};
}
// if the 2D bounds of the intersection is empty seen from the light, we have no shadow
Aabb const bounds = compute2DBounds(Mv, lsClippedShadowVolume.data(), vertexCount);
if (UTILS_UNLIKELY(!(bounds.min.x < bounds.max.x && bounds.min.y < bounds.max.y))) {
return {};
}
/*
* Compute the light's projection matrix
* (directional/point lights, i.e. projection to use, including znear/zfar clip planes)
@@ -166,17 +161,12 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
mat4f W, Wp, L;
// We can't use LISPSM in stable mode
const bool useLispsm = params.options.lispsm && !params.options.stable;
if (useLispsm ||
(!params.options.stable && !engine.debug.shadowmap.disable_light_frustum_align)) {
if (useLispsm) {
// Orient the shadow map in the direction of the view vector by constructing a
const float3 lsCameraFwd = Mv.upperLeft() * camera.getForwardVector();
L = computeLightRotation(lsCameraFwd);
LMp = L * Mp;
LMpMv = LMp * Mv;
}
if (useLispsm) {
W = applyLISPSM(Wp, camera, params,
LMp, Mv, LMpMv, lsClippedShadowVolume, vertexCount, direction);
}
@@ -194,7 +184,8 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
// In LiPSM mode, we're using the warped space here.
float4 f = computeFocusParams(LMpMv, WLMp,
lsClippedShadowVolume, vertexCount,
camera, params.options.shadowFar, params.options.stable);
camera, sceneInfo.csNearFar,
params.options.shadowFar, params.options.stable);
if (params.options.stable) {
const auto lsRef = lightData.elementAt<FScene::SHADOW_REF>(index);
@@ -395,8 +386,7 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
float3 const direction,
FLightManager::ShadowParams params,
filament::CameraInfo const& camera,
SceneInfo const& sceneInfo,
bool useDepthClamp) noexcept {
SceneInfo const& sceneInfo) noexcept {
// we use aligned_storage<> here to avoid the default initialization of std::array<>
using Storage = std::aligned_storage<sizeof(FrustumBoxIntersection)>::type;
@@ -458,49 +448,36 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
// Compute the intersection of the view volume with the intersection of receivers and casters
// in light space. This returns a set of points on the convex-hull of the intersection.
mat4f const projection = camera.cullingProjection *
(camera.view * FCamera::rigidTransformInverse(MvAtOrigin));
size_t const vertexCount = intersectFrustumWithBox(lsClippedShadowVolume,
projection, lsLightFrustumBounds);
camera.cullingProjection * camera.view * FCamera::rigidTransformInverse(MvAtOrigin),
sceneInfo.csNearFar, lsLightFrustumBounds);
if (UTILS_UNLIKELY(vertexCount < 4)) {
return {};
}
/*
* Adjust the scene's zmax (i.e. Near plane) and zmin (i.e. Far plane) in light space.
* (near/far correspond to max/min because the light looks down the -z axis).
*
* - For the Far plane, the light frustum is already set to the closest of receivers and
* casters (i.e.: shadow casters behind the last receivers can't cast any shadows).
* But it can be moved closer up to the farthest point in the camera frustum & light
* frustum intersection.
*
* - The Near plane is already set to the shadow casters max z (i.e. closest to the light)
*
* If "depth clamp" is supported, we can further tighten the near plane to the
* shadow receiver (i.e. to the closest point of camera frustum & light frustum intersection).
*
* Note: L has no influence here, since we're only interested in z values
* (L is a rotation around z)
*/
* Adjust the scene's zmax (i.e. Near plane) and zmin (i.e. Far plane) in light space.
* (near/far correspond to max/min because the light looks down the -z axis).
* - The Near plane is set to the shadow casters max z (i.e. closest to the light)
* - The Far plane is set to the closest of the farthest shadow casters and receivers
* i.e.: shadow casters behind the last receivers can't cast any shadows
*
* If "depth clamp" is supported, we can further tighten the near plane to the
* shadow receiver.
*
* Note: L has no influence here, since we're only interested in z values
* (L is a rotation around z)
*/
float lsClippedShadowVolumeFarthest = std::numeric_limits<float>::max();
float lsClippedShadowVolumeNearest = std::numeric_limits<float>::lowest();
for (size_t i = 0; i < vertexCount; ++i) {
float3 const v = lsClippedShadowVolume[i];
lsClippedShadowVolumeFarthest = std::min(lsClippedShadowVolumeFarthest, v.z);
lsClippedShadowVolumeNearest = std::max(lsClippedShadowVolumeNearest, v.z);
// far: figure out the farthest shadow receivers
lsLightFrustumBounds.min.z = min(lsLightFrustumBounds.min.z, v.z);
if constexpr (USE_DEPTH_CLAMP) {
lsLightFrustumBounds.max.z = max(lsLightFrustumBounds.max.z, v.z);
}
}
lsLightFrustumBounds.min.z =
std::max(lsLightFrustumBounds.min.z, lsClippedShadowVolumeFarthest);
if (useDepthClamp) {
lsLightFrustumBounds.max.z =
std::min(lsLightFrustumBounds.max.z, lsClippedShadowVolumeNearest);
}
if (engine.debug.shadowmap.far_uses_shadowcasters) {
// far: closest of the farthest shadow casters and receivers
lsLightFrustumBounds.min.z =
@@ -634,7 +611,7 @@ math::float4 ShadowMap::computeFocusParams(
mat4f const& LMpMv,
mat4f const& WLMp,
FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount,
filament::CameraInfo const& camera,
filament::CameraInfo const& camera, float2 const& csNearFar,
float shadowFar, bool stable) noexcept {
float2 s, o;
if (stable) {
@@ -649,7 +626,7 @@ math::float4 ShadowMap::computeFocusParams(
return wsViewVolumeBoundingSphere;
} else {
mat4f const viewFromClip = inverse(camera.cullingProjection);
Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip);
Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip, csNearFar);
float4 const wsViewVolumeBoundingSphere =
computeBoundingSphere(wsFrustumVertices.vertices, 8);
return wsViewVolumeBoundingSphere;
@@ -817,14 +794,15 @@ Aabb ShadowMap::compute2DBounds(const mat4f& lightView,
}
ShadowMap::Corners ShadowMap::computeFrustumCorners(
mat4f const& projectionInverse) noexcept {
mat4f const& projectionInverse,
float2 csNearFar) noexcept {
Corners out;
// compute view frustum in world space (from its NDC)
// matrix to convert: ndc -> camera -> world
float const near = -1.0f;
float const far = 1.0f;
float const near = csNearFar.x;
float const far = csNearFar.y;
Corners const csViewFrustumCorners = {
.vertices = {
@@ -917,11 +895,11 @@ void ShadowMap::snapLightFrustum(float2& s, float2& o,
size_t ShadowMap::intersectFrustumWithBox(
FrustumBoxIntersection& UTILS_RESTRICT outVertices,
mat4f const& UTILS_RESTRICT projection,
mat4f const& UTILS_RESTRICT projection, math::float2 const& UTILS_RESTRICT csNearFar,
Aabb const& UTILS_RESTRICT box)
{
Frustum const frustum{ projection };
Corners const frustumVertices{ computeFrustumCorners(inverse(projection)) };
Corners const frustumVertices{ computeFrustumCorners(inverse(projection), csNearFar) };
constexpr const float EPSILON = 1.0f / 8192.0f; // ~0.012 mm
size_t vertexCount = 0;
@@ -1225,11 +1203,12 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
}
ShadowMap::SceneInfo::SceneInfo(
FScene const& scene, uint8_t visibleLayers) noexcept
: visibleLayers(visibleLayers) {
FScene const& scene, uint8_t visibleLayers, mat4f const& viewMatrix) noexcept
: vsNearFar(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max()),
visibleLayers(visibleLayers) {
// the code below only works with affine transforms
// Filament's API requires viewMatrix to be rigid (and thus affine).
assert_invariant(transpose(viewMatrix)[3] == float4(0, 0, 0, 1));
// We assume the light is at the origin to compute the SceneInfo. This is consumed later by
// computeShadowCameraDirectional() which takes this into account.
@@ -1245,6 +1224,9 @@ ShadowMap::SceneInfo::SceneInfo(
[&](Aabb receiver, Culler::result_type) {
wsShadowReceiversVolume.min = min(wsShadowReceiversVolume.min, receiver.min);
wsShadowReceiversVolume.max = max(wsShadowReceiversVolume.max, receiver.max);
auto r = Aabb::transform(viewMatrix.upperLeft(), viewMatrix[3].xyz, receiver);
vsNearFar.x = std::max(vsNearFar.x, r.max.z);
vsNearFar.y = std::min(vsNearFar.y, r.min.z);
}
);
}
+14 -8
View File
@@ -109,13 +109,20 @@ public:
struct SceneInfo {
SceneInfo() noexcept = default;
SceneInfo(FScene const& scene, uint8_t visibleLayers) noexcept;
SceneInfo(FScene const& scene, uint8_t visibleLayers, math::mat4f const& viewMatrix) noexcept;
// scratch data: The near and far planes, in clip space, to use for this shadow map
math::float2 csNearFar = { -1.0f, 1.0f };
// scratch data: light's near/far expressed in light-space, calculated from the scene's
// content assuming the light is at the origin.
math::float2 lsCastersNearFar;
math::float2 lsReceiversNearFar;
// Viewing camera's near/far expressed in view-space, calculated from the
// scene's content.
math::float2 vsNearFar;
// World-space shadow-casters volume
Aabb wsShadowCastersVolume;
@@ -147,8 +154,7 @@ public:
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
ShadowMapInfo const& shadowMapInfo,
SceneInfo const& sceneInfo,
bool useDepthClamp) noexcept;
SceneInfo const& sceneInfo) noexcept;
ShaderParameters updateSpot(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
@@ -238,8 +244,7 @@ private:
math::float3 direction,
FLightManager::ShadowParams params,
filament::CameraInfo const& camera,
SceneInfo const& sceneInfo,
bool useDepthClamp) noexcept;
SceneInfo const& sceneInfo) noexcept;
static math::mat4f applyLISPSM(math::mat4f& Wp,
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
@@ -255,7 +260,7 @@ private:
math::mat4f const& LMpMv,
math::mat4f const& WLMp,
FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount,
filament::CameraInfo const& camera,
filament::CameraInfo const& camera, math::float2 const& csNearFar,
float shadowFar, bool stable) noexcept;
static inline void snapLightFrustum(math::float2& s, math::float2& o,
@@ -266,7 +271,8 @@ private:
SceneInfo const& sceneInfo,
bool stable, bool focusShadowCasters, bool farUsesShadowCasters) noexcept;
static Corners computeFrustumCorners(const math::mat4f& projectionInverse) noexcept;
static Corners computeFrustumCorners(const math::mat4f& projectionInverse,
math::float2 csNearFar = { -1.0f, 1.0f }) noexcept;
static inline math::float2 computeNearFar(math::mat4f const& view,
math::float3 const* wsVertices, size_t count) noexcept;
@@ -301,7 +307,7 @@ private:
static size_t intersectFrustumWithBox(
FrustumBoxIntersection& outVertices,
math::mat4f const& projection,
math::mat4f const& projection, math::float2 const& csNearFar,
Aabb const& box);
static math::mat4f warpFrustum(float n, float f) noexcept;
+82 -96
View File
@@ -39,20 +39,17 @@
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <utils/BitmaskEnum.h>
#include <utils/Range.h>
#include <utils/Slice.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <math/half.h>
#include <math/mat4.h>
#include <math/vec4.h>
#include <math/scalar.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <limits>
#include <new>
@@ -66,15 +63,12 @@ namespace filament {
using namespace backend;
using namespace math;
// do this only if depth-clamp is available
static constexpr bool USE_DEPTH_CLAMP = false;
ShadowMapManager::ShadowMapManager(FEngine& engine) {
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.shadowmap.visualize_cascades",
&engine.debug.shadowmap.visualize_cascades);
debugRegistry.registerProperty("d.shadowmap.disable_light_frustum_align",
&engine.debug.shadowmap.disable_light_frustum_align);
debugRegistry.registerProperty("d.shadowmap.tightly_bound_scene",
&engine.debug.shadowmap.tightly_bound_scene);
}
ShadowMapManager::~ShadowMapManager() {
@@ -153,7 +147,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(
calculateTextureRequirements(engine, view, lightData);
// Compute scene-dependent values shared across all shadow maps
ShadowMap::SceneInfo const info{ *view.getScene(), view.getVisibleLayers() };
ShadowMap::SceneInfo const info{ *view.getScene(), view.getVisibleLayers(), cameraInfo.view };
shadowTechnique |= updateCascadeShadowMaps(
engine, view, cameraInfo, renderableData, lightData, info);
@@ -549,9 +543,65 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight);
// Adjust the camera's projection for the light's shadowFar
cameraInfo.zf = params.options.shadowFar > 0.0f ? params.options.shadowFar : cameraInfo.zf;
if (UTILS_UNLIKELY(params.options.shadowFar > 0.0f)) {
cameraInfo.zf = params.options.shadowFar;
updateNearFarPlanes(&cameraInfo.cullingProjection, cameraInfo.zn, cameraInfo.zf);
float const n = cameraInfo.zn;
float const f = cameraInfo.zf;
/*
* Updating a projection matrix near and far planes:
*
* We assume that the near and far plane equations are of the form:
* N = { 0, 0, 1, n }
* F = { 0, 0, -1, -f }
*
* We also assume that the lower-left 2x2 of the projection is all 0:
* P = A 0 C 0
* 0 B D 0
* 0 0 E F
* 0 0 G H
*
* It result that we need to calculate E and F while leaving all other parameter unchanged.
*
* We know that:
* with N, F the near/far normalized plane equation parameters
* sn, sf arbitrary scale factors (they don't affect the planes)
* m is the transpose of projection (see Frustum.cpp)
*
* sn * N == -m[3] - m[2]
* sf * F == -m[3] + m[2]
*
* sn * N + sf * F == -2 * m[3]
* sn * N - sf * F == -2 * m[2]
*
* sn * N.z + sf * F.z == -2 * m[3].z
* sn * N.w + sf * F.w == -2 * m[3].w
* sn * N.z - sf * F.z == -2 * m[2].z
* sn * N.w - sf * F.w == -2 * m[2].w
*
* sn * N.z + sf * F.z == -2 * p[2].w
* sn * N.w + sf * F.w == -2 * p[3].w
* sn * N.z - sf * F.z == -2 * p[2].z
* sn * N.w - sf * F.w == -2 * p[3].z
*
* We now need to solve for { p[2].z, p[3].z, sn, sf } :
*
* sn == -2 * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z)
* sf == -2 * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z)
* p[2].z == (sf * F.z - sn * N.z) / 2
* p[3].z == (sf * F.w - sn * N.w) / 2
*/
auto& p = cameraInfo.cullingProjection;
float4 const N = { 0, 0, 1, n }; // near plane equation
float4 const F = { 0, 0, -1, -f }; // far plane equation
// near plane equation scale factor
float const sn = -2.0f * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z);
// far plane equation scale factor
float const sf = -2.0f * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z);
// New values for the projection
p[2].z = (sf * F.z - sn * N.z) * 0.5f;
p[3].z = (sf * F.w - sn * N.w) * 0.5f;
}
const ShadowMap::ShadowMapInfo shadowMapInfo{
@@ -581,10 +631,8 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
// Compute scene-dependent values shared across all cascades
ShadowMap::updateSceneInfoDirectional(MvAtOrigin, *scene, sceneInfo);
// we always do culling without depth clamp, because objects behind the camera
// must be rendered regardless
shadowMap.updateDirectional(engine,
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo, false);
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo);
hasVisibleShadows = shadowMap.hasVisibleShadows();
@@ -600,7 +648,15 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
uint32_t cascadeHasVisibleShadows = 0;
if (hasVisibleShadows) {
uint32_t const cascadeCount = cascadedShadowMaps.size();
// Adjust the near and far planes to tightly bound the scene.
float vsNear = -cameraInfo.zn;
float vsFar = -cameraInfo.zf;
if (engine.debug.shadowmap.tightly_bound_scene && !params.options.stable) {
vsNear = std::min(vsNear, sceneInfo.vsNearFar.x);
vsFar = std::max(vsFar, sceneInfo.vsNearFar.y);
}
const size_t cascadeCount = cascadedShadowMaps.size();
// We divide the camera frustum into N cascades. This gives us N + 1 split positions.
// The first split position is the near plane; the last split position is the far plane.
@@ -611,8 +667,9 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
}
const CascadeSplits splits({
.near = -cameraInfo.zn,
.far = -cameraInfo.zf,
.proj = cameraInfo.cullingProjection,
.near = vsNear,
.far = vsFar,
.cascadeCount = cascadeCount,
.splitPositions = splitPercentages
});
@@ -622,7 +679,10 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
static_assert(CONFIG_MAX_SHADOW_CASCADES <= 5,
"At most, a float4 can fit 4 split positions for 5 shadow cascades");
float4 wsSplitPositionUniform{ -std::numeric_limits<float>::infinity() };
std::copy(splits.begin() + 1, splits.end(), &wsSplitPositionUniform[0]);
std::copy(splits.beginWs() + 1, splits.endWs(), &wsSplitPositionUniform[0]);
float csSplitPosition[CONFIG_MAX_SHADOW_CASCADES + 1];
std::copy(splits.beginCs(), splits.endCs(), csSplitPosition);
mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform;
@@ -635,14 +695,10 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
ShadowMap& shadowMap = cascadedShadowMaps[i];
assert_invariant(shadowMap.getLightIndex() == 0);
// update cameraInfo culling projection for the cascade
float const* nearFarPlanes = splits.begin();
cameraInfo.zn = -nearFarPlanes[i];
cameraInfo.zf = -nearFarPlanes[i + 1];
updateNearFarPlanes(&cameraInfo.cullingProjection, cameraInfo.zn, cameraInfo.zf);
sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] };
auto shaderParameters = shadowMap.updateDirectional(engine,
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo, USE_DEPTH_CLAMP);
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo);
if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex();
@@ -1013,78 +1069,8 @@ ShadowMapManager::CascadeSplits::CascadeSplits(Params const& params) noexcept
: mSplitCount(params.cascadeCount + 1) {
for (size_t s = 0; s < mSplitCount; s++) {
mSplitsWs[s] = params.near + (params.far - params.near) * params.splitPositions[s];
mSplitsCs[s] = mat4f::project(params.proj, float3(0.0f, 0.0f, mSplitsWs[s])).z;
}
}
void ShadowMapManager::updateNearFarPlanes(mat4f* projection,
float nearDistance, float farDistance) noexcept {
float const n = nearDistance;
float const f = farDistance;
/*
* Updating a projection matrix near and far planes:
*
* We assume that the near and far plane equations are of the form:
* N = { 0, 0, 1, n }
* F = { 0, 0, -1, -f }
*
* We also assume that the lower-left 2x2 of the projection is all 0:
* P = A 0 C 0
* 0 B D 0
* 0 0 E F
* 0 0 G H
*
* It result that we need to calculate E and F while leaving all other parameter unchanged.
*
* We know that:
* with N, F the near/far normalized plane equation parameters
* sn, sf arbitrary scale factors (they don't affect the planes)
* m is the transpose of projection (see Frustum.cpp)
*
* sn * N == -m[3] - m[2]
* sf * F == -m[3] + m[2]
*
* sn * N + sf * F == -2 * m[3]
* sn * N - sf * F == -2 * m[2]
*
* sn * N.z + sf * F.z == -2 * m[3].z
* sn * N.w + sf * F.w == -2 * m[3].w
* sn * N.z - sf * F.z == -2 * m[2].z
* sn * N.w - sf * F.w == -2 * m[2].w
*
* sn * N.z + sf * F.z == -2 * p[2].w
* sn * N.w + sf * F.w == -2 * p[3].w
* sn * N.z - sf * F.z == -2 * p[2].z
* sn * N.w - sf * F.w == -2 * p[3].z
*
* We now need to solve for { p[2].z, p[3].z, sn, sf } :
*
* sn == -2 * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z)
* sf == -2 * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z)
* p[2].z == (sf * F.z - sn * N.z) / 2
* p[3].z == (sf * F.w - sn * N.w) / 2
*/
auto& p = *projection;
float4 const N = { 0, 0, 1, n }; // near plane equation
float4 const F = { 0, 0, -1, -f }; // far plane equation
// near plane equation scale factor
float const sn = -2.0f * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z);
// far plane equation scale factor
float const sf = -2.0f * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z);
// New values for the projection
p[2].z = (sf * F.z - sn * N.z) * 0.5f;
p[3].z = (sf * F.w - sn * N.w) * 0.5f;
}
utils::FixedCapacityVector<Camera const*>
ShadowMapManager::getDirectionalShadowCameras() const noexcept {
if (!mInitialized) return {};
auto const csm = getCascadedShadowMap();
auto result = utils::FixedCapacityVector<Camera const*>::with_capacity(csm.size());
for (ShadowMap const& sm : csm) {
result.push_back(sm.hasVisibleShadows() ? sm.getDebugCamera() : nullptr);
}
return result;
}
} // namespace filament
+15 -15
View File
@@ -40,7 +40,6 @@
#include <utils/BitmaskEnum.h>
#include <utils/compiler.h>
#include <utils/FixedCapacityVector.h>
#include <utils/debug.h>
#include <utils/Range.h>
#include <utils/Slice.h>
@@ -59,7 +58,6 @@
namespace filament {
class Camera;
class FCamera;
class FView;
class FrameGraph;
@@ -140,16 +138,16 @@ public:
bool hasSpotShadows() const { return !mSpotShadowMapCount; }
// for debugging only
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept;
FCamera const* getDirectionalShadowCamera() const noexcept {
if (!mInitialized) return nullptr;
return getShadowMap(0).getDebugCamera();
}
private:
explicit ShadowMapManager(FEngine& engine);
void terminate(FEngine& engine);
static void updateNearFarPlanes(math::mat4f* projection,
float nearDistance, float farDistance) noexcept;
ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
FView& view, CameraInfo cameraInfo, FScene::RenderableSoa& renderableData,
FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept;
@@ -188,21 +186,27 @@ private:
constexpr static size_t SPLIT_COUNT = CONFIG_MAX_SHADOW_CASCADES + 1;
struct Params {
math::mat4f proj;
float near = 0.0f;
float far = 0.0f;
uint32_t cascadeCount = 1;
size_t cascadeCount = 1;
std::array<float, SPLIT_COUNT> splitPositions = { 0.0f };
};
explicit CascadeSplits(Params const& params) noexcept;
// Split positions in world-space.
const float* begin() const { return mSplitsWs; }
const float* end() const { return mSplitsWs + mSplitCount; }
const float* beginWs() const { return mSplitsWs; }
const float* endWs() const { return mSplitsWs + mSplitCount; }
// Split positions in clip-space.
const float* beginCs() const { return mSplitsCs; }
const float* endCs() const { return mSplitsCs + mSplitCount; }
private:
float mSplitsWs[SPLIT_COUNT];
uint32_t mSplitCount;
float mSplitsCs[SPLIT_COUNT];
size_t mSplitCount;
};
// Atlas requirements, updated in ShadowMapManager::update(),
@@ -244,14 +248,10 @@ private:
}
utils::Slice<ShadowMap> getCascadedShadowMap() noexcept {
ShadowMap const* const p = &getShadowMap(0);
ShadowMap* const p = &getShadowMap(0);
return { p, mDirectionalShadowMapCount };
}
utils::Slice<ShadowMap> getCascadedShadowMap() const noexcept {
return const_cast<ShadowMapManager*>(this)->getCascadedShadowMap();
}
utils::Slice<ShadowMap> getSpotShadowMaps() noexcept {
ShadowMap* const p = &getShadowMap(CONFIG_MAX_SHADOW_CASCADES);
return { p, mSpotShadowMapCount };
+2 -8
View File
@@ -15,8 +15,6 @@
*/
#include "details/View.h"
#include "filament/View.h"
namespace filament {
@@ -73,8 +71,8 @@ const char* View::getName() const noexcept {
return downcast(this)->getName();
}
utils::FixedCapacityVector<Camera const*> View::getDirectionalShadowCameras() const noexcept {
return downcast(this)->getDirectionalShadowCameras();
Camera const* View::getDirectionalShadowCamera() const noexcept {
return downcast(this)->getDirectionalShadowCamera();
}
void View::setShadowingEnabled(bool enabled) noexcept {
@@ -314,8 +312,4 @@ utils::Entity View::getFogEntity() const noexcept {
return downcast(this)->getFogEntity();
}
void View::clearFrameHistory(Engine& engine) noexcept {
downcast(this)->clearFrameHistory(downcast(engine));
}
} // namespace filament
@@ -588,9 +588,6 @@ void FRenderableManager::create(
// full size of the UBO.
instances.handle = driver.createBufferObject(sizeof(PerRenderableUib),
BufferObjectBinding::UNIFORM, backend::BufferUsage::DYNAMIC);
if (auto name = instances.buffer->getName(); !name.empty()) {
driver.setDebugTag(instances.handle.getId(), std::move(name));
}
}
const uint32_t boneCount = builder->mSkinningBoneCount;
-5
View File
@@ -20,8 +20,6 @@
#include "FilamentAPI-impl.h"
#include <utils/CString.h>
namespace filament {
struct BufferObject::BuilderDetails {
@@ -58,9 +56,6 @@ FBufferObject::FBufferObject(FEngine& engine, const BufferObject::Builder& build
FEngine::DriverApi& driver = engine.getDriverApi();
mHandle = driver.createBufferObject(builder->mByteCount, builder->mBindingType,
backend::BufferUsage::STATIC);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mHandle.getId(), std::move(name));
}
}
void FBufferObject::terminate(FEngine& engine) {
+20 -25
View File
@@ -28,6 +28,12 @@
#include <string_view>
#include <utility>
#ifndef NDEBUG
# define DEBUG_PROPERTIES_WRITABLE true
#else
# define DEBUG_PROPERTIES_WRITABLE false
#endif
using namespace filament::math;
using namespace utils;
@@ -73,15 +79,17 @@ bool FDebugRegistry::hasProperty(const char* name) const noexcept {
template<typename T>
bool FDebugRegistry::setProperty(const char* name, T v) noexcept {
auto info = getPropertyInfo(name);
T* const addr = static_cast<T*>(info.first);
if (addr) {
auto old = *addr;
*addr = v;
if (info.second && old != v) {
info.second();
if constexpr (DEBUG_PROPERTIES_WRITABLE) {
auto info = getPropertyInfo(name);
T* const addr = static_cast<T*>(info.first);
if (addr) {
auto old = *addr;
*addr = v;
if (info.second && old != v) {
info.second();
}
return true;
}
return true;
}
return false;
}
@@ -110,32 +118,19 @@ template bool FDebugRegistry::getProperty<float2>(const char* name, float2* v) c
template bool FDebugRegistry::getProperty<float3>(const char* name, float3* v) const noexcept;
template bool FDebugRegistry::getProperty<float4>(const char* name, float4* v) const noexcept;
bool FDebugRegistry::registerDataSource(std::string_view name,
void FDebugRegistry::registerDataSource(std::string_view name,
void const* data, size_t count) noexcept {
auto& dataSourceMap = mDataSourceMap;
bool const found = dataSourceMap.find(name) == dataSourceMap.end();
if (found) {
if (dataSourceMap.find(name) == dataSourceMap.end()) {
dataSourceMap[name] = { data, count };
}
return found;
}
bool FDebugRegistry::registerDataSource(std::string_view name,
void FDebugRegistry::registerDataSource(std::string_view name,
utils::Invocable<DataSource()>&& creator) noexcept {
auto& dataSourceCreatorMap = mDataSourceCreatorMap;
bool const found = dataSourceCreatorMap.find(name) == dataSourceCreatorMap.end();
if (found) {
dataSourceCreatorMap[name] = std::move(creator);
}
return found;
mDataSourceCreatorMap[name] = std::move(creator);
}
void FDebugRegistry::unregisterDataSource(std::string_view name) noexcept {
mDataSourceCreatorMap.erase(name);
mDataSourceMap.erase(name);
}
DebugRegistry::DataSource FDebugRegistry::getDataSource(const char* name) const noexcept {
std::string_view const key{ name };
auto& dataSourceMap = mDataSourceMap;
+2 -4
View File
@@ -101,14 +101,12 @@ public:
}
// registers a DataSource directly
bool registerDataSource(std::string_view name, void const* data, size_t count) noexcept;
void registerDataSource(std::string_view name, void const* data, size_t count) noexcept;
// registers a DataSource lazily
bool registerDataSource(std::string_view name,
void registerDataSource(std::string_view name,
utils::Invocable<DataSource()>&& creator) noexcept;
void unregisterDataSource(std::string_view name) noexcept;
#if !defined(_MSC_VER)
private:
#endif
+17 -28
View File
@@ -71,7 +71,7 @@ struct Engine::BuilderDetails {
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
void* mSharedContext = nullptr;
bool mPaused = false;
static Config validateConfig(Config config) noexcept;
static Config validateConfig(const Config* pConfig) noexcept;
};
Engine* FEngine::create(Engine::Builder const& builder) {
@@ -163,7 +163,8 @@ FEngine* FEngine::getEngine(void* token) {
FILAMENT_CHECK_PRECONDITION(ThreadUtils::isThisThread(instance->mMainThreadId))
<< "Engine::createAsync() and Engine::getEngine() must be called on the same thread.";
if (!instance->mInitialized) {
// we use mResourceAllocator as a proxy for "am I already initialized"
if (!instance->mResourceAllocator) {
if (UTILS_UNLIKELY(!instance->mDriver)) {
// something went horribly wrong during driver initialization
instance->mDriverThread.join();
@@ -261,7 +262,7 @@ void FEngine::init() {
slog.i << "FEngine feature level: " << int(mActiveFeatureLevel) << io::endl;
mResourceAllocatorDisposer = std::make_shared<ResourceAllocatorDisposer>(driverApi);
mResourceAllocator = new ResourceAllocator(mConfig, driverApi);
mFullScreenTriangleVb = downcast(VertexBuffer::Builder()
.vertexCount(3)
@@ -424,13 +425,11 @@ void FEngine::init() {
}
});
});
mInitialized = true;
}
FEngine::~FEngine() noexcept {
SYSTRACE_CALL();
assert_invariant(!mResourceAllocatorDisposer);
delete mResourceAllocator;
delete mDriver;
if (mOwnPlatform) {
PlatformFactory::destroy(&mPlatform);
@@ -441,7 +440,7 @@ void FEngine::shutdown() {
SYSTRACE_CALL();
// by construction this should never be nullptr
assert_invariant(mResourceAllocatorDisposer);
assert_invariant(mResourceAllocator);
FILAMENT_CHECK_PRECONDITION(ThreadUtils::isThisThread(mMainThreadId))
<< "Engine::shutdown() called from the wrong thread!";
@@ -461,8 +460,7 @@ void FEngine::shutdown() {
*/
mPostProcessManager.terminate(driver); // free-up post-process manager resources
mResourceAllocatorDisposer->terminate();
mResourceAllocatorDisposer.reset();
mResourceAllocator->terminate();
mDFG.terminate(*this); // free-up the DFG
mRenderableManager.terminate(); // free-up all renderables
mLightManager.terminate(); // free-up all lights
@@ -1197,22 +1195,6 @@ bool FEngine::isValid(const FInstanceBuffer* p) const {
return isValid(p, mInstanceBuffers);
}
size_t FEngine::getBufferObjectCount() const noexcept { return mBufferObjects.size(); }
size_t FEngine::getViewCount() const noexcept { return mViews.size(); }
size_t FEngine::getSceneCount() const noexcept { return mScenes.size(); }
size_t FEngine::getSwapChainCount() const noexcept { return mSwapChains.size(); }
size_t FEngine::getStreamCount() const noexcept { return mStreams.size(); }
size_t FEngine::getIndexBufferCount() const noexcept { return mIndexBuffers.size(); }
size_t FEngine::getSkinningBufferCount() const noexcept { return mSkinningBuffers.size(); }
size_t FEngine::getMorphTargetBufferCount() const noexcept { return mMorphTargetBuffers.size(); }
size_t FEngine::getInstanceBufferCount() const noexcept { return mInstanceBuffers.size(); }
size_t FEngine::getVertexBufferCount() const noexcept { return mVertexBuffers.size(); }
size_t FEngine::getIndirectLightCount() const noexcept { return mIndirectLights.size(); }
size_t FEngine::getMaterialCount() const noexcept { return mMaterials.size(); }
size_t FEngine::getTextureCount() const noexcept { return mTextures.size(); }
size_t FEngine::getSkyboxeCount() const noexcept { return mSkyboxes.size(); }
size_t FEngine::getColorGradingCount() const noexcept { return mColorGradings.size(); }
size_t FEngine::getRenderTargetCount() const noexcept { return mRenderTargets.size(); }
void* FEngine::streamAlloc(size_t size, size_t alignment) noexcept {
// we allow this only for small allocations
@@ -1302,7 +1284,7 @@ Engine::Builder& Engine::Builder::platform(Platform* platform) noexcept {
}
Engine::Builder& Engine::Builder::config(Engine::Config const* config) noexcept {
mImpl->mConfig = config ? *config : Engine::Config{};
mImpl->mConfig = BuilderDetails::validateConfig(config);
return *this;
}
@@ -1330,15 +1312,22 @@ void Engine::Builder::build(Invocable<void(void*)>&& callback) const {
#endif
Engine* Engine::Builder::build() const {
mImpl->mConfig = BuilderDetails::validateConfig(mImpl->mConfig);
return FEngine::create(*this);
}
Engine::Config Engine::BuilderDetails::validateConfig(Config config) noexcept {
Engine::Config Engine::BuilderDetails::validateConfig(const Config* const pConfig) noexcept {
// Rule of thumb: perRenderPassArenaMB must be roughly 1 MB larger than perFrameCommandsMB
constexpr uint32_t COMMAND_ARENA_OVERHEAD = 1;
constexpr uint32_t CONCURRENT_FRAME_COUNT = 3;
Config config;
if (!pConfig) {
return config;
}
// make sure to copy all the fields
config = *pConfig;
// Use at least the defaults set by the build system
config.minCommandBufferSizeMB = std::max(
config.minCommandBufferSizeMB,
+6 -31
View File
@@ -86,7 +86,6 @@ namespace filament {
class Renderer;
class MaterialParser;
class ResourceAllocatorDisposer;
namespace backend {
class Driver;
@@ -255,13 +254,9 @@ public:
}
}
ResourceAllocatorDisposer& getResourceAllocatorDisposer() noexcept {
assert_invariant(mResourceAllocatorDisposer);
return *mResourceAllocatorDisposer;
}
std::shared_ptr<ResourceAllocatorDisposer> const& getSharedResourceAllocatorDisposer() noexcept {
return mResourceAllocatorDisposer;
ResourceAllocator& getResourceAllocator() noexcept {
assert_invariant(mResourceAllocator);
return *mResourceAllocator;
}
void* streamAlloc(size_t size, size_t alignment) noexcept;
@@ -351,23 +346,6 @@ public:
bool isValid(const FView* p) const;
bool isValid(const FInstanceBuffer* p) const;
size_t getBufferObjectCount() const noexcept;
size_t getViewCount() const noexcept;
size_t getSceneCount() const noexcept;
size_t getSwapChainCount() const noexcept;
size_t getStreamCount() const noexcept;
size_t getIndexBufferCount() const noexcept;
size_t getSkinningBufferCount() const noexcept;
size_t getMorphTargetBufferCount() const noexcept;
size_t getInstanceBufferCount() const noexcept;
size_t getVertexBufferCount() const noexcept;
size_t getIndirectLightCount() const noexcept;
size_t getMaterialCount() const noexcept;
size_t getTextureCount() const noexcept;
size_t getSkyboxeCount() const noexcept;
size_t getColorGradingCount() const noexcept;
size_t getRenderTargetCount() const noexcept;
void destroy(utils::Entity e);
bool isPaused() const noexcept;
@@ -512,7 +490,7 @@ private:
FTransformManager mTransformManager;
FLightManager mLightManager;
FCameraManager mCameraManager;
std::shared_ptr<ResourceAllocatorDisposer> mResourceAllocatorDisposer;
ResourceAllocator* mResourceAllocator = nullptr;
HwVertexBufferInfoFactory mHwVertexBufferInfoFactory;
ResourceList<FBufferObject> mBufferObjects{ "BufferObject" };
@@ -584,14 +562,11 @@ private:
std::thread::id mMainThreadId{};
bool mInitialized = false;
// Creation parameters
Config mConfig;
public:
// These are the debug properties used by FDebug.
// They're accessed directly by modules who need them.
// these are the debug properties used by FDebug. They're accessed directly by modules who need them.
struct {
struct {
bool debug_directional_shadowmap = false;
@@ -599,7 +574,7 @@ public:
bool far_uses_shadowcasters = true;
bool focus_shadowcasters = true;
bool visualize_cascades = false;
bool disable_light_frustum_align = false;
bool tightly_bound_scene = true;
float dzn = -1.0f;
float dzf = 1.0f;
float display_shadow_texture_scale = 0.25f;
-5
View File
@@ -20,8 +20,6 @@
#include "FilamentAPI-impl.h"
#include <utils/CString.h>
namespace filament {
struct IndexBuffer::BuilderDetails {
@@ -60,9 +58,6 @@ FIndexBuffer::FIndexBuffer(FEngine& engine, const IndexBuffer::Builder& builder)
(backend::ElementType)builder->mIndexType,
uint32_t(builder->mIndexCount),
backend::BufferUsage::STATIC);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mHandle.getId(), std::move(name));
}
}
void FIndexBuffer::terminate(FEngine& engine) {
+1 -2
View File
@@ -61,8 +61,7 @@ InstanceBuffer* InstanceBuffer::Builder::build(Engine& engine) {
// ------------------------------------------------------------------------------------------------
FInstanceBuffer::FInstanceBuffer(FEngine& engine, const Builder& builder)
: mName(builder.getName()) {
FInstanceBuffer::FInstanceBuffer(FEngine& engine, const Builder& builder) {
mInstanceCount = builder->mInstanceCount;
mLocalTransforms.reserve(mInstanceCount);
-4
View File
@@ -25,7 +25,6 @@
#include <math/mat4.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
namespace filament {
@@ -47,13 +46,10 @@ public:
void prepare(FEngine& engine, math::mat4f rootTransform, const PerRenderableData& ubo,
backend::Handle<backend::HwBufferObject> handle);
utils::CString const& getName() const noexcept { return mName; }
private:
friend class RenderableManager;
utils::FixedCapacityVector<math::mat4f> mLocalTransforms;
utils::CString mName;
size_t mInstanceCount;
};
+3 -27
View File
@@ -192,30 +192,6 @@ Material* Material::Builder::build(Engine& engine) {
return nullptr;
}
// Print a warning if the material's stereo type doesn't align with the engine's setting.
MaterialDomain materialDomain;
UserVariantFilterMask variantFilterMask;
materialParser->getMaterialDomain(&materialDomain);
materialParser->getMaterialVariantFilterMask(&variantFilterMask);
bool const hasStereoVariants = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::STE));
if (materialDomain == MaterialDomain::SURFACE && hasStereoVariants) {
StereoscopicType const engineStereoscopicType = engine.getConfig().stereoscopicType;
// Default materials are always compiled with either 'instanced' or 'multiview'.
// So, we only verify compatibility if the engine is set up for stereo.
if (engineStereoscopicType != StereoscopicType::NONE) {
StereoscopicType materialStereoscopicType = StereoscopicType::NONE;
materialParser->getStereoscopicType(&materialStereoscopicType);
if (materialStereoscopicType != engineStereoscopicType) {
CString name;
materialParser->getName(&name);
slog.w << "The stereoscopic type in the compiled material '" << name.c_str_safe()
<< "' is " << (int)materialStereoscopicType
<< ", which is not compatiable with the engine's setting "
<< (int)engineStereoscopicType << "." << io::endl;
}
}
}
return downcast(engine).createMaterial(*this, std::move(materialParser));
}
@@ -224,7 +200,8 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
: mIsDefaultMaterial(builder->mDefaultMaterial),
mEngine(engine),
mMaterialId(engine.getMaterialId()),
mMaterialParser(std::move(materialParser)) {
mMaterialParser(std::move(materialParser))
{
MaterialParser* const parser = mMaterialParser.get();
UTILS_UNUSED_IN_RELEASE bool const nameOk = parser->getName(&mName);
@@ -618,7 +595,6 @@ Program FMaterial::getProgramWithVariants(
void FMaterial::createAndCacheProgram(Program&& p, Variant variant) const noexcept {
auto program = mEngine.getDriverApi().createProgram(std::move(p));
mEngine.getDriverApi().setDebugTag(program.getId(), mName);
assert_invariant(program);
mCachedPrograms[variant.key] = program;
}
@@ -897,7 +873,7 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Material::Builde
engine.getDriverApi().getMaxUniformBufferSize() / 16u);
bool const staticTextureWorkaround =
engine.getDriverApi().isWorkaroundNeeded(Workaround::METAL_STATIC_TEXTURE_TARGET_ERROR);
engine.getDriverApi().isWorkaroundNeeded(Workaround::A8X_STATIC_TEXTURE_TARGET_ERROR);
bool const powerVrShaderWorkarounds =
engine.getDriverApi().isWorkaroundNeeded(Workaround::POWER_VR_SHADER_WORKAROUNDS);
@@ -51,7 +51,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material)
mUniforms = UniformBuffer(material->getUniformInterfaceBlock().getSize());
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
BufferObjectBinding::UNIFORM, backend::BufferUsage::STATIC);
driver.setDebugTag(mUbHandle.getId(), material->getName());
}
if (!material->getSamplerInterfaceBlock().isEmpty()) {
@@ -117,7 +116,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
mUniforms.setUniforms(other->getUniformBuffer());
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
BufferObjectBinding::UNIFORM, backend::BufferUsage::DYNAMIC);
driver.setDebugTag(mUbHandle.getId(), material->getName());
}
if (!material->getSamplerInterfaceBlock().isEmpty()) {
@@ -25,8 +25,6 @@
#include <math/mat4.h>
#include <math/norm.h>
#include <utils/CString.h>
namespace filament {
using namespace backend;
@@ -126,11 +124,6 @@ FMorphTargetBuffer::FMorphTargetBuffer(FEngine& engine, const Builder& builder)
mCount,
TextureUsage::DEFAULT);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mPbHandle.getId(), name);
driver.setDebugTag(mTbHandle.getId(), std::move(name));
}
// create and update sampler group
mSbHandle = driver.createSamplerGroup(PerRenderPrimitiveMorphingSib::SAMPLER_COUNT,
utils::FixedSizeString<32>("Morph target samplers"));
+31 -102
View File
@@ -60,7 +60,6 @@
#include <chrono>
#include <limits>
#include <memory>
#include <utility>
#include <stddef.h>
@@ -89,11 +88,7 @@ FRenderer::FRenderer(FEngine& engine) :
mHdrQualityMedium(TextureFormat::R11F_G11F_B10F),
mHdrQualityHigh(TextureFormat::RGB16F),
mIsRGB8Supported(false),
mUserEpoch(engine.getEngineEpoch()),
mResourceAllocator(std::make_unique<ResourceAllocator>(
engine.getSharedResourceAllocatorDisposer(),
engine.getConfig(),
engine.getDriverApi()))
mUserEpoch(engine.getEngineEpoch())
{
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.renderer.doFrameCapture",
@@ -181,7 +176,6 @@ void FRenderer::terminate(FEngine& engine) {
}
mFrameInfoManager.terminate(driver);
mFrameSkipper.terminate(driver);
mResourceAllocator->terminate();
}
void FRenderer::resetUserTime() {
@@ -242,39 +236,6 @@ void FRenderer::setVsyncTime(uint64_t steadyClockTimeNano) noexcept {
mVsyncSteadyClockTimeNano = steadyClockTimeNano;
}
void FRenderer::skipFrame(uint64_t vsyncSteadyClockTimeNano) {
SYSTRACE_CALL();
FILAMENT_CHECK_PRECONDITION(!mSwapChain) <<
"skipFrame() can't be called between beginFrame() and endFrame()";
if (!vsyncSteadyClockTimeNano) {
vsyncSteadyClockTimeNano = mVsyncSteadyClockTimeNano;
mVsyncSteadyClockTimeNano = 0;
}
FEngine& engine = mEngine;
FEngine::DriverApi& driver = engine.getDriverApi();
// Gives the backend a chance to execute periodic tasks. This must be called before
// the frame skipper.
driver.tick();
// do this before engine.flush()
mResourceAllocator->gc(true);
// Run the component managers' GC in parallel
// WARNING: while doing this we can't access any component manager
auto& js = engine.getJobSystem();
auto *job = js.runAndRetain(jobs::createJob(js, nullptr, &FEngine::gc, &engine)); // gc all managers
engine.flush(); // flush command stream
// make sure we're done with the gcs
js.waitAndRelease(job);
}
bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano) {
assert_invariant(swapChain);
@@ -344,14 +305,10 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
* to ignore the return value and render the frame anyway -- which is perfectly fine.
* The remaining work will be done when the first render() call is made.
*/
auto beginFrameInternal = [this, appVsync, swapChain]() {
auto beginFrameInternal = [this, appVsync]() {
FEngine& engine = mEngine;
FEngine::DriverApi& driver = engine.getDriverApi();
// we need to re-set mSwapChain here because if a frame was marked as "skip" but the
// user ignored us, we'd still want mSwapChain to be set.
mSwapChain = swapChain;
driver.beginFrame(
appVsync.time_since_epoch().count(),
mDisplayInfo.refreshRate == 0.0 ? 0 : int64_t(
@@ -380,10 +337,6 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
// so we need to delay this work until that happens.
mBeginFrameInternal = beginFrameInternal;
// clear mSwapChain because the frame should be skipped (it will be re-set if the user
// ignores the skip)
mSwapChain = nullptr;
// we need to flush in this case, to make sure the tick() call is executed at some point
engine.flush();
@@ -427,7 +380,7 @@ void FRenderer::endFrame() {
}
// do this before engine.flush()
mResourceAllocator->gc();
engine.getResourceAllocator().gc();
// Run the component managers' GC in parallel
// WARNING: while doing this we can't access any component manager
@@ -542,6 +495,8 @@ void FRenderer::renderStandaloneView(FView const* view) {
void FRenderer::render(FView const* view) {
SYSTRACE_CALL();
assert_invariant(mSwapChain);
if (UTILS_UNLIKELY(mBeginFrameInternal)) {
// this should not happen, the user should not call render() if we returned false from
// beginFrame(). But because this is allowed, we handle it gracefully.
@@ -549,9 +504,6 @@ void FRenderer::render(FView const* view) {
mBeginFrameInternal = {};
}
// after beginFrame() is called, mSwapChain should be true
assert_invariant(mSwapChain);
if (UTILS_LIKELY(view && view->getScene() && view->hasCamera())) {
if (mViewRenderedCount) {
// This is a good place to kick the GPU, since we've rendered a View before,
@@ -655,16 +607,16 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// on qualcomm hardware -- we might need a backend dependent toggle at some point
const PostProcessManager::ColorGradingConfig colorGradingConfig{
.asSubpass =
msaaSampleCount <= 1 &&
driver.isFrameBufferFetchSupported() &&
hasColorGrading &&
msaaSampleCount <= 1 &&
!bloomOptions.enabled && !dofOptions.enabled && !taaOptions.enabled &&
driver.isFrameBufferFetchSupported() &&
!engine.debug.renderer.disable_subpasses,
.customResolve =
msaaSampleCount > 1 &&
driver.isFrameBufferFetchMultiSampleSupported() &&
msaaOptions.customResolve &&
msaaSampleCount > 1 &&
hasColorGrading &&
driver.isFrameBufferFetchMultiSampleSupported() &&
!engine.debug.renderer.disable_subpasses,
.translucent = needsAlphaChannel,
.fxaa = hasFXAA,
@@ -673,9 +625,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
TextureFormat::RGBA8 : getLdrFormat(needsAlphaChannel)
};
// by construction (msaaSampleCount) both asSubpass and customResolve can't be true
assert_invariant(colorGradingConfig.asSubpass + colorGradingConfig.customResolve < 2);
// whether we're scaled at all
bool scaled = any(notEqual(scale, float2(1.0f)));
@@ -698,12 +647,13 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
CameraInfo cameraInfo = view.computeCameraInfo(engine);
// If fxaa and scaling are not enabled, we're essentially in a very fast rendering path -- in
// this case, we would need an extra blit to "resolve" the buffer padding (because there are no
// other pass that can do it as a side effect). In this case, it is better to skip the padding,
// which won't be helping much.
const bool noBufferPadding
= (!hasFXAA && !scaled) || engine.debug.renderer.disable_buffer_padding;
// when colorgrading-as-subpass is active, we know that many other effects are disabled
// such as dof, bloom. Moreover, if fxaa and scaling are not enabled, we're essentially in
// a very fast rendering path -- in this case, we would need an extra blit to "resolve" the
// buffer padding (because there are no other pass that can do it as a side effect).
// In this case, it is better to skip the padding, which won't be helping much.
const bool noBufferPadding = (colorGradingConfig.asSubpass && !hasFXAA && !scaled)
|| engine.debug.renderer.disable_buffer_padding;
// guardBand must be a multiple of 16 to guarantee the same exact rendering up to 4 mip levels.
float const guardBand = guardBandOptions.enabled ? 16.0f : 0.0f;
@@ -800,7 +750,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
* Frame graph
*/
FrameGraph fg(*mResourceAllocator,
FrameGraph fg(engine.getResourceAllocator(),
isProtectedContent ? FrameGraph::Mode::PROTECTED : FrameGraph::Mode::UNPROTECTED);
auto& blackboard = fg.getBlackboard();
@@ -954,6 +904,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
.scale = aoOptions.resolution,
.picking = view.hasPicking()
});
blackboard["structure"] = structure;
const auto picking = picking_;
@@ -1001,6 +952,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
ssReflectionsOptions.enabled ? TextureFormat::RGBA16F : TextureFormat::R11F_G11F_B10F,
view.getCameraUser().getFieldOfView(Camera::Fov::VERTICAL), config.scale);
config.ssrLodOffset = ssrConfig.lodOffset;
blackboard["ssr"] = ssrConfig.ssr;
// --------------------------------------------------------------------------------------------
// screen-space reflections pass
@@ -1104,39 +1056,24 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
);
// the color pass itself + color-grading as subpass if needed
auto colorPassOutput = RendererUtils::colorPass(fg, "Color Pass", mEngine, view, {
.shadows = blackboard.get<FrameGraphTexture>("shadows"),
.ssao = blackboard.get<FrameGraphTexture>("ssao"),
.ssr = ssrConfig.ssr,
.structure = structure
},
auto colorPassOutput = RendererUtils::colorPass(fg, "Color Pass", mEngine, view,
colorBufferDesc, config, colorGradingConfigForColor, pass.getExecutor());
if (view.isScreenSpaceRefractionEnabled() && !pass.empty()) {
// This cancels the colorPass() call above if refraction is active.
// The color pass + refraction + color-grading as subpass if needed
auto const output = RendererUtils::refractionPass(fg, mEngine, view, {
.shadows = blackboard.get<FrameGraphTexture>("shadows"),
.ssao = blackboard.get<FrameGraphTexture>("ssao"),
.ssr = ssrConfig.ssr,
.structure = structure
},
// this cancels the colorPass() call above if refraction is active.
// the color pass + refraction + color-grading as subpass if needed
const auto [output, enabled] = RendererUtils::refractionPass(fg, mEngine, view,
config, ssrConfig, colorGradingConfigForColor, pass);
hasScreenSpaceRefraction = output.has_value();
if (hasScreenSpaceRefraction) {
colorPassOutput = output.value();
}
colorPassOutput = output;
hasScreenSpaceRefraction = enabled;
}
if (colorGradingConfig.customResolve) {
assert_invariant(fg.getDescriptor(colorPassOutput.linearColor).samples <= 1);
// TODO: we have to "uncompress" (i.e. detonemap) the color buffer here because it's used
// by many other passes (Bloom, TAA, DoF, etc...). We could make this more
// efficient by using ARM_shader_framebuffer_fetch. We use a load/store (i.e.
// subpass) here because it's more convenient.
colorPassOutput.linearColor =
ppm.customResolveUncompressPass(fg, colorPassOutput.linearColor);
colorPassOutput = ppm.customResolveUncompressPass(fg, colorPassOutput);
}
// export the color buffer if screen-space reflections are enabled
@@ -1152,9 +1089,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// The "output" of this pass is going to be used during the next frame as
// an "import".
builder.sideEffect();
// we can't use colorPassOutput here because it could be tonemapped
data.history = builder.sample(colorPassOutput.linearColor); // FIXME: an access must be declared for detach(), why?
data.history = builder.sample(colorPassOutput); // FIXME: an access must be declared for detach(), why?
}, [&view, projection](FrameGraphResources const& resources, auto const& data,
backend::DriverApi&) {
auto& history = view.getFrameHistory();
@@ -1164,14 +1099,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
});
}
// this is the output of the color pass / input to post processing,
// this is only used later for comparing it with the output after post-processing
FrameGraphId<FrameGraphTexture> const postProcessInput = colorGradingConfig.asSubpass ?
colorPassOutput.tonemappedColor :
colorPassOutput.linearColor;
// input can change below
FrameGraphId<FrameGraphTexture> input = postProcessInput;
FrameGraphId<FrameGraphTexture> input = colorPassOutput;
fg.addTrivialSideEffectPass("Finish Color Passes", [&view](DriverApi& driver) {
// Unbind SSAO sampler, b/c the FrameGraph will delete the texture at the end of the pass.
view.cleanupRenderPasses();
@@ -1182,7 +1110,8 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// if the depth is not used below or if the depth is not MS (e.g. it could have been
// auto-resolved).
// In practice, this is used on Vulkan and older Metal devices.
auto depth = ppm.resolve(fg, "Resolved Depth Buffer", colorPassOutput.depth, { .levels = 1 });
auto depth = blackboard.get<FrameGraphTexture>("depth");
depth = ppm.resolve(fg, "Resolved Depth Buffer", depth, { .levels = 1 });
// Debug: CSM visualisation
if (UTILS_UNLIKELY(engine.debug.shadowmap.visualize_cascades &&
@@ -1298,7 +1227,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// The intermediate buffer is accomplished with a "fake" opaqueBlit (i.e. blit) operation.
const bool outputIsSwapChain =
(input == postProcessInput) && (viewRenderTarget == mRenderTargetHandle);
(input == colorPassOutput) && (viewRenderTarget == mRenderTargetHandle);
if (mightNeedFinalBlit) {
if (blendModeTranslucent ||
xvp != svp ||
-7
View File
@@ -46,7 +46,6 @@
#include <algorithm>
#include <chrono>
#include <functional>
#include <memory>
#include <utility>
#include <stddef.h>
@@ -54,8 +53,6 @@
namespace filament {
class ResourceAllocator;
namespace backend {
class Driver;
} // namespace backend
@@ -91,9 +88,6 @@ public:
void setVsyncTime(uint64_t steadyClockTimeNano) noexcept;
// skip a frame
void skipFrame(uint64_t vsyncSteadyClockTimeNano);
// start a frame
bool beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano);
@@ -212,7 +206,6 @@ private:
tsl::robin_set<FRenderTarget*> mPreviousRenderTargets;
std::function<void()> mBeginFrameInternal;
uint64_t mVsyncSteadyClockTimeNano = 0;
std::unique_ptr<ResourceAllocator> mResourceAllocator{};
};
FILAMENT_DOWNCAST(Renderer)
-7
View File
@@ -27,8 +27,6 @@
#include <math/half.h>
#include <math/mat4.h>
#include <utils/CString.h>
#include <cstring>
namespace filament {
@@ -82,11 +80,6 @@ FSkinningBuffer::FSkinningBuffer(FEngine& engine, const Builder& builder)
BufferObjectBinding::UNIFORM,
BufferUsage::DYNAMIC);
if (auto name = builder.getName(); !name.empty()) {
// TODO: We should also tag the texture created inside createIndicesAndWeightsHandle.
driver.setDebugTag(mHandle.getId(), std::move(name));
}
if (builder->mInitialize) {
// initialize the bones to identity (before rounding up)
auto* out = driver.allocatePod<PerRenderableBoneUib::BoneData>(mBoneCount);
+1 -5
View File
@@ -23,10 +23,10 @@
#include <backend/PixelBufferDescriptor.h>
#include <utils/CString.h>
#include <utils/Panic.h>
#include <filament/Stream.h>
namespace filament {
using namespace backend;
@@ -80,10 +80,6 @@ FStream::FStream(FEngine& engine, const Builder& builder) noexcept
} else {
mStreamHandle = engine.getDriverApi().createStreamAcquired();
}
if (auto name = builder.getName(); !name.empty()) {
engine.getDriverApi().setDebugTag(mStreamHandle.getId(), std::move(name));
}
}
void FStream::terminate(FEngine& engine) noexcept {
-3
View File
@@ -243,9 +243,6 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) {
mHandle = driver.importTexture(builder->mImportedId,
mTarget, mLevelCount, mFormat, mSampleCount, mWidth, mHeight, mDepth, mUsage);
}
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mHandle.getId(), std::move(name));
}
}
// frees driver resources, object becomes invalid
+1 -10
View File
@@ -29,7 +29,6 @@
#include <utils/bitset.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
@@ -260,9 +259,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
mBufferCount, mDeclaredAttributes.count(), mAttributes);
mHandle = driver.createVertexBuffer(mVertexCount, mVertexBufferInfoHandle);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mHandle.getId(), name);
}
// calculate buffer sizes
size_t bufferSizes[MAX_VERTEX_BUFFER_COUNT] = {};
@@ -290,9 +287,6 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
if (!mBufferObjects[i]) {
BufferObjectHandle bo = driver.createBufferObject(bufferSizes[i],
backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(bo.getId(), name);
}
driver.setVertexBufferObject(mHandle, i, bo);
mBufferObjects[i] = bo;
}
@@ -309,9 +303,6 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
if (!mBufferObjects[i]) {
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(bo.getId(), name);
}
driver.setVertexBufferObject(mHandle, i, bo);
mBufferObjects[i] = bo;
}
+25 -63
View File
@@ -17,7 +17,6 @@
#include "details/View.h"
#include "Culler.h"
#include "FrameHistory.h"
#include "Froxelizer.h"
#include "RenderPrimitive.h"
#include "ResourceAllocator.h"
@@ -31,17 +30,15 @@
#include "details/Skybox.h"
#include <filament/Exposure.h>
#include <filament/DebugRegistry.h>
#include <filament/TextureSampler.h>
#include <filament/View.h>
#include <private/filament/UibStructs.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Profiler.h>
#include <utils/Slice.h>
#include <utils/Systrace.h>
#include <utils/debug.h>
#include <utils/Zip2Iterator.h>
#include <math/scalar.h>
@@ -49,7 +46,6 @@
#include <array>
#include <memory>
#include <tuple>
using namespace utils;
@@ -81,29 +77,21 @@ FView::FView(FEngine& engine)
mPidController.setOutputDeadBand(-0.01f, 0.05f);
#ifndef NDEBUG
// This can fail if another view has already registered this data source
mDebugState->owner = debugRegistry.registerDataSource("d.view.frame_info",
[weak = std::weak_ptr<DebugState>(mDebugState)]() -> DebugRegistry::DataSource {
// the View could have been destroyed by the time we do this
auto const state = weak.lock();
if (!state) {
return { nullptr, 0 };
}
// Lazily allocate the buffer for the debug data source, and mark this
// data source as active. It can never go back to inactive.
assert_invariant(!state->debugFrameHistory);
state->active = true;
state->debugFrameHistory =
std::make_unique<std::array<DebugRegistry::FrameHistory, 5 * 60>>();
return { state->debugFrameHistory->data(), state->debugFrameHistory->size() };
debugRegistry.registerDataSource("d.view.frame_info",
[this]() -> DebugRegistry::DataSource {
assert_invariant(!mDebugFrameHistory);
mDebugFrameHistory = std::make_unique<std::array<DebugRegistry::FrameHistory, 5*60>>();
return { mDebugFrameHistory->data(), mDebugFrameHistory->size() };
});
if (UTILS_UNLIKELY(mDebugState->owner)) {
// publish the properties (they will be initialized in the main loop)
debugRegistry.registerProperty("d.view.pid.kp", &engine.debug.view.pid.kp);
debugRegistry.registerProperty("d.view.pid.ki", &engine.debug.view.pid.ki);
debugRegistry.registerProperty("d.view.pid.kd", &engine.debug.view.pid.kd);
}
debugRegistry.registerProperty("d.view.pid.kp", &engine.debug.view.pid.kp);
debugRegistry.registerProperty("d.view.pid.ki", &engine.debug.view.pid.ki);
debugRegistry.registerProperty("d.view.pid.kd", &engine.debug.view.pid.kd);
// default parameters for debugging UI
engine.debug.view.pid.kp = 1.0f - std::exp(-1.0f / 8.0f);
engine.debug.view.pid.ki = PID_CONTROLLER_Ki;
engine.debug.view.pid.kd = PID_CONTROLLER_Kd;
mPidController.setParallelGains(
engine.debug.view.pid.kp, engine.debug.view.pid.ki, engine.debug.view.pid.kd);
#endif
// allocate UBOs
@@ -130,19 +118,13 @@ void FView::terminate(FEngine& engine) {
DriverApi& driver = engine.getDriverApi();
driver.destroyBufferObject(mLightUbh);
driver.destroyBufferObject(mRenderableUbh);
clearFrameHistory(engine);
drainFrameHistory(engine);
ShadowMapManager::terminate(engine, mShadowMapManager);
mPerViewUniforms.terminate(driver);
mFroxelizer.terminate(driver);
engine.getEntityManager().destroy(mFogEntity);
#ifndef NDEBUG
if (UTILS_UNLIKELY(mDebugState->owner)) {
engine.getDebugRegistry().unregisterDataSource("d.view.frame_info");
}
#endif
}
void FView::setViewport(filament::Viewport const& viewport) noexcept {
@@ -184,16 +166,6 @@ float2 FView::updateScale(FEngine& engine,
Renderer::FrameRateOptions const& frameRateOptions,
Renderer::DisplayInfo const& displayInfo) noexcept {
#ifndef NDEBUG
if (UTILS_LIKELY(!mDebugState->active)) {
// if we're not active, update the debug properties with the normal values
// and use that for configuring the PID controller.
engine.debug.view.pid.kp = 1.0f - std::exp(-frameRateOptions.scaleRate);
engine.debug.view.pid.ki = PID_CONTROLLER_Ki;
engine.debug.view.pid.kd = PID_CONTROLLER_Kd;
}
#endif
DynamicResolutionOptions const& options = mDynamicResolution;
if (options.enabled) {
if (!UTILS_UNLIKELY(info.valid)) {
@@ -277,15 +249,14 @@ float2 FView::updateScale(FEngine& engine,
#ifndef NDEBUG
// only for debugging...
if (UTILS_UNLIKELY(mDebugState->active && mDebugState->debugFrameHistory)) {
auto* const debugFrameHistory = mDebugState->debugFrameHistory.get();
if (mDebugFrameHistory) {
using namespace std::chrono;
using duration_ms = duration<float, std::milli>;
const float target = (1000.0f * float(frameRateOptions.interval)) / displayInfo.refreshRate;
const float targetWithHeadroom = target * (1.0f - frameRateOptions.headRoomRatio);
std::move(debugFrameHistory->begin() + 1,
debugFrameHistory->end(), debugFrameHistory->begin());
debugFrameHistory->back() = {
std::move(mDebugFrameHistory->begin() + 1,
mDebugFrameHistory->end(), mDebugFrameHistory->begin());
mDebugFrameHistory->back() = {
.target = target,
.targetWithHeadroom = targetWithHeadroom,
.frameTime = duration_cast<duration_ms>(info.frameTime).count(),
@@ -1036,29 +1007,20 @@ FrameGraphId<FrameGraphTexture> FView::renderShadowMaps(FEngine& engine, FrameGr
void FView::commitFrameHistory(FEngine& engine) noexcept {
// Here we need to destroy resources in mFrameHistory.back()
auto& disposer = engine.getResourceAllocatorDisposer();
auto& frameHistory = mFrameHistory;
FrameHistoryEntry& last = frameHistory.back();
disposer.destroy(last.taa.color.handle);
disposer.destroy(last.ssr.color.handle);
last.taa.color.handle.clear();
last.ssr.color.handle.clear();
last.taa.color.destroy(engine.getResourceAllocator());
last.ssr.color.destroy(engine.getResourceAllocator());
// and then push the new history entry to the history stack
frameHistory.commit();
}
void FView::clearFrameHistory(FEngine& engine) noexcept {
void FView::drainFrameHistory(FEngine& engine) noexcept {
// make sure we free all resources in the history
auto& disposer = engine.getResourceAllocatorDisposer();
auto& frameHistory = mFrameHistory;
for (size_t i = 0; i < frameHistory.size(); ++i) {
FrameHistoryEntry& last = frameHistory[i];
disposer.destroy(last.taa.color.handle);
disposer.destroy(last.ssr.color.handle);
last.taa.color.handle.clear();
last.ssr.color.handle.clear();
for (size_t i = 0; i < mFrameHistory.size(); ++i) {
commitFrameHistory(engine);
}
}
+8 -13
View File
@@ -201,9 +201,9 @@ public:
void setStereoscopicOptions(StereoscopicOptions const& options) noexcept;
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept {
if (!mShadowMapManager) return {};
return mShadowMapManager->getDirectionalShadowCameras();
FCamera const* getDirectionalShadowCamera() const noexcept {
if (!mShadowMapManager) return nullptr;
return mShadowMapManager->getDirectionalShadowCamera();
}
void setRenderTarget(FRenderTarget* renderTarget) noexcept {
@@ -422,10 +422,6 @@ public:
// (e.g.: after the FrameGraph execution).
void commitFrameHistory(FEngine& engine) noexcept;
// Clean-up the whole history, free all resources. This is typically called when the View is
// being terminated. Or we're changing Renderer.
void clearFrameHistory(FEngine& engine) noexcept;
// create the picking query
View::PickingQuery& pick(uint32_t x, uint32_t y, backend::CallbackHandler* handler,
View::PickingQueryResultCallback callback) noexcept;
@@ -489,6 +485,10 @@ private:
Culler::result_type* visibleMask,
size_t count);
// Clean-up the whole history, free all resources. This is typically called when the View is
// being terminated.
void drainFrameHistory(FEngine& engine) noexcept;
// we don't inline this one, because the function is quite large and there is not much to
// gain from inlining.
static FScene::RenderableSoa::iterator partition(
@@ -576,12 +576,7 @@ private:
}};
#ifndef NDEBUG
struct DebugState {
std::unique_ptr<std::array<DebugRegistry::FrameHistory, 5*60>> debugFrameHistory{};
bool owner = false;
bool active = false;
};
std::shared_ptr<DebugState> mDebugState{ new DebugState };
std::unique_ptr<std::array<DebugRegistry::FrameHistory, 5*60>> mDebugFrameHistory;
#endif
};
-1
View File
@@ -24,7 +24,6 @@
#include "FrameGraphPass.h"
#include "FrameGraphRenderPass.h"
#include "FrameGraphTexture.h"
#include "ResourceAllocator.h"
#include "details/Engine.h"

Some files were not shown because too many files have changed in this diff Show More