Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0404be5127 | ||
|
|
a784fb7d8b | ||
|
|
8459cbc91c | ||
|
|
344c04a2d8 | ||
|
|
55e2691f3f |
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
Reference in New Issue
Block a user