Compare commits

..
Author SHA1 Message Date
Mathias Agopian 0404be5127 new LinearAllocatorWithFallback
LinearAllocatorWithFallback is a linear allocator that can fall back
to the heap allocator. We use it for the high level command buffer to
avoid crashing when running out of memory.

FIXES=[277115740]
2024-02-21 14:52:04 -08:00
Mathias Agopian a784fb7d8b Rework RenderPass to improve allocations and API
RenderPass now is a fully immutable object that gets constructed with a
RenderPassBuilder. RenderPassBuilder can be passed around and doesn't
do any (major) allocations.

All RenderPass allocations and heavy lifting is done in 
RenderPassBuilder::Build().

Additionally, RenderPass cannot be copied anymore.

Where allocations happen is now much clearer.
2024-02-21 14:51:57 -08:00
Mathias Agopian 8459cbc91c Fix a couple threading vs. allocations
- prepareVisibleLights was run on a dedicated thread (via JobSystem), 
  but was using its own local ArenaScope. This is wrong because it
  could reset the root arena at any later point. This is fixed by
  just not using a local ArenaScope.

- related to the above, the root Arena (LinearAllocatorArena) didn't
  use a locked policy, which cause also cause problems since some
  allocations are done off the main thread. We now pre-allocate the one
  buffer we need.

This PR also renames some variable and types to improve readability.
2024-02-21 14:51:51 -08:00
Mathias Agopian 344c04a2d8 clenaup CircularBuffer implementation and API
Also fix a bug in DEBUG mode that could corrupt the CircularBuffer, it
was due to a wrong debugging code attempting to clear the unused
area of the buffer (this was wrong because in "ashmem" mode, there are
no guaranteed unused areas).
2024-02-21 14:51:44 -08:00
Mathias Agopian 55e2691f3f Automatically flush CommandStream
When generating commands, we now automatically flush the CommandStream,
so that we're guaranteed to not overrun the circular buffer.
2024-02-21 14:51:36 -08:00
39 changed files with 355 additions and 800 deletions
+1 -1
View File
@@ -129,7 +129,7 @@ jobs:
- name: Sign sample-gltf-viewer
run: |
echo "${APK_KEYSTORE_BASE64}" > filament.jks.base64
base64 --decode -i filament.jks.base64 > filament.jks
base64 --decode filament.jks.base64 > filament.jks
BUILD_TOOLS_VERSION=$(ls ${ANDROID_HOME}/build-tools | sort -V | tail -n 1)
APKSIGNER=${ANDROID_HOME}/build-tools/${BUILD_TOOLS_VERSION}/apksigner
IN_FILE="out/sample-gltf-viewer-release.apk"
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.50.4'
implementation 'com.google.android.filament:filament-android:1.50.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.50.4'
pod 'Filament', '~> 1.50.2'
```
### Snapshots
-3
View File
@@ -7,9 +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.50.4
## v1.50.3
@@ -484,9 +484,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderConfig(JNIEnv*,
jclass, jlong nativeBuilder, jlong commandBufferSizeMB, jlong perRenderPassArenaSizeMB,
jlong driverHandleArenaSizeMB, jlong minCommandBufferSizeMB, jlong perFrameCommandsSizeMB,
jlong jobSystemThreadCount,
jlong textureUseAfterFreePoolSize, jboolean disableParallelShaderCompile,
jint stereoscopicType, jlong stereoscopicEyeCount,
jlong jobSystemThreadCount, jlong stereoscopicEyeCount,
jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
Engine::Config config = {
@@ -496,9 +494,6 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
.minCommandBufferSizeMB = (uint32_t) minCommandBufferSizeMB,
.perFrameCommandsSizeMB = (uint32_t) perFrameCommandsSizeMB,
.jobSystemThreadCount = (uint32_t) jobSystemThreadCount,
.textureUseAfterFreePoolSize = (uint32_t) textureUseAfterFreePoolSize,
.disableParallelShaderCompile = (bool) disableParallelShaderCompile,
.stereoscopicType = (Engine::StereoscopicType) stereoscopicType,
.stereoscopicEyeCount = (uint8_t) stereoscopicEyeCount,
.resourceAllocatorCacheSizeMB = (uint32_t) resourceAllocatorCacheSizeMB,
.resourceAllocatorCacheMaxAge = (uint8_t) resourceAllocatorCacheMaxAge,
@@ -158,16 +158,6 @@ public class Engine {
FEATURE_LEVEL_3,
};
/**
* The type of technique for stereoscopic rendering
*/
public enum StereoscopicType {
/** Stereoscopic rendering is performed using instanced rendering technique. */
INSTANCED,
/** Stereoscopic rendering is performed using the multiview feature from the graphics backend. */
MULTIVIEW,
};
/**
* Constructs <code>Engine</code> objects using a builder pattern.
*/
@@ -221,9 +211,7 @@ public class Engine {
nSetBuilderConfig(mNativeBuilder, config.commandBufferSizeMB,
config.perRenderPassArenaSizeMB, config.driverHandleArenaSizeMB,
config.minCommandBufferSizeMB, config.perFrameCommandsSizeMB,
config.jobSystemThreadCount,
config.textureUseAfterFreePoolSize, config.disableParallelShaderCompile,
config.stereoscopicType.ordinal(), config.stereoscopicEyeCount,
config.jobSystemThreadCount, config.stereoscopicEyeCount,
config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge);
return this;
}
@@ -361,35 +349,6 @@ public class Engine {
*/
public long jobSystemThreadCount = 0;
/**
* Number of most-recently destroyed textures to track for use-after-free.
*
* This will cause the backend to throw an exception when a texture is freed but still bound
* to a SamplerGroup and used in a draw call. 0 disables completely.
*
* Currently only respected by the Metal backend.
*/
public long textureUseAfterFreePoolSize = 0;
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL backend.
*/
public boolean disableParallelShaderCompile = false;
/**
* The type of technique for stereoscopic rendering.
*
* This setting determines the algorithm used when stereoscopic rendering is enabled. This
* decision applies to the entire Engine for the lifetime of the Engine. E.g., multiple
* Views created from the Engine must use the same stereoscopic type.
*
* Each view can enable stereoscopic rendering via the StereoscopicOptions::enable flag.
*
* @see View#setStereoscopicOptions
*/
public StereoscopicType stereoscopicType = StereoscopicType.INSTANCED;
/**
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
* between 1 and Engine#getMaxStereoscopicEyes() (inclusive).
@@ -1281,8 +1240,7 @@ public class Engine {
private static native void nSetBuilderConfig(long nativeBuilder, long commandBufferSizeMB,
long perRenderPassArenaSizeMB, long driverHandleArenaSizeMB,
long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount,
long textureUseAfterFreePoolSize, boolean disableParallelShaderCompile,
int stereoscopicType, long stereoscopicEyeCount,
long stereoscopicEyeCount,
long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge);
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.50.4
VERSION_NAME=1.50.2
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -1212,14 +1212,6 @@ enum class Workaround : uint16_t {
DISABLE_THREAD_AFFINITY
};
//! The type of technique for stereoscopic rendering
enum class StereoscopicType : uint8_t {
// Stereoscopic rendering is performed using instanced rendering technique.
INSTANCED,
// Stereoscopic rendering is performed using the multiview feature from the graphics backend.
MULTIVIEW,
};
} // namespace filament::backend
template<> struct utils::EnableBitMaskOperators<filament::backend::ShaderStageFlags>
+8 -8
View File
@@ -62,6 +62,14 @@ public:
// clear the handle, this doesn't free associated resources
void clear() noexcept { object = nullid; }
// compare handles
bool operator==(const HandleBase& rhs) const noexcept { return object == rhs.object; }
bool operator!=(const HandleBase& rhs) const noexcept { return object != rhs.object; }
bool operator<(const HandleBase& rhs) const noexcept { return object < rhs.object; }
bool operator<=(const HandleBase& rhs) const noexcept { return object <= rhs.object; }
bool operator>(const HandleBase& rhs) const noexcept { return object > rhs.object; }
bool operator>=(const HandleBase& rhs) const noexcept { return object >= rhs.object; }
// get this handle's handleId
HandleId getId() const noexcept { return object; }
@@ -93,14 +101,6 @@ struct Handle : public HandleBase {
explicit Handle(HandleId id) noexcept : HandleBase(id) { }
// compare handles of the same type
bool operator==(const Handle& rhs) const noexcept { return getId() == rhs.getId(); }
bool operator!=(const Handle& rhs) const noexcept { return getId() != rhs.getId(); }
bool operator<(const Handle& rhs) const noexcept { return getId() < rhs.getId(); }
bool operator<=(const Handle& rhs) const noexcept { return getId() <= rhs.getId(); }
bool operator>(const Handle& rhs) const noexcept { return getId() > rhs.getId(); }
bool operator>=(const Handle& rhs) const noexcept { return getId() >= rhs.getId(); }
// type-safe Handle cast
template<typename B, typename = std::enable_if_t<std::is_base_of<T, B>::value> >
Handle(Handle<B> const& base) noexcept : HandleBase(base) { } // NOLINT(hicpp-explicit-conversions,google-explicit-constructor)
@@ -24,31 +24,35 @@
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <tsl/robin_map.h>
#include <cstddef>
#include <exception>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <stddef.h>
#include <stdint.h>
#define HandleAllocatorGL HandleAllocator<16, 64, 208> // ~3640 / pool / MiB
#define HandleAllocatorVK HandleAllocator<80, 176, 320> // ~1820 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<48, 160, 592> // ~1310 / pool / MiB
#if !defined(NDEBUG) && UTILS_HAS_RTTI
# define HANDLE_TYPE_SAFETY 1
#else
# define HANDLE_TYPE_SAFETY 0
#endif
#define HandleAllocatorGL HandleAllocator<16, 64, 208>
#define HandleAllocatorVK HandleAllocator<16, 64, 880>
#define HandleAllocatorMTL HandleAllocator<16, 64, 584>
namespace filament::backend {
/*
* A utility class to efficiently allocate and manage Handle<>
*/
template<size_t P0, size_t P1, size_t P2>
template <size_t P0, size_t P1, size_t P2>
class HandleAllocator {
public:
HandleAllocator(const char* name, size_t size) noexcept;
HandleAllocator(HandleAllocator const& rhs) = delete;
HandleAllocator& operator=(HandleAllocator const& rhs) = delete;
@@ -66,9 +70,14 @@ public:
*/
template<typename D, typename ... ARGS>
Handle<D> allocateAndConstruct(ARGS&& ... args) noexcept {
Handle<D> h{ allocateHandle<D>() };
Handle<D> h{ allocateHandle<sizeof(D)>() };
D* addr = handle_cast<D*>(h);
new(addr) D(std::forward<ARGS>(args)...);
#if HANDLE_TYPE_SAFETY
mLock.lock();
mHandleTypeId[addr] = typeid(D).name();
mLock.unlock();
#endif
return h;
}
@@ -84,7 +93,13 @@ public:
*/
template<typename D>
Handle<D> allocate() noexcept {
Handle<D> h{ allocateHandle<D>() };
Handle<D> h{ allocateHandle<sizeof(D)>() };
#if HANDLE_TYPE_SAFETY
D* addr = handle_cast<D*>(h);
mLock.lock();
mHandleTypeId[addr] = typeid(D).name();
mLock.unlock();
#endif
return h;
}
@@ -101,10 +116,17 @@ public:
assert_invariant(handle);
D* addr = handle_cast<D*>(const_cast<Handle<B>&>(handle));
assert_invariant(addr);
// currently we implement construct<> with dtor+ctor, we could use operator= also
// but all our dtors are trivial, ~D() is actually a noop.
addr->~D();
new(addr) D(std::forward<ARGS>(args)...);
#if HANDLE_TYPE_SAFETY
mLock.lock();
mHandleTypeId[addr] = typeid(D).name();
mLock.unlock();
#endif
return addr;
}
@@ -121,6 +143,12 @@ public:
D* addr = handle_cast<D*>(const_cast<Handle<B>&>(handle));
assert_invariant(addr);
new(addr) D(std::forward<ARGS>(args)...);
#if HANDLE_TYPE_SAFETY
mLock.lock();
mHandleTypeId[addr] = typeid(D).name();
mLock.unlock();
#endif
return addr;
}
@@ -136,8 +164,19 @@ public:
void deallocate(Handle<B>& handle, D const* p) noexcept {
// allow to destroy the nullptr, similarly to operator delete
if (p) {
#if HANDLE_TYPE_SAFETY
mLock.lock();
auto typeId = mHandleTypeId[p];
mHandleTypeId.erase(p);
mLock.unlock();
if (UTILS_UNLIKELY(typeId != typeid(D).name())) {
utils::slog.e << "Destroying handle " << handle.getId() << ", type " << typeid(D).name()
<< ", but handle's actual type is " << typeId << utils::io::endl;
std::terminate();
}
#endif
p->~D();
deallocateHandle<D>(handle.getId());
deallocateHandle<sizeof(D)>(handle.getId());
}
}
@@ -165,17 +204,7 @@ public:
std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
handle_cast(Handle<B>& handle) noexcept {
assert_invariant(handle);
auto [p, tag] = handleToPointer(handle.getId());
if (isPoolHandle(handle.getId())) {
// check for use after free
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;
ASSERT_POSTCONDITION(expectedAge == age,
"use-after-free of Handle with id=%d", handle.getId());
}
void* const p = handleToPointer(handle.getId());
return static_cast<Dp>(p);
}
@@ -190,57 +219,29 @@ public:
private:
template<typename D>
static constexpr size_t getBucketSize() noexcept {
if constexpr (sizeof(D) <= P0) { return P0; }
if constexpr (sizeof(D) <= P1) { return P1; }
static_assert(sizeof(D) <= P2);
return P2;
}
// template <int P0, int P1, int P2>
class Allocator {
friend class HandleAllocator;
static constexpr size_t MIN_ALIGNMENT = alignof(std::max_align_t);
struct Node { uint8_t age; };
// Note: using the `extra` parameter of PoolAllocator<>, even with a 1-byte structure,
// generally increases all pool allocations by 8-bytes because of alignment restrictions.
template<size_t SIZE>
using Pool = utils::PoolAllocator<SIZE, MIN_ALIGNMENT, sizeof(Node)>;
Pool<P0> mPool0;
Pool<P1> mPool1;
Pool<P2> mPool2;
utils::PoolAllocator<P0, 16> mPool0;
utils::PoolAllocator<P1, 16> mPool1;
utils::PoolAllocator<P2, 16> mPool2;
UTILS_UNUSED_IN_RELEASE const utils::AreaPolicy::HeapArea& mArea;
public:
static constexpr size_t MIN_ALIGNMENT_SHIFT = 4;
explicit Allocator(const utils::AreaPolicy::HeapArea& area);
static constexpr size_t getAlignment() noexcept { return MIN_ALIGNMENT; }
// this is in fact always called with a constexpr size argument
[[nodiscard]] inline void* alloc(size_t size, size_t, size_t, uint8_t* outAge) noexcept {
[[nodiscard]] inline void* alloc(size_t size, size_t, size_t extra = 0) noexcept {
void* p = nullptr;
if (size <= mPool0.getSize()) p = mPool0.alloc(size);
else if (size <= mPool1.getSize()) p = mPool1.alloc(size);
else if (size <= mPool2.getSize()) p = mPool2.alloc(size);
if (UTILS_LIKELY(p)) {
Node const* const pNode = static_cast<Node const*>(p);
// we are guaranteed to have at least sizeof<Node> bytes of extra storage before
// the allocation address.
*outAge = pNode[-1].age;
}
if (size <= mPool0.getSize()) p = mPool0.alloc(size, 16, extra);
else if (size <= mPool1.getSize()) p = mPool1.alloc(size, 16, extra);
else if (size <= mPool2.getSize()) p = mPool2.alloc(size, 16, extra);
return p;
}
// this is in fact always called with a constexpr size argument
inline void free(void* p, size_t size, uint8_t age) noexcept {
inline void free(void* p, size_t size) noexcept {
assert_invariant(p >= mArea.begin() && (char*)p + size <= (char*)mArea.end());
// check for double-free
Node* const pNode = static_cast<Node*>(p);
uint8_t& expectedAge = pNode[-1].age;
ASSERT_POSTCONDITION(expectedAge == age,
"double-free of Handle of size %d at %p", size, p);
expectedAge = (expectedAge + 1) & 0xF; // fixme
if (size <= mPool0.getSize()) { mPool0.free(p); return; }
if (size <= mPool1.getSize()) { mPool1.free(p); return; }
if (size <= mPool2.getSize()) { mPool2.free(p); return; }
@@ -262,16 +263,24 @@ private:
// allocateHandle()/deallocateHandle() selects the pool to use at compile-time based on the
// allocation size this is always inlined, because all these do is to call
// allocateHandleInPool()/deallocateHandleFromPool() with the right pool size.
template<typename D>
template<size_t SIZE>
HandleBase::HandleId allocateHandle() noexcept {
constexpr size_t BUCKET_SIZE = getBucketSize<D>();
return allocateHandleInPool<BUCKET_SIZE>();
if constexpr (SIZE <= P0) { return allocateHandleInPool<P0>(); }
if constexpr (SIZE <= P1) { return allocateHandleInPool<P1>(); }
static_assert(SIZE <= P2);
return allocateHandleInPool<P2>();
}
template<typename D>
template<size_t SIZE>
void deallocateHandle(HandleBase::HandleId id) noexcept {
constexpr size_t BUCKET_SIZE = getBucketSize<D>();
deallocateHandleFromPool<BUCKET_SIZE>(id);
if constexpr (SIZE <= P0) {
deallocateHandleFromPool<P0>(id);
} else if constexpr (SIZE <= P1) {
deallocateHandleFromPool<P1>(id);
} else {
static_assert(SIZE <= P2);
deallocateHandleFromPool<P2>(id);
}
}
// allocateHandleInPool()/deallocateHandleFromPool() is NOT inlined, which will cause three
@@ -280,11 +289,9 @@ private:
template<size_t SIZE>
UTILS_NOINLINE
HandleBase::HandleId allocateHandleInPool() noexcept {
uint8_t age;
void* p = mHandleArena.alloc(SIZE, alignof(std::max_align_t), 0, &age);
void* p = mHandleArena.alloc(SIZE);
if (UTILS_LIKELY(p)) {
uint32_t const tag = (uint32_t(age) << HANDLE_AGE_SHIFT) & HANDLE_AGE_MASK;
return arenaPointerToHandle(p, tag);
return pointerToHandle(p);
} else {
return allocateHandleSlow(SIZE);
}
@@ -294,51 +301,42 @@ private:
UTILS_NOINLINE
void deallocateHandleFromPool(HandleBase::HandleId id) noexcept {
if (UTILS_LIKELY(isPoolHandle(id))) {
auto [p, tag] = handleToPointer(id);
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
mHandleArena.free(p, SIZE, age);
void* p = handleToPointer(id);
mHandleArena.free(p, SIZE);
} else {
deallocateHandleSlow(id, SIZE);
}
}
// 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 constexpr uint32_t HEAP_HANDLE_FLAG = 0x80000000u;
static bool isPoolHandle(HandleBase::HandleId id) noexcept {
return (id & HANDLE_HEAP_FLAG) == 0u;
return (id & HEAP_HANDLE_FLAG) == 0u;
}
HandleBase::HandleId allocateHandleSlow(size_t size) noexcept;
void deallocateHandleSlow(HandleBase::HandleId id, size_t size) noexcept;
// We inline this because it's just 4 instructions in the fast case
inline std::pair<void*, uint32_t> handleToPointer(HandleBase::HandleId id) const noexcept {
inline void* handleToPointer(HandleBase::HandleId id) const noexcept {
// note: the null handle will end-up returning nullptr b/c it'll be handled as
// a non-pool handle.
if (UTILS_LIKELY(isPoolHandle(id))) {
char* const base = (char*)mHandleArena.getArea().begin();
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 };
size_t offset = id << Allocator::MIN_ALIGNMENT_SHIFT;
return static_cast<void*>(base + offset);
}
return { handleToPointerSlow(id), 0 };
return handleToPointerSlow(id);
}
void* handleToPointerSlow(HandleBase::HandleId id) const noexcept;
// We inline this because it's just 3 instructions
inline HandleBase::HandleId arenaPointerToHandle(void* p, uint32_t tag) const noexcept {
inline HandleBase::HandleId pointerToHandle(void* p) const noexcept {
char* const base = (char*)mHandleArena.getArea().begin();
size_t const offset = (char*)p - base;
assert_invariant((offset % Allocator::getAlignment()) == 0);
auto id = HandleBase::HandleId(offset / Allocator::getAlignment());
id |= tag & HANDLE_TAG_MASK;
assert_invariant((id & HANDLE_HEAP_FLAG) == 0);
size_t offset = (char*)p - base;
auto id = HandleBase::HandleId(offset >> Allocator::MIN_ALIGNMENT_SHIFT);
assert_invariant((id & HEAP_HANDLE_FLAG) == 0);
return id;
}
@@ -348,6 +346,9 @@ private:
mutable utils::Mutex mLock;
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
HandleBase::HandleId mId = 0;
#if HANDLE_TYPE_SAFETY
mutable std::unordered_map<const void*, const char*> mHandleTypeId;
#endif
};
} // namespace filament::backend
+11 -50
View File
@@ -16,22 +16,9 @@
#include "private/backend/HandleAllocator.h"
#include <backend/Handle.h>
#include <utils/Allocator.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <algorithm>
#include <exception>
#include <limits>
#include <mutex>
#include <stdlib.h>
#include <string.h>
namespace filament::backend {
@@ -41,34 +28,14 @@ template <size_t P0, size_t P1, size_t P2>
UTILS_NOINLINE
HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& area)
: mArea(area) {
// The largest handle this allocator can generate currently depends on the architecture's
// min alignment, typically 8 or 16 bytes.
// e.g. On Android armv8, the alignment is 16 bytes, so for a 1 MiB heap, the largest handle
// index will be 65536. Note that this is not the same as the number of handles (which
// will always be less).
// Because our maximum representable handle currently is 0x07FFFFFF, the maximum no-nonsensical
// heap size is 2 GiB, which amounts to 7.6 millions handles per pool (in the GL case).
size_t const maxHeapSize = std::min(area.size(), HANDLE_INDEX_MASK * getAlignment());
if (UTILS_UNLIKELY(maxHeapSize != area.size())) {
slog.w << "HandleAllocator heap size reduced to "
<< maxHeapSize << " from " << area.size() << io::endl;
}
// make sure we start with a clean arena. This is needed to ensure that all blocks start
// with an age of 0.
memset(area.data(), 0, maxHeapSize);
// size the different pools so that they can all contain the same number of handles
size_t const count = maxHeapSize / (P0 + P1 + P2);
char* const p0 = static_cast<char*>(area.begin());
char* const p1 = p0 + count * P0;
char* const p2 = p1 + count * P1;
mPool0 = Pool<P0>(p0, count * P0);
mPool1 = Pool<P1>(p1, count * P1);
mPool2 = Pool<P2>(p2, count * P2);
// TODO: we probably need a better way to set the size of these pools
const size_t unit = area.size() / 32;
const size_t offsetPool1 = unit;
const size_t offsetPool2 = 16 * unit;
char* const p = (char*)area.begin();
mPool0 = PoolAllocator< P0, 16>(p, p + offsetPool1);
mPool1 = PoolAllocator< P1, 16>(p + offsetPool1, p + offsetPool2);
mPool2 = PoolAllocator< P2, 16>(p + offsetPool2, area.end());
}
// ------------------------------------------------------------------------------------------------
@@ -106,17 +73,11 @@ template <size_t P0, size_t P1, size_t P2>
HandleBase::HandleId HandleAllocator<P0, P1, P2>::allocateHandleSlow(size_t size) noexcept {
void* p = ::malloc(size);
std::unique_lock lock(mLock);
HandleBase::HandleId id = (++mId) | HANDLE_HEAP_FLAG;
ASSERT_POSTCONDITION(mId < HANDLE_HEAP_FLAG,
"No more Handle ids available! This can happen if HandleAllocator arena has been full"
" for a while. Please increase FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB");
HandleBase::HandleId id = (++mId) | HEAP_HANDLE_FLAG;
mOverflowMap.emplace(id, p);
lock.unlock();
if (UTILS_UNLIKELY(id == (HANDLE_HEAP_FLAG | 1u))) { // meaning id was zero
if (UTILS_UNLIKELY(id == (HEAP_HANDLE_FLAG|1u))) { // meaning id was zero
PANIC_LOG("HandleAllocator arena is full, using slower system heap. Please increase "
"the appropriate constant (e.g. FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB).");
}
@@ -125,7 +86,7 @@ HandleBase::HandleId HandleAllocator<P0, P1, P2>::allocateHandleSlow(size_t size
template <size_t P0, size_t P1, size_t P2>
void HandleAllocator<P0, P1, P2>::deallocateHandleSlow(HandleBase::HandleId id, size_t) noexcept {
assert_invariant(id & HANDLE_HEAP_FLAG);
assert_invariant(id & HEAP_HANDLE_FLAG);
void* p = nullptr;
auto& overflowMap = mOverflowMap;
-34
View File
@@ -43,40 +43,6 @@ namespace filament {
namespace backend {
Driver* MetalDriverFactory::create(MetalPlatform* const platform, const Platform::DriverConfig& driverConfig) {
#if 0
// this is useful for development, but too verbose even for debug builds
// For reference on a 64-bits machine in Release mode:
// MetalTimerQuery : 16 few
// HwStream : 24 few
// MetalIndexBuffer : 40 moderate
// MetalFence : 48 few
// MetalBufferObject : 48 many
// -- less than or equal 48 bytes
// MetalSamplerGroup : 112 few
// MetalProgram : 144 moderate
// MetalTexture : 152 moderate
// MetalVertexBuffer : 152 moderate
// -- less than or equal 160 bytes
// MetalSwapChain : 184 few
// MetalRenderTarget : 272 few
// MetalRenderPrimitive : 584 many
// -- less than or equal to 592 bytes
utils::slog.d
<< "\nMetalSwapChain: " << sizeof(MetalSwapChain)
<< "\nMetalBufferObject: " << sizeof(MetalBufferObject)
<< "\nMetalVertexBuffer: " << sizeof(MetalVertexBuffer)
<< "\nMetalIndexBuffer: " << sizeof(MetalIndexBuffer)
<< "\nMetalSamplerGroup: " << sizeof(MetalSamplerGroup)
<< "\nMetalRenderPrimitive: " << sizeof(MetalRenderPrimitive)
<< "\nMetalTexture: " << sizeof(MetalTexture)
<< "\nMetalTimerQuery: " << sizeof(MetalTimerQuery)
<< "\nHwStream: " << sizeof(HwStream)
<< "\nMetalRenderTarget: " << sizeof(MetalRenderTarget)
<< "\nMetalFence: " << sizeof(MetalFence)
<< "\nMetalProgram: " << sizeof(MetalProgram)
<< utils::io::endl;
#endif
return MetalDriver::create(platform, driverConfig);
}
+11 -11
View File
@@ -90,24 +90,24 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
#if 0
// this is useful for development, but too verbose even for debug builds
// For reference on a 64-bits machine in Release mode:
// GLFence : 8 few
// GLIndexBuffer : 8 moderate
// GLSamplerGroup : 16 few
// GLSwapChain : 16 few
// GLTimerQuery : 16 few
// GLSamplerGroup : 8 few
// -- less than or equal 16 bytes
// GLFence : 24 few
// GLBufferObject : 32 many
// GLRenderPrimitive : 40 many
// OpenGLProgram : 56 moderate
// GLTexture : 64 moderate
// GLBufferObject : 24 many
// GLSync : 24 few
// GLTimerQuery : 32 few
// OpenGLProgram : 32 moderate
// GLRenderPrimitive : 48 many
// -- less than or equal 64 bytes
// GLStream : 104 few
// GLTexture : 72 moderate
// GLRenderTarget : 112 few
// GLStream : 184 few
// GLVertexBuffer : 200 moderate
// -- less than or equal to 208 bytes
slog.d
<< "\nGLSwapChain: " << sizeof(GLSwapChain)
<< "HwFence: " << sizeof(HwFence)
<< "\nGLBufferObject: " << sizeof(GLBufferObject)
<< "\nGLVertexBuffer: " << sizeof(GLVertexBuffer)
<< "\nGLIndexBuffer: " << sizeof(GLIndexBuffer)
@@ -117,7 +117,7 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
<< "\nGLTimerQuery: " << sizeof(GLTimerQuery)
<< "\nGLStream: " << sizeof(GLStream)
<< "\nGLRenderTarget: " << sizeof(GLRenderTarget)
<< "\nGLFence: " << sizeof(GLFence)
<< "\nGLSync: " << sizeof(GLSync)
<< "\nOpenGLProgram: " << sizeof(OpenGLProgram)
<< io::endl;
#endif
+21 -56
View File
@@ -213,41 +213,6 @@ VulkanDriver::~VulkanDriver() noexcept = default;
UTILS_NOINLINE
Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig) noexcept {
#if 0
// this is useful for development, but too verbose even for debug builds
// For reference on a 64-bits machine in Release mode:
// VulkanSamplerGroup : 24 few
// HwStream : 24 few
// VulkanFence : 40 few
// VulkanProgram : 40 moderate
// VulkanIndexBuffer : 72 moderate
// VulkanBufferObject : 72 many
// -- less than or equal 80 bytes
// VulkanRenderPrimitive : 104 many
// VulkanSwapChain : 112 few
// VulkanTimerQuery : 168 few
// -- less than or equal 176 bytes
// VulkanTexture : 232 moderate
// VulkanVertexBuffer : 312 moderate
// VulkanRenderTarget : 320 few
// -- less than or equal to 320 bytes
utils::slog.d
<< "\nVulkanSwapChain: " << sizeof(VulkanSwapChain)
<< "\nVulkanBufferObject: " << sizeof(VulkanBufferObject)
<< "\nVulkanVertexBuffer: " << sizeof(VulkanVertexBuffer)
<< "\nVulkanIndexBuffer: " << sizeof(VulkanIndexBuffer)
<< "\nVulkanSamplerGroup: " << sizeof(VulkanSamplerGroup)
<< "\nVulkanRenderPrimitive: " << sizeof(VulkanRenderPrimitive)
<< "\nVulkanTexture: " << sizeof(VulkanTexture)
<< "\nVulkanTimerQuery: " << sizeof(VulkanTimerQuery)
<< "\nHwStream: " << sizeof(HwStream)
<< "\nVulkanRenderTarget: " << sizeof(VulkanRenderTarget)
<< "\nVulkanFence: " << sizeof(VulkanFence)
<< "\nVulkanProgram: " << sizeof(VulkanProgram)
<< utils::io::endl;
#endif
assert_invariant(platform);
size_t defaultSize = FVK_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig {driverConfig};
@@ -1676,26 +1641,26 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
// Update the VK raster state.
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
VulkanPipelineCache::RasterState const vulkanRasterState{
.cullMode = getCullMode(rasterState.culling),
.frontFace = getFrontFace(rasterState.inverseFrontFaces),
.depthBiasEnable = (depthOffset.constant || depthOffset.slope) ? true : false,
.blendEnable = rasterState.hasBlending(),
.depthWriteEnable = rasterState.depthWrite,
.alphaToCoverageEnable = rasterState.alphaToCoverage,
.srcColorBlendFactor = getBlendFactor(rasterState.blendFunctionSrcRGB),
.dstColorBlendFactor = getBlendFactor(rasterState.blendFunctionDstRGB),
.srcAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionSrcAlpha),
.dstAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionDstAlpha),
.colorWriteMask = (VkColorComponentFlags) (rasterState.colorWrite ? 0xf : 0x0),
.rasterizationSamples = rt->getSamples(),
.colorTargetCount = rt->getColorTargetCount(mCurrentRenderPass),
.colorBlendOp = rasterState.blendEquationRGB,
.alphaBlendOp = rasterState.blendEquationAlpha,
.depthCompareOp = rasterState.depthFunc,
.depthBiasConstantFactor = depthOffset.constant,
.depthBiasSlopeFactor = depthOffset.slope
};
auto vkraster = mPipelineCache.getCurrentRasterState();
vkraster.cullMode = getCullMode(rasterState.culling);
vkraster.frontFace = getFrontFace(rasterState.inverseFrontFaces);
vkraster.depthBiasEnable = (depthOffset.constant || depthOffset.slope) ? true : false;
vkraster.depthBiasConstantFactor = depthOffset.constant;
vkraster.depthBiasSlopeFactor = depthOffset.slope;
vkraster.blendEnable = rasterState.hasBlending();
vkraster.srcColorBlendFactor = getBlendFactor(rasterState.blendFunctionSrcRGB);
vkraster.dstColorBlendFactor = getBlendFactor(rasterState.blendFunctionDstRGB);
vkraster.colorBlendOp = rasterState.blendEquationRGB;
vkraster.srcAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionSrcAlpha);
vkraster.dstAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionDstAlpha);
vkraster.alphaBlendOp = rasterState.blendEquationAlpha;
vkraster.colorWriteMask = (VkColorComponentFlags) (rasterState.colorWrite ? 0xf : 0x0);
vkraster.depthWriteEnable = rasterState.depthWrite;
vkraster.depthCompareOp = rasterState.depthFunc;
vkraster.rasterizationSamples = rt->getSamples();
vkraster.alphaToCoverageEnable = rasterState.alphaToCoverage;
vkraster.colorTargetCount = rt->getColorTargetCount(mCurrentRenderPass);
mPipelineCache.setCurrentRasterState(vkraster);
// Declare fixed-size arrays that get passed to the pipeCache and to vkCmdBindVertexBuffers.
uint32_t const bufferCount = prim.vertexBuffer->attributes.size();
@@ -1706,7 +1671,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
// Push state changes to the VulkanPipelineCache instance. This is fast and does not make VK calls.
mPipelineCache.bindProgram(program);
mPipelineCache.bindRasterState(vulkanRasterState);
mPipelineCache.bindRasterState(mPipelineCache.getCurrentRasterState());
mPipelineCache.bindPrimitiveTopology(prim.primitiveTopology);
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, bufferCount);
@@ -34,6 +34,8 @@ using namespace bluevk;
namespace filament::backend {
static VulkanPipelineCache::RasterState createDefaultRasterState();
static VkShaderStageFlags getShaderStageFlags(VulkanPipelineCache::UsageFlags key, uint16_t binding) {
// NOTE: if you modify this function, you also need to modify getUsageFlags.
assert_invariant(binding < MAX_SAMPLER_COUNT);
@@ -71,7 +73,8 @@ VulkanPipelineCache::UsageFlags VulkanPipelineCache::disableUsageFlags(uint16_t
}
VulkanPipelineCache::VulkanPipelineCache(VulkanResourceAllocator* allocator)
: mResourceAllocator(allocator),
: mCurrentRasterState(createDefaultRasterState()),
mResourceAllocator(allocator),
mPipelineBoundResources(allocator) {
mDummyBufferWriteInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
mDummyBufferWriteInfo.pNext = nullptr;
@@ -566,7 +569,7 @@ void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
}
void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept {
mPipelineRequirements.rasterState = rasterState;
mPipelineRequirements.rasterState = mCurrentRasterState = rasterState;
}
void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept {
@@ -914,6 +917,23 @@ bool VulkanPipelineCache::DescEqual::operator()(const DescriptorKey& k1,
return true;
}
static VulkanPipelineCache::RasterState createDefaultRasterState() {
return VulkanPipelineCache::RasterState {
.cullMode = VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.depthBiasEnable = VK_FALSE,
.blendEnable = VK_FALSE,
.depthWriteEnable = VK_TRUE,
.alphaToCoverageEnable = true,
.colorWriteMask = 0xf,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
.colorTargetCount = 1,
.depthCompareOp = SamplerCompareFunc::LE,
.depthBiasConstantFactor = 0.0f,
.depthBiasSlopeFactor = 0.0f,
};
}
} // namespace filament::backend
#pragma clang diagnostic pop
@@ -199,6 +199,15 @@ public:
mPipelineBoundResources.acquire(resource);
}
inline RasterState getCurrentRasterState() const noexcept {
return mCurrentRasterState;
}
// We need to update this outside of bindRasterState due to VulkanDriver::draw.
inline void setCurrentRasterState(RasterState const& rasterState) noexcept {
mCurrentRasterState = rasterState;
}
private:
// PIPELINE LAYOUT CACHE KEY
// -------------------------
@@ -404,6 +413,7 @@ private:
VmaAllocator mAllocator = VK_NULL_HANDLE;
// Current requirements for the pipeline layout, pipeline, and descriptor sets.
RasterState mCurrentRasterState;
PipelineKey mPipelineRequirements = {};
DescriptorKey mDescriptorRequirements = {};
@@ -63,8 +63,7 @@ protected:
explicit VulkanResourceBase(VulkanResourceType type)
: mRefCount(IS_HEAP_ALLOC_TYPE(type) ? 1 : 0),
mType(type),
mHandleId(0) {
}
mHandleId(0) {}
private:
inline VulkanResourceType getType() {
@@ -83,7 +82,6 @@ private:
if (IS_HEAP_ALLOC_TYPE(mType)) {
return;
}
assert_invariant(mRefCount < ((1<<24) - 1));
++mRefCount;
}
@@ -91,7 +89,6 @@ private:
if (IS_HEAP_ALLOC_TYPE(mType)) {
return;
}
assert_invariant(mRefCount > 0);
--mRefCount;
}
@@ -99,8 +96,8 @@ private:
return mRefCount;
}
uint32_t mRefCount : 24; // 16M is enough for the refcount
VulkanResourceType mType : 8;
size_t mRefCount = 0;
VulkanResourceType mType = VulkanResourceType::BUFFER_OBJECT;
HandleBase::HandleId mHandleId;
friend struct VulkanThreadSafeResource;
-20
View File
@@ -178,7 +178,6 @@ public:
using Backend = backend::Backend;
using DriverConfig = backend::Platform::DriverConfig;
using FeatureLevel = backend::FeatureLevel;
using StereoscopicType = backend::StereoscopicType;
/**
* Config is used to define the memory footprint used by the engine, such as the
@@ -298,25 +297,6 @@ public:
*/
size_t textureUseAfterFreePoolSize = 0;
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL backend.
*/
bool disableParallelShaderCompile = false;
/*
* The type of technique for stereoscopic rendering.
*
* This setting determines the algorithm used when stereoscopic rendering is enabled. This
* decision applies to the entire Engine for the lifetime of the Engine. E.g., multiple
* Views created from the Engine must use the same stereoscopic type.
*
* Each view can enable stereoscopic rendering via the StereoscopicOptions::enable flag.
*
* @see View::setStereoscopicOptions
*/
StereoscopicType stereoscopicType = StereoscopicType::INSTANCED;
/*
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
* between 1 and Engine::getMaxStereoscopicEyes() (inclusive).
+1 -1
View File
@@ -719,7 +719,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.
Camera const* UTILS_NULLABLE getDirectionalShadowCamera() const noexcept;
Camera const* UTILS_NULLABLE getDirectionalLightCamera() const noexcept;
/** Result of a picking query */
-5
View File
@@ -164,11 +164,6 @@ void RenderPass::appendCommands(FEngine& engine,
// trace the number of visible renderables
SYSTRACE_VALUE32("visibleRenderables", vr.size());
if (UTILS_UNLIKELY(vr.empty())) {
// no renderables, we still need the sentinel and the command buffer size should be
// exactly 1.
assert_invariant(commands.size() == 1);
Command* curr = commands.data();
curr->key = uint64_t(Pass::SENTINEL);
return;
}
+1
View File
@@ -407,6 +407,7 @@ private:
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> vr) noexcept;
// Arena where all Commands are allocated. The Arena owns the commands.
FScene::RenderableSoa const& mRenderableSoa;
utils::Range<uint32_t> const mVisibleRenderables;
backend::Handle<backend::HwBufferObject> const mUboHandle;
+1 -1
View File
@@ -109,7 +109,7 @@ size_t ResourceAllocator::TextureKey::getSize() const noexcept {
}
ResourceAllocator::ResourceAllocator(Engine::Config const& config, DriverApi& driverApi) noexcept
: mCacheCapacity(config.resourceAllocatorCacheSizeMB << 20),
: mCacheCapacity(config.resourceAllocatorCacheSizeMB),
mCacheMaxAge(config.resourceAllocatorCacheMaxAge),
mBackend(driverApi) {
}
+2 -23
View File
@@ -16,39 +16,19 @@
#include "ShadowMap.h"
#include <filament/Box.h>
#include <filament/Frustum.h>
#include <filament/LightManager.h>
#include "RenderPass.h"
#include "components/LightManager.h"
#include "details/DebugRegistry.h"
#include "details/Engine.h"
#include "details/Scene.h"
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Entity.h>
#include <utils/Slice.h>
#include <utils/Systrace.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat3.h>
#include <math/mat4.h>
#include <math/scalar.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <limits>
#include <type_traits>
#include <stddef.h>
#include <stdint.h>
using namespace utils;
@@ -259,8 +239,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
ShadowMap::ShaderParameters ShadowMap::updatePunctual(
mat4f const& Mv, float outerConeAngle, float nearPlane, float farPlane,
const ShadowMapInfo& shadowMapInfo, const FLightManager::ShadowParams& params) noexcept {
const mat4f Mp = mat4f::perspective(
outerConeAngle * f::RAD_TO_DEG * 2.0f, 1.0f, nearPlane, farPlane);
const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f, 1.0f, nearPlane, farPlane);
assert_invariant(shadowMapInfo.textureDimension == mOptions->mapSize);
+4 -14
View File
@@ -17,30 +17,20 @@
#ifndef TNT_FILAMENT_DETAILS_SHADOWMAP_H
#define TNT_FILAMENT_DETAILS_SHADOWMAP_H
#include <filament/Box.h>
#include "components/LightManager.h"
#include "Culler.h"
#include "PerShadowMapUniforms.h"
#include "details/Camera.h"
#include "details/Scene.h"
#include "components/LightManager.h"
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <filament/Box.h>
#include <filament/Viewport.h>
#include <math/mathfwd.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat4.h>
#include <array>
#include <stddef.h>
#include <stdint.h>
#include <math/vec4.h>
namespace filament {
+44 -116
View File
@@ -15,49 +15,25 @@
*/
#include "ShadowMapManager.h"
#include "RenderPass.h"
#include "ShadowMap.h"
#include <filament/Frustum.h>
#include <filament/LightManager.h>
#include <filament/Options.h>
#include <private/filament/EngineEnums.h>
#include "components/RenderableManager.h"
#include "details/Camera.h"
#include "details/DebugRegistry.h"
#include "details/Texture.h"
#include "details/View.h"
#include "fg/FrameGraph.h"
#include "fg/FrameGraphId.h"
#include "fg/FrameGraphRenderPass.h"
#include "fg/FrameGraphTexture.h"
#include <fg/FrameGraph.h>
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Range.h>
#include <utils/Slice.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <math/half.h>
#include <math/mat4.h>
#include <math/vec4.h>
#include <math/scalar.h>
#include <cmath>
#include <limits>
#include <new>
#include <memory>
#include <stdint.h>
#include <stddef.h>
namespace filament {
using namespace backend;
@@ -65,6 +41,15 @@ using namespace math;
ShadowMapManager::ShadowMapManager(FEngine& engine)
: mEngine(engine) {
// initialize our ShadowMap array in-place
UTILS_NOUNROLL
for (auto& entry : mShadowMapCache) {
new (&entry) ShadowMap(engine);
}
mShadowUbh = engine.getDriverApi().createBufferObject(mShadowUb.getSize(),
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.shadowmap.visualize_cascades",
&engine.debug.shadowmap.visualize_cascades);
@@ -74,75 +59,25 @@ ShadowMapManager::ShadowMapManager(FEngine& engine)
ShadowMapManager::~ShadowMapManager() {
// destroy the ShadowMap array in-place
if (UTILS_UNLIKELY(mInitialized)) {
UTILS_NOUNROLL
for (auto& entry: mShadowMapCache) {
std::destroy_at(std::launder(reinterpret_cast<ShadowMap*>(&entry)));
}
}
}
void ShadowMapManager::createIfNeeded(FEngine& engine,
std::unique_ptr<ShadowMapManager>& inOutShadowMapManager) {
if (UTILS_UNLIKELY(!inOutShadowMapManager)) {
inOutShadowMapManager.reset(new ShadowMapManager(engine));
}
}
void ShadowMapManager::terminate(FEngine& engine,
std::unique_ptr<ShadowMapManager>& shadowMapManager) {
if (shadowMapManager) {
shadowMapManager->terminate(engine);
UTILS_NOUNROLL
for (auto& entry : mShadowMapCache) {
std::destroy_at(std::launder(reinterpret_cast<ShadowMap*>(&entry)));
}
}
void ShadowMapManager::terminate(FEngine& engine) {
if (UTILS_UNLIKELY(mInitialized)) {
DriverApi& driver = engine.getDriverApi();
driver.destroyBufferObject(mShadowUbh);
UTILS_NOUNROLL
for (auto& entry: mShadowMapCache) {
std::launder(reinterpret_cast<ShadowMap*>(&entry))->terminate(engine);
}
DriverApi& driver = engine.getDriverApi();
driver.destroyBufferObject(mShadowUbh);
UTILS_NOUNROLL
for (auto& entry : mShadowMapCache) {
std::launder(reinterpret_cast<ShadowMap*>(&entry))->terminate(engine);
}
}
ShadowMapManager::ShadowTechnique ShadowMapManager::update(
Builder const& builder,
FEngine& engine, FView& view,
ShadowMapManager::ShadowTechnique ShadowMapManager::update(FEngine& engine, FView& view,
CameraInfo const& cameraInfo,
FScene::RenderableSoa& renderableData, FScene::LightSoa const& lightData) noexcept {
if (!builder.mDirectionalShadowMapCount && !builder.mSpotShadowMapCount) {
// no shadows were recorder
return ShadowTechnique::NONE;
}
// initialize the shadowmap array the first time
if (UTILS_UNLIKELY(!mInitialized)) {
mInitialized = true;
// initialize our ShadowMap array in-place
mShadowUbh = engine.getDriverApi().createBufferObject(mShadowUb.getSize(),
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
UTILS_NOUNROLL
for (auto& entry: mShadowMapCache) {
new(&entry) ShadowMap(engine);
}
}
mDirectionalShadowMapCount = builder.mDirectionalShadowMapCount;
mSpotShadowMapCount = builder.mSpotShadowMapCount;
for (auto const& entry : builder.mShadowMaps) {
auto& shadowMap = getShadowMap(entry.shadowIndex);
shadowMap.initialize(
entry.lightIndex,
entry.shadowType,
entry.shadowIndex,
entry.face,
entry.options);
}
ShadowTechnique shadowTechnique = {};
calculateTextureRequirements(engine, view, lightData);
@@ -161,49 +96,42 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(
return shadowTechnique;
}
ShadowMapManager::Builder& ShadowMapManager::Builder::directionalShadowMap(size_t lightIndex,
LightManager::ShadowOptions const* options) noexcept {
assert_invariant(options->shadowCascades <= CONFIG_MAX_SHADOW_CASCADES);
// this updates getCascadedShadowMap()
mDirectionalShadowMapCount = options->shadowCascades;
for (size_t c = 0; c < options->shadowCascades; c++) {
mShadowMaps.push_back({
.lightIndex = lightIndex,
.shadowType = ShadowType::DIRECTIONAL,
.shadowIndex = uint8_t(c),
.face = 0,
.options = options });
}
return *this;
void ShadowMapManager::reset() noexcept {
mDirectionalShadowMapCount = 0;
mSpotShadowMapCount = 0;
}
ShadowMapManager::Builder& ShadowMapManager::Builder::shadowMap(size_t lightIndex, bool spotlight,
void ShadowMapManager::setDirectionalShadowMap(size_t lightIndex,
LightManager::ShadowOptions const* options) noexcept {
assert_invariant(options->shadowCascades <= CONFIG_MAX_SHADOW_CASCADES);
// this updates getCascadedShadowMap()
mDirectionalShadowMapCount = options->shadowCascades;
utils::Slice<ShadowMap> cascadedShadowMap = getCascadedShadowMap();
for (size_t c = 0; c < options->shadowCascades; c++) {
ShadowMap& shadowMap = cascadedShadowMap[c];
shadowMap.initialize(lightIndex, ShadowType::DIRECTIONAL, c, 0, options);
}
}
void ShadowMapManager::addShadowMap(size_t lightIndex, bool spotlight,
LightManager::ShadowOptions const* options) noexcept {
if (spotlight) {
const size_t c = mSpotShadowMapCount++;
const size_t i = c + CONFIG_MAX_SHADOW_CASCADES;
assert_invariant(i < CONFIG_MAX_SHADOWMAPS);
mShadowMaps.push_back({
.lightIndex = lightIndex,
.shadowType = ShadowType::SPOT,
.shadowIndex = uint8_t(i),
.face = 0,
.options = options });
auto& shadowMap = getShadowMap(i);
shadowMap.initialize(lightIndex, ShadowType::SPOT, i, 0, options);
} else {
// point-light, generate 6 independent shadowmaps
for (size_t face = 0; face < 6; face++) {
const size_t c = mSpotShadowMapCount++;
const size_t i = c + CONFIG_MAX_SHADOW_CASCADES;
assert_invariant(i < CONFIG_MAX_SHADOWMAPS);
mShadowMaps.push_back({
.lightIndex = lightIndex,
.shadowType = ShadowType::POINT,
.shadowIndex = uint8_t(i),
.face = uint8_t(face),
.options = options });
auto& shadowMap = getShadowMap(i);
shadowMap.initialize(lightIndex, ShadowType::POINT, i, face, options);
}
}
return *this;
}
FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameGraph& fg,
@@ -845,7 +773,7 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
FScene::LightSoa& lightData,
ShadowMap::SceneInfo const&) noexcept {
ShadowMap::SceneInfo const& sceneInfo) noexcept {
const uint8_t face = shadowMap.getFace();
const size_t lightIndex = shadowMap.getLightIndex();
+18 -61
View File
@@ -17,48 +17,29 @@
#ifndef TNT_FILAMENT_DETAILS_SHADOWMAPMANAGER_H
#define TNT_FILAMENT_DETAILS_SHADOWMAPMANAGER_H
#include "Culler.h"
#include <filament/Viewport.h>
#include "ShadowMap.h"
#include "TypedUniformBuffer.h"
#include <filament/LightManager.h>
#include <filament/Options.h>
#include <private/filament/EngineEnums.h>
#include <private/filament/UibStructs.h>
#include "components/RenderableManager.h"
#include "details/Engine.h"
#include "details/Scene.h"
#include "fg/FrameGraphId.h"
#include "fg/FrameGraphTexture.h"
#include <private/filament/EngineEnums.h>
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/BitmaskEnum.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Range.h>
#include <utils/Slice.h>
#include <math/mat4.h>
#include <math/vec4.h>
#include <math/vec3.h>
#include <array>
#include <memory>
#include <new>
#include <type_traits>
#include <vector>
#include <stdint.h>
#include <stddef.h>
namespace filament {
class FCamera;
class FView;
class FrameGraph;
class RenderPass;
@@ -74,7 +55,7 @@ struct ShadowMappingUniforms {
class ShadowMapManager {
public:
using ShadowMappingUniforms = filament::ShadowMappingUniforms;
using ShadowMappingUniforms = ShadowMappingUniforms;
using ShadowType = ShadowMap::ShadowType;
@@ -84,42 +65,24 @@ public:
SCREEN_SPACE = 0x2u,
};
class Builder {
friend class ShadowMapManager;
uint32_t mDirectionalShadowMapCount = 0;
uint32_t mSpotShadowMapCount = 0;
struct ShadowMap {
size_t lightIndex;
ShadowType shadowType;
uint16_t shadowIndex;
uint8_t face;
LightManager::ShadowOptions const* options;
};
std::vector<ShadowMap> mShadowMaps;
public:
Builder& directionalShadowMap(size_t lightIndex,
LightManager::ShadowOptions const* options) noexcept;
Builder& shadowMap(size_t lightIndex, bool spotlight,
LightManager::ShadowOptions const* options) noexcept;
bool hasShadowMaps() const noexcept {
return mDirectionalShadowMapCount || mSpotShadowMapCount;
}
};
explicit ShadowMapManager(FEngine& engine);
~ShadowMapManager();
static void createIfNeeded(FEngine& engine,
std::unique_ptr<ShadowMapManager>& inOutShadowMapManager);
void terminate(FEngine& engine);
static void terminate(FEngine& engine,
std::unique_ptr<ShadowMapManager>& shadowMapManager);
// Reset shadow map layout.
void reset() noexcept;
void setDirectionalShadowMap(size_t lightIndex,
LightManager::ShadowOptions const* options) noexcept;
void addShadowMap(size_t lightIndex, bool spotlight,
LightManager::ShadowOptions const* options) noexcept;
// Updates all the shadow maps and performs culling.
// Returns true if any of the shadow maps have visible shadows.
ShadowMapManager::ShadowTechnique update(Builder const& builder,
FEngine& engine, FView& view,
ShadowMapManager::ShadowTechnique update(FEngine& engine, FView& view,
CameraInfo const& cameraInfo,
FScene::RenderableSoa& renderableData, FScene::LightSoa const& lightData) noexcept;
@@ -138,16 +101,11 @@ public:
bool hasSpotShadows() const { return !mSpotShadowMapCount; }
// for debugging only
FCamera const* getDirectionalShadowCamera() const noexcept {
if (!mInitialized) return nullptr;
FCamera const* getDirectionalLightCamera() const noexcept {
return getShadowMap(0).getDebugCamera();
}
private:
explicit ShadowMapManager(FEngine& engine);
void terminate(FEngine& engine);
ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
FView& view, CameraInfo cameraInfo, FScene::RenderableSoa& renderableData,
FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept;
@@ -232,7 +190,6 @@ private:
ShadowMapCacheContainer mShadowMapCache;
uint32_t mDirectionalShadowMapCount = 0;
uint32_t mSpotShadowMapCount = 0;
bool mInitialized = false;
ShadowMap& getShadowMap(size_t index) noexcept {
assert_invariant(index < CONFIG_MAX_SHADOWMAPS);
+2 -2
View File
@@ -67,8 +67,8 @@ const char* View::getName() const noexcept {
return downcast(this)->getName();
}
Camera const* View::getDirectionalShadowCamera() const noexcept {
return downcast(this)->getDirectionalShadowCamera();
Camera const* View::getDirectionalLightCamera() const noexcept {
return downcast(this)->getDirectionalLightCamera();
}
void View::setShadowingEnabled(bool enabled) noexcept {
+2 -21
View File
@@ -16,18 +16,12 @@
#include "details/DebugRegistry.h"
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <utils/Panic.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <functional>
#include <string_view>
#include <utility>
#ifndef NDEBUG
# define DEBUG_PROPERTIES_WRITABLE true
#else
@@ -126,25 +120,12 @@ void FDebugRegistry::registerDataSource(std::string_view name,
}
}
void FDebugRegistry::registerDataSource(std::string_view name,
utils::Invocable<DataSource()>&& creator) noexcept {
mDataSourceCreatorMap[name] = std::move(creator);
}
DebugRegistry::DataSource FDebugRegistry::getDataSource(const char* name) const noexcept {
std::string_view const key{ name };
auto& dataSourceMap = mDataSourceMap;
auto const& it = dataSourceMap.find(key);
if (UTILS_UNLIKELY(it == dataSourceMap.end())) {
auto& dataSourceCreatorMap = mDataSourceCreatorMap;
auto const& pos = dataSourceCreatorMap.find(key);
if (pos == dataSourceCreatorMap.end()) {
return { nullptr, 0u };
}
DataSource dataSource{ pos->second() };
dataSourceMap[key] = dataSource;
dataSourceCreatorMap.erase(pos);
return dataSource;
if (it == dataSourceMap.end()) {
return { nullptr, 0u };
}
return it->second;
}
+1 -12
View File
@@ -22,17 +22,12 @@
#include <filament/DebugRegistry.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <math/mathfwd.h>
#include <functional>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <stddef.h>
namespace filament {
class FEngine;
@@ -100,13 +95,8 @@ public:
registerProperty(name, p, FLOAT4, std::move(fn));
}
// registers a DataSource directly
void registerDataSource(std::string_view name, void const* data, size_t count) noexcept;
// registers a DataSource lazily
void registerDataSource(std::string_view name,
utils::Invocable<DataSource()>&& creator) noexcept;
#if !defined(_MSC_VER)
private:
#endif
@@ -123,8 +113,7 @@ private:
void const* getPropertyAddress(const char* name) const noexcept;
DataSource getDataSource(const char* name) const noexcept;
std::unordered_map<std::string_view, PropertyInfo> mPropertyMap;
mutable std::unordered_map<std::string_view, DataSource> mDataSourceMap;
mutable std::unordered_map<std::string_view, utils::Invocable<DataSource()>> mDataSourceCreatorMap;
std::unordered_map<std::string_view, DataSource> mDataSourceMap;
};
FILAMENT_DOWNCAST(DebugRegistry)
+2 -6
View File
@@ -98,10 +98,7 @@ Engine* FEngine::create(Engine::Builder const& builder) {
return nullptr;
}
DriverConfig const driverConfig{
.handleArenaSize = instance->getRequestedDriverHandleArenaSize(),
.textureUseAfterFreePoolSize = instance->getConfig().textureUseAfterFreePoolSize,
.disableParallelShaderCompile = instance->getConfig().disableParallelShaderCompile
};
.handleArenaSize = instance->getRequestedDriverHandleArenaSize() };
instance->mDriver = platform->createDriver(sharedContext, driverConfig);
} else {
@@ -654,8 +651,7 @@ int FEngine::loop() {
DriverConfig const driverConfig {
.handleArenaSize = getRequestedDriverHandleArenaSize(),
.textureUseAfterFreePoolSize = mConfig.textureUseAfterFreePoolSize,
.disableParallelShaderCompile = mConfig.disableParallelShaderCompile
.textureUseAfterFreePoolSize = mConfig.textureUseAfterFreePoolSize
};
mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig);
+1 -1
View File
@@ -148,7 +148,7 @@ void FScene::prepare(utils::JobSystem& js,
// TODO: the resize below could happen in a job
if (!sceneData.capacity() || sceneData.size() != renderableInstances.size()) {
if (sceneData.size() != renderableInstances.size()) {
sceneData.clear();
if (sceneData.capacity() < renderableDataCapacity) {
sceneData.setCapacity(renderableDataCapacity);
+32 -54
View File
@@ -20,7 +20,6 @@
#include "Froxelizer.h"
#include "RenderPrimitive.h"
#include "ResourceAllocator.h"
#include "ShadowMapManager.h"
#include "details/Engine.h"
#include "details/IndirectLight.h"
@@ -44,7 +43,6 @@
#include <math/scalar.h>
#include <math/fast.h>
#include <array>
#include <memory>
using namespace utils;
@@ -61,8 +59,8 @@ FView::FView(FEngine& engine)
: mFroxelizer(engine),
mFogEntity(engine.getEntityManager().create()),
mIsStereoSupported(engine.getDriverApi().isStereoSupported()),
mPerViewUniforms(engine) {
mPerViewUniforms(engine),
mShadowMapManager(engine) {
DriverApi& driver = engine.getDriverApi();
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
@@ -78,11 +76,7 @@ FView::FView(FEngine& engine)
#ifndef NDEBUG
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() };
});
mDebugFrameHistory.data(), mDebugFrameHistory.size());
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);
@@ -119,8 +113,7 @@ void FView::terminate(FEngine& engine) {
driver.destroyBufferObject(mLightUbh);
driver.destroyBufferObject(mRenderableUbh);
drainFrameHistory(engine);
ShadowMapManager::terminate(engine, mShadowMapManager);
mShadowMapManager.terminate(engine);
mPerViewUniforms.terminate(driver);
mFroxelizer.terminate(driver);
@@ -249,24 +242,21 @@ float2 FView::updateScale(FEngine& engine,
#ifndef NDEBUG
// only for debugging...
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(mDebugFrameHistory->begin() + 1,
mDebugFrameHistory->end(), mDebugFrameHistory->begin());
mDebugFrameHistory->back() = {
.target = target,
.targetWithHeadroom = targetWithHeadroom,
.frameTime = duration_cast<duration_ms>(info.frameTime).count(),
.frameTimeDenoised = duration_cast<duration_ms>(info.denoisedFrameTime).count(),
.scale = mScale.x * mScale.y,
.pid_e = mPidController.getError(),
.pid_i = mPidController.getIntegral(),
.pid_d = mPidController.getDerivative()
};
}
using duration_ms = std::chrono::duration<float, std::milli>;
const float target = (1000.0f * float(frameRateOptions.interval)) / displayInfo.refreshRate;
const float targetWithHeadroom = target * (1.0f - frameRateOptions.headRoomRatio);
std::move(mDebugFrameHistory.begin() + 1,
mDebugFrameHistory.end(), mDebugFrameHistory.begin());
mDebugFrameHistory.back() = {
.target = target,
.targetWithHeadroom = targetWithHeadroom,
.frameTime = std::chrono::duration_cast<duration_ms>(info.frameTime).count(),
.frameTimeDenoised = std::chrono::duration_cast<duration_ms>(info.denoisedFrameTime).count(),
.scale = mScale.x * mScale.y,
.pid_e = mPidController.getError(),
.pid_i = mPidController.getIntegral(),
.pid_d = mPidController.getDerivative()
};
#endif
return mScale;
@@ -291,9 +281,9 @@ void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableD
return;
}
auto& lcm = engine.getLightManager();
mShadowMapManager.reset();
ShadowMapManager::Builder builder;
auto& lcm = engine.getLightManager();
// dominant directional light is always as index 0
FLightManager::Instance const directionalLight = lightData.elementAt<FScene::LIGHT_INSTANCE>(0);
@@ -302,7 +292,7 @@ void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableD
const auto& shadowOptions = lcm.getShadowOptions(directionalLight);
assert_invariant(shadowOptions.shadowCascades >= 1 &&
shadowOptions.shadowCascades <= CONFIG_MAX_SHADOW_CASCADES);
builder.directionalShadowMap(0, &shadowOptions);
mShadowMapManager.setDirectionalShadowMap(0, &shadowOptions);
}
// Find all shadow-casting spotlights.
@@ -336,7 +326,7 @@ void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableD
if (shadowMapCount + shadowMapCountNeeded <= CONFIG_MAX_SHADOWMAPS) {
shadowMapCount += shadowMapCountNeeded;
const auto& shadowOptions = lcm.getShadowOptions(li);
builder.shadowMap(l, spotLight, &shadowOptions);
mShadowMapManager.addShadowMap(l, spotLight, &shadowOptions);
}
if (shadowMapCount >= CONFIG_MAX_SHADOWMAPS) {
@@ -344,14 +334,11 @@ void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableD
}
}
if (builder.hasShadowMaps()) {
ShadowMapManager::createIfNeeded(engine, mShadowMapManager);
auto shadowTechnique = mShadowMapManager->update(builder, engine, *this,
cameraInfo, renderableData, lightData);
auto shadowTechnique = mShadowMapManager.update(engine, *this, cameraInfo,
renderableData, lightData);
mHasShadowing = any(shadowTechnique);
mNeedsShadowMap = any(shadowTechnique & ShadowMapManager::ShadowTechnique::SHADOW_MAP);
}
mHasShadowing = any(shadowTechnique);
mNeedsShadowMap = any(shadowTechnique & ShadowMapManager::ShadowTechnique::SHADOW_MAP);
}
void FView::prepareLighting(FEngine& engine, CameraInfo const& cameraInfo) noexcept {
@@ -626,7 +613,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
uint32_t(endDirCastersOnly - beginRenderables)};
merged = { 0, uint32_t(endPotentialSpotCastersOnly - beginRenderables) };
if (!needsShadowMap() || !mShadowMapManager->hasSpotShadows()) {
if (!mShadowMapManager.hasSpotShadows()) {
// we know we don't have spot shadows, we can reduce the range to not even include
// the potential spot casters
merged = { 0, uint32_t(endDirCastersOnly - beginRenderables) };
@@ -692,11 +679,8 @@ void FView::bindPerViewUniformsAndSamplers(FEngine::DriverApi& driver) const noe
driver.bindUniformBuffer(+UniformBindingPoints::LIGHTS,
mLightUbh);
if (needsShadowMap()) {
assert_invariant(mShadowMapManager->getShadowUniformsHandle());
driver.bindUniformBuffer(+UniformBindingPoints::SHADOW,
mShadowMapManager->getShadowUniformsHandle());
}
driver.bindUniformBuffer(+UniformBindingPoints::SHADOW,
mShadowMapManager.getShadowUniformsHandle());
driver.bindUniformBuffer(+UniformBindingPoints::FROXEL_RECORDS,
mFroxelizer.getRecordBuffer());
@@ -797,12 +781,7 @@ void FView::prepareStructure(Handle<HwTexture> structure) const noexcept {
}
void FView::prepareShadow(Handle<HwTexture> texture) const noexcept {
// when needsShadowMap() is not set, this method only just sets a dummy texture
// in the needed samplers (in that case `texture` is actually a dummy texture).
ShadowMapManager::ShadowMappingUniforms uniforms;
if (needsShadowMap()) {
uniforms = mShadowMapManager->getShadowMappingUniforms();
}
const auto& uniforms = mShadowMapManager.getShadowMappingUniforms();
switch (mShadowType) {
case filament::ShadowType::PCF:
mPerViewUniforms.prepareShadowPCF(texture, uniforms);
@@ -1000,8 +979,7 @@ void FView::updatePrimitivesLod(FEngine& engine, const CameraInfo&,
FrameGraphId<FrameGraphTexture> FView::renderShadowMaps(FEngine& engine, FrameGraph& fg,
CameraInfo const& cameraInfo, float4 const& userTime,
RenderPassBuilder const& passBuilder) noexcept {
assert_invariant(needsShadowMap());
return mShadowMapManager->render(engine, fg, passBuilder, *this, cameraInfo, userTime);
return mShadowMapManager.render(engine, fg, passBuilder, *this, cameraInfo, userTime);
}
void FView::commitFrameHistory(FEngine& engine) noexcept {
+4 -8
View File
@@ -53,9 +53,6 @@
#include <math/scalar.h>
#include <math/mat4.h>
#include <array>
#include <memory>
namespace utils {
class JobSystem;
} // namespace utils;
@@ -201,9 +198,8 @@ public:
void setStereoscopicOptions(StereoscopicOptions const& options) noexcept;
FCamera const* getDirectionalShadowCamera() const noexcept {
if (!mShadowMapManager) return nullptr;
return mShadowMapManager->getDirectionalShadowCamera();
FCamera const* getDirectionalLightCamera() const noexcept {
return mShadowMapManager.getDirectionalLightCamera();
}
void setRenderTarget(FRenderTarget* renderTarget) noexcept {
@@ -559,7 +555,7 @@ private:
mutable bool mHasShadowing = false;
mutable bool mNeedsShadowMap = false;
std::unique_ptr<ShadowMapManager> mShadowMapManager;
ShadowMapManager mShadowMapManager;
std::array<math::float4, 4> mMaterialGlobals = {{
{ 0, 0, 0, 1 },
@@ -569,7 +565,7 @@ private:
}};
#ifndef NDEBUG
std::unique_ptr<std::array<DebugRegistry::FrameHistory, 5*60>> mDebugFrameHistory;
std::array<DebugRegistry::FrameHistory, 5*60> mDebugFrameHistory;
#endif
};
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.50.4"
spec.version = "1.50.2"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.50.4/filament-v1.50.4-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.50.2/filament-v1.50.2-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
+36 -77
View File
@@ -24,7 +24,6 @@
#include <math/mat4.h>
#include <math/quat.h>
#include <algorithm>
#include <cmath>
namespace filament {
@@ -122,90 +121,50 @@ public:
}
void update(FLOAT deltaTime) override {
vec3 forceLocal { 0.0, 0.0, 0.0 };
auto getLocalDirection = [this]() -> vec3 {
vec3 directionLocal{ 0.0, 0.0, 0.0 };
if (mKeyDown[(int)Base::Key::FORWARD]) {
directionLocal += vec3{ 0.0, 0.0, -1.0 };
}
if (mKeyDown[(int)Base::Key::LEFT]) {
directionLocal += vec3{ -1.0, 0.0, 0.0 };
}
if (mKeyDown[(int)Base::Key::BACKWARD]) {
directionLocal += vec3{ 0.0, 0.0, 1.0 };
}
if (mKeyDown[(int)Base::Key::RIGHT]) {
directionLocal += vec3{ 1.0, 0.0, 0.0 };
}
return directionLocal;
};
if (mKeyDown[(int) Base::Key::FORWARD]) {
forceLocal += vec3{ 0.0, 0.0, -1.0 };
}
if (mKeyDown[(int) Base::Key::LEFT]) {
forceLocal += vec3{ -1.0, 0.0, 0.0 };
}
if (mKeyDown[(int) Base::Key::BACKWARD]) {
forceLocal += vec3{ 0.0, 0.0, 1.0 };
}
if (mKeyDown[(int) Base::Key::RIGHT]) {
forceLocal += vec3{ 1.0, 0.0, 0.0 };
}
auto getWorldDirection = [this](vec3 directionLocal) -> vec3 {
const mat4 orientation = mat4::lookAt(Base::mEye, Base::mTarget, Base::mProps.upVector);
vec3 directionWorld = (orientation * vec4{ directionLocal, 0.0f }).xyz;
if (mKeyDown[(int)Base::Key::UP]) {
directionWorld += vec3{ 0.0, 1.0, 0.0 };
}
if (mKeyDown[(int)Base::Key::DOWN]) {
directionWorld += vec3{ 0.0, -1.0, 0.0 };
}
return directionWorld;
};
const mat4 orientation = mat4::lookAt(Base::mEye, Base::mTarget, Base::mProps.upVector);
vec3 forceWorld = (orientation * vec4{ forceLocal, 0.0f }).xyz;
vec3 const localDirection = getLocalDirection();
vec3 const worldDirection = getWorldDirection(localDirection);
if (mKeyDown[(int) Base::Key::UP]) {
forceWorld += vec3{ 0.0, 1.0, 0.0 };
}
if (mKeyDown[(int) Base::Key::DOWN]) {
forceWorld += vec3{ 0.0, -1.0, 0.0 };
}
// unit of dampingFactor is [1/s]
FLOAT const dampingFactor = Base::mProps.flightMoveDamping;
forceWorld *= mMoveSpeed;
const auto dampingFactor = Base::mProps.flightMoveDamping;
if (dampingFactor == 0.0) {
// Without damping, we simply treat the force as our velocity.
vec3 const speed = worldDirection * mMoveSpeed;
mEyeVelocity = speed;
vec3 const positionDelta = mEyeVelocity * deltaTime;
Base::mEye += positionDelta;
Base::mTarget += positionDelta;
mEyeVelocity = forceWorld;
} else {
auto dt = deltaTime / 16.0;
for (size_t i = 0; i < 16; i++) {
// Note: the algorithm below doesn't work well for large time steps because
// we're not using a closed form for updating the position, so we need
// to loop a few times. We could make this better by having a dynamic
// loop count. What we're really doing is evaluation the solution to
// a differential equation numerically.
// Kinetic friction is a force opposing velocity and proportional to it.:
// F = -kv
// F = ma
// ==> ma = -kv
// a = -vk/m [m.s^-2] = [m/s] * [Kg/s] / [Kg]
// ==> dampingFactor = k/m [1/s] = [Kg/s] / [Kg]
//
// The velocity update for dt due to friction is then:
// v = v + a.dt
// = v - v * dampingFactor * dt
// = v * (1.0 - dampingFactor * dt)
mEyeVelocity = mEyeVelocity * saturate(1.0 - dampingFactor * dt);
// We also undergo an acceleration proportional to the distance to the target speed
// (the closer we are the less we accelerate, similar to a car).
// F = k * (target_v - v)
// F = ma
// ==> ma = k * (target_v - v)
// a = k/m * (target_v - v) [m.s^-2] = [Kg/s] / [Kg] * [m/s]
//
// The velocity update for dt due to the acceleration (the gas basically) is then:
// v = v + a.dt
// = v + k/m * (target_v - v).dt
// We're using the same dampingFactor here, but we don't have to.
auto const accelerationFactor = dampingFactor;
vec3 const acceleration = worldDirection *
(accelerationFactor * std::max(mMoveSpeed - length(mEyeVelocity), FLOAT(0)));
mEyeVelocity += acceleration * dt;
vec3 const positionDelta = mEyeVelocity * dt;
Base::mEye += positionDelta;
Base::mTarget += positionDelta;
}
// The dampingFactor acts as "friction", which acts upon the camera in the direction
// opposite its velocity.
// Force is also multiplied by the dampingFactor, to "make up" for the friction.
// This ensures that the max velocity still approaches mMoveSpeed;
vec3 velocityDelta = (forceWorld - mEyeVelocity) * dampingFactor;
mEyeVelocity += velocityDelta * deltaTime;
}
const vec3 positionDelta = mEyeVelocity * deltaTime;
Base::mEye += positionDelta;
Base::mTarget += positionDelta;
}
Bookmark getCurrentBookmark() const override {
+3 -8
View File
@@ -430,10 +430,8 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
window->mDebugCamera->lookAt(eye, center, up);
// Update the cube distortion matrix used for frustum visualization.
const Camera* lightmapCamera = window->mMainView->getView()->getDirectionalShadowCamera();
if (lightmapCamera) {
lightmapCube->mapFrustum(*mEngine, lightmapCamera);
}
const Camera* lightmapCamera = window->mMainView->getView()->getDirectionalLightCamera();
lightmapCube->mapFrustum(*mEngine, lightmapCamera);
cameraCube->mapFrustum(*mEngine, window->mMainCamera);
// Delay rendering for roughly one monitor refresh interval
@@ -715,10 +713,7 @@ FilamentApp::Window::Window(FilamentApp* filamentApp,
mGodView->setCameraManipulator(mDebugCameraMan);
// Ortho view obviously uses an ortho camera
Camera const* debugDirectionalShadowCamera = mMainView->getView()->getDirectionalShadowCamera();
if (debugDirectionalShadowCamera) {
mOrthoView->setCamera(const_cast<Camera *>(debugDirectionalShadowCamera));
}
mOrthoView->setCamera( (Camera *)mMainView->getView()->getDirectionalLightCamera() );
}
// configure the cameras
+2 -3
View File
@@ -19,13 +19,12 @@
#include <math/mat3.h>
#include <math/norm.h>
#include <meshoptimizer.h>
#include <mikktspace/mikktspace.h>
#include <vector>
#include <string.h> // memcpy
namespace filament::geometry {
using namespace filament::math;
@@ -99,7 +98,7 @@ void MikktspaceImpl::setTSpaceBasic(SMikkTSpaceContext const* context, float con
cursor += 36;
for (auto [attribArray, attribStride, attribSize]: wrapper->mInputAttribArrays) {
uint8_t const* input = pointerAdd(attribArray, vertInd, attribStride);
memcpy(cursor, input, attribSize);
std::memcpy(cursor, input, attribSize);
cursor += attribSize;
}
}
+1 -7
View File
@@ -17,21 +17,15 @@
#ifndef TNT_MATH_MAT3_H
#define TNT_MATH_MAT3_H
#include <math/TMatHelpers.h>
#include <math/compiler.h>
#include <math/quat.h>
#include <math/vec3.h>
#include <math/TMatHelpers.h>
#include <math/TVecHelpers.h>
#include <limits.h>
#include <stdint.h>
#include <sys/types.h>
#include <cmath>
#include <assert.h>
#include <stddef.h>
namespace filament {
namespace math {
// -------------------------------------------------------------------------------------
+11 -35
View File
@@ -250,11 +250,11 @@ public:
return mHead;
}
private:
struct Node {
Node* next;
};
private:
static Node* init(void* begin, void* end,
size_t elementSize, size_t alignment, size_t extra) noexcept;
@@ -272,15 +272,15 @@ public:
AtomicFreeList() noexcept = default;
AtomicFreeList(void* begin, void* end,
size_t elementSize, size_t alignment, size_t extra) noexcept;
AtomicFreeList(const AtomicFreeList& rhs) = delete;
AtomicFreeList& operator=(const AtomicFreeList& rhs) = delete;
AtomicFreeList(const FreeList& rhs) = delete;
AtomicFreeList& operator=(const FreeList& rhs) = delete;
void* pop() noexcept {
Node* const pStorage = mStorage;
HeadPtr currentHead = mHead.load();
while (currentHead.offset >= 0) {
// The value of "pNext" we load here might already contain application data if another
// The value of "next" we load here might already contain application data if another
// thread raced ahead of us. But in that case, the computed "newHead" will be discarded
// since compare_exchange_weak fails. Then this thread will loop with the updated
// value of currentHead, and try again.
@@ -319,6 +319,7 @@ public:
return mStorage + mHead.load(std::memory_order_relaxed).offset;
}
private:
struct Node {
// This should be a regular (non-atomic) pointer, but this causes TSAN to complain
// about a data-race that exists but is benin. We always use this atomic<> in
@@ -349,7 +350,6 @@ public:
std::atomic<Node*> next;
};
private:
// This struct is using a 32-bit offset into the arena rather than
// a direct pointer, because together with the 32-bit tag, it needs to
// fit into 8 bytes. If it was any larger, it would not be possible to
@@ -372,8 +372,7 @@ template <
size_t OFFSET = 0,
typename FREELIST = FreeList>
class PoolAllocator {
static_assert(ELEMENT_SIZE >= sizeof(typename FREELIST::Node),
"ELEMENT_SIZE must accommodate at least a FreeList::Node");
static_assert(ELEMENT_SIZE >= sizeof(void*), "ELEMENT_SIZE must accommodate at least a pointer");
public:
// our allocator concept
void* alloc(size_t size = ELEMENT_SIZE,
@@ -394,10 +393,6 @@ public:
: mFreeList(begin, end, ELEMENT_SIZE, ALIGNMENT, OFFSET) {
}
PoolAllocator(void* begin, size_t size) noexcept
: mFreeList(begin, static_cast<char *>(begin) + size, ELEMENT_SIZE, ALIGNMENT, OFFSET) {
}
template <typename AREA>
explicit PoolAllocator(const AREA& area) noexcept
: PoolAllocator(area.begin(), area.end()) {
@@ -637,15 +632,6 @@ public:
mListener(name, mArea.data(), mArea.size()) {
}
template<typename ... ARGS>
void* alloc(size_t size, size_t alignment, size_t extra, ARGS&& ... args) noexcept {
std::lock_guard<LockingPolicy> guard(mLock);
void* p = mAllocator.alloc(size, alignment, extra, std::forward<ARGS>(args) ...);
mListener.onAlloc(p, size, alignment, extra);
return p;
}
// allocate memory from arena with given size and alignment
// (acceptable size/alignment may depend on the allocator provided)
void* alloc(size_t size, size_t alignment, size_t extra) noexcept {
@@ -678,13 +664,13 @@ public:
return (T*)alloc(count * sizeof(T), alignment);
}
// some allocators require more parameters
template<typename ... ARGS>
void free(void* p, size_t size, ARGS&& ... args) noexcept {
// return memory pointed by p to the arena
// (actual behaviour may depend on allocator provided)
void free(void* p) noexcept {
if (p) {
std::lock_guard<LockingPolicy> guard(mLock);
mListener.onFree(p, size);
mAllocator.free(p, size, std::forward<ARGS>(args) ...);
mListener.onFree(p);
mAllocator.free(p);
}
}
@@ -697,16 +683,6 @@ public:
}
}
// return memory pointed by p to the arena
// (actual behaviour may depend on allocator provided)
void free(void* p) noexcept {
if (p) {
std::lock_guard<LockingPolicy> guard(mLock);
mListener.onFree(p);
mAllocator.free(p);
}
}
// some allocators don't have a free() call, but a single reset() or rewind() instead
void reset() noexcept {
std::lock_guard<LockingPolicy> guard(mLock);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.50.4",
"version": "1.50.2",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",